diff options
203 files changed, 9296 insertions, 3887 deletions
diff --git a/Android.bp b/Android.bp index 9374c01066e6..72fc1030b63b 100644 --- a/Android.bp +++ b/Android.bp @@ -344,8 +344,8 @@ filegroup { genrule { name: "statslog-telephony-common-java-gen", tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common" - + " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog", + cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common" + + " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog", out: ["com/android/internal/telephony/TelephonyCommonStatsLog.java"], } @@ -752,8 +752,8 @@ java_library { } platform_compat_config { - name: "framework-platform-compat-config", - src: ":framework-minus-apex", + name: "framework-platform-compat-config", + src: ":framework-minus-apex", } // A temporary build target that is conditionally included on the bootclasspath if @@ -774,7 +774,7 @@ genrule { name: "statslog-framework-java-gen", tools: ["stats-log-api-gen"], cmd: "$(location stats-log-api-gen) --java $(out) --module framework" + - " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource", + " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource", out: ["com/android/internal/util/FrameworkStatsLog.java"], } @@ -883,7 +883,7 @@ filegroup { java_library { name: "framework-annotations-lib", - srcs: [ ":framework-annotations" ], + srcs: [":framework-annotations"], sdk_version: "core_current", } @@ -1161,7 +1161,6 @@ cc_library { }, } - // This is the full proto version of libplatformprotos. It may only // be used by test code that is not shipped on the device. cc_library { @@ -1227,68 +1226,57 @@ filegroup { path: "core/java", } -aidl_interface { - name: "libincremental_aidl", - unstable: true, +cc_defaults { + name: "incremental_default", + cflags: [ + "-Wall", + "-Wextra", + "-Wextra-semi", + "-Werror", + "-Wzero-as-null-pointer-constant", + "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION", + ], + shared_libs: [ + "libbinder", + "libutils", + ], + aidl: { + include_dirs: [ + "frameworks/native/aidl/binder", + ], + export_aidl_headers: true, + }, +} + +cc_library { + name: "libincremental_aidl-cpp", srcs: [ ":incremental_aidl", ], - backend: { - java: { - sdk_version: "28", - }, - cpp: { - enabled: true, - }, - ndk: { - enabled: true, - }, - }, + defaults: ["incremental_default"], } -aidl_interface { - name: "libdataloader_aidl", - unstable: true, +cc_library { + name: "libdataloader_aidl-cpp", srcs: [ ":dataloader_aidl", ], - imports: [ - "libincremental_aidl", + defaults: ["incremental_default"], + shared_libs: [ + "libincremental_aidl-cpp", ], - backend: { - java: { - sdk_version: "28", - }, - cpp: { - enabled: true, - }, - ndk: { - enabled: false, - }, - }, } -aidl_interface { - name: "libincremental_manager_aidl", - unstable: true, +cc_library { + name: "libincremental_manager_aidl-cpp", srcs: [ ":incremental_manager_aidl", ], - imports: [ - "libincremental_aidl", - "libdataloader_aidl", + defaults: ["incremental_default"], + shared_libs: [ + "libincremental_aidl-cpp", + "libdataloader_aidl-cpp", ], - backend: { - java: { - sdk_version: "28", - }, - cpp: { - enabled: true, - }, - ndk: { - enabled: false, - }, - }, } // TODO(b/77285514): remove this once the last few hidl interfaces have been @@ -1317,7 +1305,7 @@ java_library { "core/java/android/os/RemoteException.java", "core/java/android/util/AndroidException.java", ], - libs: [ "unsupportedappusage" ], + libs: ["unsupportedappusage"], dxflags: ["--core-library"], installable: false, @@ -1536,4 +1524,5 @@ java_library { ":protolog-common-src", ], } + // protolog end diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 30ed7de92614..175fb38bafc7 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,5 +1,6 @@ [Builtin Hooks] clang_format = true +bpfmt = true [Builtin Hooks Options] # Only turn on clang-format check for the following subfolders. @@ -15,7 +16,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp services/incremental/ tests/ tools/ - +bpfmt = -d [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index 168c7c2f13cd..5ded446ff81a 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -207,7 +207,6 @@ package android.app.appsearch { public static final class GetByUriRequest.Builder { ctor public GetByUriRequest.Builder(); - method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.lang.String...); method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>); method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...); method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>); @@ -323,7 +322,6 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.util.Collection<java.lang.String>); method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.lang.String...); method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.util.Collection<java.lang.String>); - method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.lang.String...); method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>); method @NonNull public android.app.appsearch.SearchSpec build(); method @NonNull public android.app.appsearch.SearchSpec.Builder setMaxSnippetSize(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_SNIPPET_SIZE_LIMIT) int); @@ -338,7 +336,8 @@ package android.app.appsearch { public final class SetSchemaRequest { method @NonNull public java.util.Map<java.lang.String,android.app.appsearch.AppSearchSchema.Migrator> getMigrators(); method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas(); - method @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi(); + method @NonNull public java.util.Set<java.lang.String> getSchemasNotDisplayedBySystem(); + method @Deprecated @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi(); method @NonNull public java.util.Map<java.lang.String,java.util.Set<android.app.appsearch.PackageIdentifier>> getSchemasVisibleToPackages(); method public boolean isForceOverride(); } @@ -350,8 +349,9 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.SetSchemaRequest build(); method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean); method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.AppSearchSchema.Migrator); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeDisplayedBySystem(@NonNull String, boolean); method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier); - method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean); + method @Deprecated @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean); } public class SetSchemaResponse { diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index 6a5975ef7ff7..da446bf1e7c6 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -317,7 +317,7 @@ public class AppSearchManager { getPackageName(), DEFAULT_DATABASE_NAME, schemaBundles, - new ArrayList<>(request.getSchemasNotVisibleToSystemUi()), + new ArrayList<>(request.getSchemasNotDisplayedBySystem()), /*schemasPackageAccessible=*/ Collections.emptyMap(), request.isForceOverride(), mContext.getUserId(), diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 24cc60e5eef5..f379739a3778 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -146,7 +146,7 @@ public final class AppSearchSession implements Closeable { mPackageName, mDatabaseName, schemaBundles, - new ArrayList<>(request.getSchemasNotVisibleToSystemUi()), + new ArrayList<>(request.getSchemasNotDisplayedBySystem()), schemasPackageAccessibleBundles, request.isForceOverride(), mUserId, diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java index 8dd9dc1be312..fb63e16aa6a8 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java @@ -96,7 +96,7 @@ public class GlobalSearchSession implements Closeable { * SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} when building a schema. * * <p>Document access can also be granted to system UIs by specifying {@link - * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} when building a schema. + * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} when building a schema. * * <p>See {@link AppSearchSession#search} for a detailed explanation on forming a query string. * diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl index 68ae23f9a9db..ba27762c7dfb 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl @@ -33,7 +33,7 @@ interface IAppSearchManager { * @param packageName The name of the package that owns this schema. * @param databaseName The name of the database where this schema lives. * @param schemaBundles List of {@link AppSearchSchema} bundles. - * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform + * @param schemasNotDisplayedBySystem Schema types that should not be surfaced on platform * surfaces. * @param schemasPackageAccessibleBundles Schema types that are visible to the specified * packages. The value List contains PackageIdentifier Bundles. @@ -47,7 +47,7 @@ interface IAppSearchManager { in String packageName, in String databaseName, in List<Bundle> schemaBundles, - in List<String> schemasNotPlatformSurfaceable, + in List<String> schemasNotDisplayedBySystem, in Map<String, List<Bundle>> schemasPackageAccessibleBundles, boolean forceOverride, in int userId, diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java index c927e3412cbc..17266f82b603 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java @@ -166,29 +166,7 @@ public final class GetByUriRequest { * all results, excepting any types that have their own, specific property paths set. * * @throws IllegalStateException if the builder has already been used. - * <p>{@see SearchSpec.Builder#addProjection(String, String...)} - */ - @NonNull - public Builder addProjection(@NonNull String schemaType, @NonNull String... propertyPaths) { - Preconditions.checkNotNull(propertyPaths); - return addProjection(schemaType, Arrays.asList(propertyPaths)); - } - - /** - * Adds property paths for the specified type to be used for projection. If property paths - * are added for a type, then only the properties referred to will be retrieved for results - * of that type. If a property path that is specified isn't present in a result, it will be - * ignored for that result. Property paths cannot be null. - * - * <p>If no property paths are added for a particular type, then all properties of results - * of that type will be retrieved. - * - * <p>If property path is added for the {@link - * GetByUriRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will apply to - * all results, excepting any types that have their own, specific property paths set. - * - * @throws IllegalStateException if the builder has already been used. - * <p>{@see SearchSpec.Builder#addProjection(String, String...)} + * @see SearchSpec.Builder#addProjection */ @NonNull public Builder addProjection( diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java index d792f450bc05..f34034b1c5c0 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java @@ -202,14 +202,6 @@ public final class SearchResult { */ public static final String PROPERTY_PATH_FIELD = "propertyPath"; - /** - * The index of matching value in its property. A property may have multiple values. This - * index indicates which value is the match. - * - * @hide - */ - public static final String VALUES_INDEX_FIELD = "valuesIndex"; - /** @hide */ public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower"; @@ -232,8 +224,7 @@ public final class SearchResult { mBundle = Preconditions.checkNotNull(bundle); Preconditions.checkNotNull(document); mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD)); - mFullText = - getPropertyValues(document, mPropertyPath, mBundle.getInt(VALUES_INDEX_FIELD)); + mFullText = getPropertyValues(document, mPropertyPath); } /** @@ -326,8 +317,7 @@ public final class SearchResult { } /** Extracts the matching string from the document. */ - private static String getPropertyValues( - GenericDocument document, String propertyName, int valueIndex) { + private static String getPropertyValues(GenericDocument document, String propertyName) { // In IcingLib snippeting is available for only 3 data types i.e String, double and // long, so we need to check which of these three are requested. // TODO (tytytyww): getPropertyStringArray takes property name, handle for property @@ -337,7 +327,9 @@ public final class SearchResult { if (values == null) { throw new IllegalStateException("No content found for requested property path!"); } - return values[valueIndex]; + + // TODO(b/175146044): Return the proper match based on the index in the propertyName. + return values[0]; } } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java index f72f8e1bbc5d..7888c8d78cd8 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java @@ -535,28 +535,6 @@ public final class SearchSpec { */ @NonNull public SearchSpec.Builder addProjection( - @NonNull String schema, @NonNull String... propertyPaths) { - Preconditions.checkNotNull(propertyPaths); - return addProjection(schema, Arrays.asList(propertyPaths)); - } - - /** - * Adds property paths for the specified type to be used for projection. If property paths - * are added for a type, then only the properties referred to will be retrieved for results - * of that type. If a property path that is specified isn't present in a result, it will be - * ignored for that result. Property paths cannot be null. - * - * <p>If no property paths are added for a particular type, then all properties of results - * of that type will be retrieved. - * - * <p>If property path is added for the {@link SearchSpec#PROJECTION_SCHEMA_TYPE_WILDCARD}, - * then those property paths will apply to all results, excepting any types that have their - * own, specific property paths set. - * - * <p>{@see SearchSpec.Builder#addProjection(String, String...)} - */ - @NonNull - public SearchSpec.Builder addProjection( @NonNull String schema, @NonNull Collection<String> propertyPaths) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(schema); 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 426a903981b3..c05406358995 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java @@ -71,19 +71,19 @@ import java.util.Set; */ public final class SetSchemaRequest { private final Set<AppSearchSchema> mSchemas; - private final Set<String> mSchemasNotVisibleToSystemUi; + private final Set<String> mSchemasNotDisplayedBySystem; private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages; private final Map<String, AppSearchSchema.Migrator> mMigrators; private final boolean mForceOverride; SetSchemaRequest( @NonNull Set<AppSearchSchema> schemas, - @NonNull Set<String> schemasNotVisibleToSystemUi, + @NonNull Set<String> schemasNotDisplayedBySystem, @NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages, @NonNull Map<String, AppSearchSchema.Migrator> migrators, boolean forceOverride) { mSchemas = Preconditions.checkNotNull(schemas); - mSchemasNotVisibleToSystemUi = Preconditions.checkNotNull(schemasNotVisibleToSystemUi); + mSchemasNotDisplayedBySystem = Preconditions.checkNotNull(schemasNotDisplayedBySystem); mSchemasVisibleToPackages = Preconditions.checkNotNull(schemasVisibleToPackages); mMigrators = Preconditions.checkNotNull(migrators); mForceOverride = forceOverride; @@ -96,12 +96,22 @@ public final class SetSchemaRequest { } /** + * TODO(b/181887768): This method exists only for dogfooder transition and must be removed. + * @deprecated This method exists only for dogfooder transition and must be removed. + */ + @Deprecated + @NonNull + public Set<String> getSchemasNotVisibleToSystemUi() { + return getSchemasNotDisplayedBySystem(); + } + + /** * Returns all the schema types that are opted out of being displayed and visible on any system * UI surface. */ @NonNull - public Set<String> getSchemasNotVisibleToSystemUi() { - return Collections.unmodifiableSet(mSchemasNotVisibleToSystemUi); + public Set<String> getSchemasNotDisplayedBySystem() { + return Collections.unmodifiableSet(mSchemasNotDisplayedBySystem); } /** @@ -151,7 +161,7 @@ public final class SetSchemaRequest { */ public static final class Builder { private final Set<AppSearchSchema> mSchemas = new ArraySet<>(); - private final Set<String> mSchemasNotVisibleToSystemUi = new ArraySet<>(); + private final Set<String> mSchemasNotDisplayedBySystem = new ArraySet<>(); private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages = new ArrayMap<>(); private final Map<String, AppSearchSchema.Migrator> mMigrators = new ArrayMap<>(); @@ -163,6 +173,8 @@ public final class SetSchemaRequest { * * <p>An {@link AppSearchSchema} object represents one type of structured data. * + * <p>Any documents of these types will be displayed on system UI surfaces by default. + * * @throws IllegalStateException if the builder has already been used. */ @NonNull @@ -187,30 +199,43 @@ public final class SetSchemaRequest { } /** + * TODO(b/181887768): This method exists only for dogfooder transition and must be removed. + * @deprecated This method exists only for dogfooder transition and must be removed. + */ + @Deprecated + @NonNull + public Builder setSchemaTypeVisibilityForSystemUi( + @NonNull String schemaType, boolean displayed) { + return setSchemaTypeDisplayedBySystem(schemaType, displayed); + } + + /** * 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. + * <p>The default behavior, if this method is not called, is to allow types to be displayed + * on system UI surfaces. * - * @param schemaType The schema type to set visibility on. - * @param visible Whether the {@code schemaType} will be visible or not. + * @param schemaType The name of an {@link AppSearchSchema} within the same {@link + * SetSchemaRequest}, which will be configured. + * @param displayed Whether documents of this type will be displayed on system UI surfaces. * @throws IllegalStateException if the builder has already been used. */ - // Merged list available from getSchemasNotVisibleToSystemUi + // Merged list available from getSchemasNotDisplayedBySystem @SuppressLint("MissingGetterMatchingBuilder") @NonNull - public Builder setSchemaTypeVisibilityForSystemUi( - @NonNull String schemaType, boolean visible) { + public Builder setSchemaTypeDisplayedBySystem( + @NonNull String schemaType, boolean displayed) { Preconditions.checkNotNull(schemaType); Preconditions.checkState(!mBuilt, "Builder has already been used"); - if (visible) { - mSchemasNotVisibleToSystemUi.remove(schemaType); + if (displayed) { + mSchemasNotDisplayedBySystem.remove(schemaType); } else { - mSchemasNotVisibleToSystemUi.add(schemaType); + mSchemasNotDisplayedBySystem.add(schemaType); } return this; } @@ -315,9 +340,10 @@ public final class SetSchemaRequest { Preconditions.checkState(!mBuilt, "Builder has already been used"); mBuilt = true; - // Verify that any schema types with visibility settings refer to a real schema. + // Verify that any schema types with display or visibility settings refer to a real + // schema. // Create a copy because we're going to remove from the set for verification purposes. - Set<String> referencedSchemas = new ArraySet<>(mSchemasNotVisibleToSystemUi); + Set<String> referencedSchemas = new ArraySet<>(mSchemasNotDisplayedBySystem); referencedSchemas.addAll(mSchemasVisibleToPackages.keySet()); for (AppSearchSchema schema : mSchemas) { @@ -332,7 +358,7 @@ public final class SetSchemaRequest { return new SetSchemaRequest( mSchemas, - mSchemasNotVisibleToSystemUi, + mSchemasNotDisplayedBySystem, mSchemasVisibleToPackages, mMigrators, mForceOverride); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index a45fa39bd58d..27c9ccb2cca6 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -89,7 +89,7 @@ public class AppSearchManagerService extends SystemService { @NonNull String packageName, @NonNull String databaseName, @NonNull List<Bundle> schemaBundles, - @NonNull List<String> schemasNotPlatformSurfaceable, + @NonNull List<String> schemasNotDisplayedBySystem, @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles, boolean forceOverride, @UserIdInt int userId, @@ -119,13 +119,12 @@ public class AppSearchManagerService extends SystemService { } schemasPackageAccessible.put(entry.getKey(), packageIdentifiers); } - AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId); impl.setSchema( packageName, databaseName, schemas, - schemasNotPlatformSurfaceable, + schemasNotDisplayedBySystem, schemasPackageAccessible, forceOverride); invokeCallbackOnResult( @@ -153,7 +152,7 @@ public class AppSearchManagerService extends SystemService { verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getAppSearchImpl(callingUserId); List<AppSearchSchema> schemas = impl.getSchema(packageName, databaseName); List<Bundle> schemaBundles = new ArrayList<>(schemas.size()); for (int i = 0; i < schemas.size(); i++) { @@ -188,7 +187,7 @@ public class AppSearchManagerService extends SystemService { AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getAppSearchImpl(callingUserId); for (int i = 0; i < documentBundles.size(); i++) { GenericDocument document = new GenericDocument(documentBundles.get(i)); try { @@ -231,7 +230,7 @@ public class AppSearchManagerService extends SystemService { AppSearchBatchResult.Builder<String, Bundle> resultBuilder = new AppSearchBatchResult.Builder<>(); AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getAppSearchImpl(callingUserId); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { @@ -276,7 +275,7 @@ public class AppSearchManagerService extends SystemService { verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getAppSearchImpl(callingUserId); SearchResultPage searchResultPage = impl.query( packageName, @@ -311,7 +310,7 @@ public class AppSearchManagerService extends SystemService { verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getAppSearchImpl(callingUserId); SearchResultPage searchResultPage = impl.globalQuery( queryExpression, @@ -342,7 +341,7 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(callingUserId); AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getAppSearchImpl(callingUserId); SearchResultPage searchResultPage = impl.getNextPage(nextPageToken); invokeCallbackOnResult( callback, @@ -362,7 +361,7 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(callingUserId); AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getAppSearchImpl(callingUserId); impl.invalidateNextPageToken(nextPageToken); } catch (Throwable t) { Log.e(TAG, "Unable to invalidate the query page token", t); @@ -390,7 +389,7 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(callingUserId); AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getAppSearchImpl(callingUserId); impl.reportUsage(packageName, databaseName, namespace, uri, usageTimeMillis); invokeCallbackOnResult( callback, AppSearchResult.newSuccessfulResult(/*result=*/ null)); @@ -422,7 +421,7 @@ public class AppSearchManagerService extends SystemService { AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getAppSearchImpl(callingUserId); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { @@ -460,7 +459,7 @@ public class AppSearchManagerService extends SystemService { verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getAppSearchImpl(callingUserId); impl.removeByQuery( packageName, databaseName, @@ -482,7 +481,7 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(callingUserId); AppSearchImpl impl = - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getAppSearchImpl(callingUserId); impl.persistToDisk(); } catch (Throwable t) { Log.e(TAG, "Unable to persist the data to disk", t); @@ -499,7 +498,7 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { verifyUserUnlocked(callingUserId); - mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + mImplInstanceManager.getOrCreateAppSearchImpl(getContext(), callingUserId); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { invokeCallbackOnError(callback, t); @@ -571,6 +570,7 @@ public class AppSearchManagerService extends SystemService { private void invokeCallbackOnError( IAppSearchBatchResultCallback callback, Throwable throwable) { try { + //TODO(b/175067650) verify ParcelableException could propagate throwable correctly. callback.onSystemError(new ParcelableException(throwable)); } catch (RemoteException e) { Log.e(TAG, "Unable to send error to the callback", e); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java index 82319d4f353b..8c953d12fd98 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java @@ -74,7 +74,7 @@ public final class ImplInstanceManager { } /** - * Gets an instance of AppSearchImpl for the given user. + * Gets an instance of AppSearchImpl for the given user, or creates one if none exists. * * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and * one will be created. @@ -84,7 +84,7 @@ public final class ImplInstanceManager { * @return An initialized {@link AppSearchImpl} for this user */ @NonNull - public AppSearchImpl getAppSearchImpl( + public AppSearchImpl getOrCreateAppSearchImpl( @NonNull Context context, @UserIdInt int userId) throws AppSearchException { synchronized (mInstancesLocked) { AppSearchImpl instance = mInstancesLocked.get(userId); @@ -96,6 +96,32 @@ public final class ImplInstanceManager { } } + + /** + * Gets an instance of AppSearchImpl for the given user. + * + * <p>This method should only be called by an initialized SearchSession, which has been already + * created the AppSearchImpl instance for the given user. + * + * @param userId The multi-user userId of the device user calling AppSearch + * @return An initialized {@link AppSearchImpl} for this user + * @throws IllegalStateException if {@link AppSearchImpl} haven't created for the given user. + */ + @NonNull + public AppSearchImpl getAppSearchImpl(@UserIdInt int userId) { + synchronized (mInstancesLocked) { + AppSearchImpl instance = mInstancesLocked.get(userId); + if (instance == null) { + // Impossible scenario, user cannot call an uninitialized SearchSession, + // getInstance should always find the instance for the given user and never try to + // create an instance for this user again. + throw new IllegalStateException( + "AppSearchImpl has never been created for this user: " + userId); + } + return instance; + } + } + private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId) throws AppSearchException { File appSearchDir = getAppSearchDir(context, userId); @@ -104,6 +130,7 @@ public final class ImplInstanceManager { private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) { // See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs + //TODO(b/177685938):Switch from getDataUserCePackageDirectory to getDataSystemCeDirectory File userCeDir = Environment.getDataUserCePackageDirectory( StorageManager.UUID_PRIVATE_INTERNAL, userId, context.getPackageName()); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java index f422ebc2db7b..e9852aa1cd41 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java @@ -113,8 +113,6 @@ public class SearchResultToProtoConverter { Bundle bundle = new Bundle(); bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, propertyPath); bundle.putInt( - SearchResult.MatchInfo.VALUES_INDEX_FIELD, snippetMatchProto.getValuesIndex()); - bundle.putInt( SearchResult.MatchInfo.EXACT_MATCH_POSITION_LOWER_FIELD, snippetMatchProto.getExactMatchPosition()); bundle.putInt( diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index 2c9477a5d76b..41c70f072262 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -I593dfd22279739e5f578e07d36a55cf02ee942c5 +I42b89416968565ceb6483b400894f5b49524208c diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp index eb072afec696..3c5082ef937d 100644 --- a/apex/appsearch/testing/Android.bp +++ b/apex/appsearch/testing/Android.bp @@ -30,5 +30,8 @@ java_library { "guava", "truth-prebuilt", ], - visibility: ["//cts/tests/appsearch"], + visibility: [ + "//cts/tests/appsearch", + "//vendor:__subpackages__", + ], } diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java index d912c08e7d5f..440050faaa7d 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java @@ -34,8 +34,8 @@ public interface GlobalSearchSessionShim extends Closeable { * SetSchemaRequest.Builder#setDocumentClassVisibilityForPackage} when building a schema. * * <p>Document access can also be granted to system UIs by specifying {@link - * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi}, or {@link - * SetSchemaRequest.Builder#setDocumentClassVisibilityForSystemUi} when building a schema. + * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem}, or {@link + * SetSchemaRequest.Builder#setDocumentClassDisplayedBySystem} when building a schema. * * <p>See {@link AppSearchSessionShim#search} for a detailed explanation on forming a query * string. diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java index 5e5717d11432..0dde546e85de 100644 --- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java @@ -32,12 +32,8 @@ public interface DeviceIdleInternal { void exitIdle(String reason); - // duration in milliseconds - 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, + long durationMs, int userId, boolean sync, @ReasonCode int reasonCode, @Nullable String reason); /** @@ -49,10 +45,11 @@ public interface DeviceIdleInternal { * @param sync * @param reasonCode one of {@link ReasonCode} * @param reason + * @param callingUid UID of app who added this temp-allowlist. */ void addPowerSaveTempWhitelistAppDirect(int uid, long duration, @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode, - @Nullable String reason); + @Nullable String reason, int callingUid); // 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 ac28e828eb2e..84fb39b273c4 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -1943,19 +1943,11 @@ public class DeviceIdleController extends SystemService } // duration in milliseconds - @Deprecated @Override public void addPowerSaveTempWhitelistApp(int callingUid, String packageName, - long duration, int userId, boolean sync, @Nullable String reason) { - addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration, - userId, sync, REASON_UNKNOWN, reason); - } - - @Override - public void addPowerSaveTempWhitelistApp(int callingUid, String packageName, - long duration, int userId, boolean sync, @ReasonCode int reasonCode, + long durationMs, int userId, boolean sync, @ReasonCode int reasonCode, @Nullable String reason) { - addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration, + addPowerSaveTempAllowlistAppInternal(callingUid, packageName, durationMs, userId, sync, reasonCode, reason); } @@ -1963,8 +1955,8 @@ public class DeviceIdleController extends SystemService @Override public void addPowerSaveTempWhitelistAppDirect(int uid, long duration, @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode, - @Nullable String reason) { - addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync, + @Nullable String reason, int callingUid) { + addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, type, sync, reasonCode, reason); } @@ -2741,6 +2733,16 @@ public class DeviceIdleController extends SystemService void addPowerSaveTempAllowlistAppInternal(int callingUid, String packageName, long duration, int userId, boolean sync, @ReasonCode int reasonCode, @Nullable String reason) { + synchronized (this) { + int callingAppId = UserHandle.getAppId(callingUid); + if (callingAppId >= Process.FIRST_APPLICATION_UID) { + if (!mPowerSaveWhitelistSystemAppIds.get(callingAppId)) { + throw new SecurityException( + "Calling app " + UserHandle.formatUid(callingUid) + + " is not on whitelist"); + } + } + } try { int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId); addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, @@ -2760,13 +2762,6 @@ public class DeviceIdleController extends SystemService boolean informWhitelistChanged = false; int appId = UserHandle.getAppId(uid); synchronized (this) { - int callingAppId = UserHandle.getAppId(callingUid); - if (callingAppId >= Process.FIRST_APPLICATION_UID) { - if (!mPowerSaveWhitelistSystemAppIds.get(callingAppId)) { - throw new SecurityException("Calling app " + UserHandle.formatUid(callingUid) - + " is not on whitelist"); - } - } duration = Math.min(duration, mConstants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS); Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.get(appId); final boolean newEntry = entry == null; diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java index 0308d68d6a56..460763ad4bbc 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -25,6 +25,7 @@ import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.UserSwitchObserver; import android.app.job.JobInfo; +import android.app.job.JobParameters; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -469,7 +470,7 @@ class JobConcurrencyManager { // Update the priorities of jobs that aren't running, and also count the pending work types. // Do this before the following loop to hopefully reduce the cost of // shouldStopRunningJobLocked(). - updateNonRunningPriorities(pendingJobs, true); + updateNonRunningPrioritiesLocked(pendingJobs, true); for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { final JobServiceContext js = activeServices.get(i); @@ -585,7 +586,8 @@ class JobConcurrencyManager { + activeServices.get(i).getRunningJobLocked()); } // preferredUid will be set to uid of currently running job. - activeServices.get(i).preemptExecutingJobLocked(preemptReasonForContext[i]); + activeServices.get(i).cancelExecutingJobLocked( + JobParameters.REASON_PREEMPT, preemptReasonForContext[i]); preservePreferredUid = true; } else { final JobStatus pendingJob = contextIdToJobMap[i]; @@ -610,7 +612,8 @@ class JobConcurrencyManager { mWorkCountTracker.getRunningJobCount(WORK_TYPE_TOP)); } - private void updateNonRunningPriorities(@NonNull final List<JobStatus> pendingJobs, + @GuardedBy("mLock") + private void updateNonRunningPrioritiesLocked(@NonNull final List<JobStatus> pendingJobs, boolean updateCounter) { for (int i = 0; i < pendingJobs.size(); i++) { final JobStatus pending = pendingJobs.get(i); @@ -628,6 +631,7 @@ class JobConcurrencyManager { } } + @GuardedBy("mLock") private void startJobLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus, @WorkType final int workType) { final List<StateController> controllers = mService.mControllers; @@ -647,6 +651,7 @@ class JobConcurrencyManager { } } + @GuardedBy("mLock") void onJobCompletedLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus, @WorkType final int workType) { mWorkCountTracker.onJobFinished(workType); @@ -655,7 +660,7 @@ class JobConcurrencyManager { if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) { updateCounterConfigLocked(); // Preemption case needs special care. - updateNonRunningPriorities(pendingJobs, false); + updateNonRunningPrioritiesLocked(pendingJobs, false); JobStatus highestPriorityJob = null; int highPriWorkType = workType; @@ -726,7 +731,7 @@ class JobConcurrencyManager { } } else if (pendingJobs.size() > 0) { updateCounterConfigLocked(); - updateNonRunningPriorities(pendingJobs, false); + updateNonRunningPrioritiesLocked(pendingJobs, false); // This slot is now free and we have pending jobs. Start the highest priority job we // find. @@ -775,6 +780,7 @@ class JobConcurrencyManager { * another job to run, or if system state suggests the job should stop. */ @Nullable + @GuardedBy("mLock") String shouldStopRunningJobLocked(@NonNull JobServiceContext context) { final JobStatus js = context.getRunningJobLocked(); if (js == null) { @@ -881,6 +887,7 @@ class JobConcurrencyManager { return s.toString(); } + @GuardedBy("mLock") void updateConfigLocked() { DeviceConfig.Properties properties = DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER); 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 96734499ee20..6802b6b6ee2d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -269,8 +269,6 @@ public final class JobServiceContext implements ServiceConnection { try { final int bindFlags; 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 | Context.BIND_NOT_FOREGROUND | Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_ALLOW_NETWORK_ACCESS @@ -355,15 +353,10 @@ public final class JobServiceContext implements ServiceConnection { /** Called externally when a job that was scheduled for execution should be cancelled. */ @GuardedBy("mLock") - void cancelExecutingJobLocked(int reason, String debugReason) { + void cancelExecutingJobLocked(int reason, @NonNull String debugReason) { doCancelLocked(reason, debugReason); } - @GuardedBy("mLock") - void preemptExecutingJobLocked(@NonNull String reason) { - doCancelLocked(JobParameters.REASON_PREEMPT, reason); - } - int getPreferredUid() { return mPreferredUid; } @@ -620,7 +613,7 @@ public final class JobServiceContext implements ServiceConnection { } @GuardedBy("mLock") - private void doCancelLocked(int stopReasonCode, String debugReason) { + private void doCancelLocked(int stopReasonCode, @Nullable String debugReason) { if (mVerb == VERB_FINISHED) { if (DEBUG) { Slog.d(TAG, @@ -733,7 +726,7 @@ public final class JobServiceContext implements ServiceConnection { * _ENDING -> No point in doing anything here, so we ignore. */ @GuardedBy("mLock") - private void handleCancelLocked(String reason) { + private void handleCancelLocked(@Nullable String reason) { if (JobSchedulerService.DEBUG) { Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " " + VERB_STRINGS[mVerb]); @@ -817,7 +810,7 @@ public final class JobServiceContext implements ServiceConnection { * VERB_STOPPING. */ @GuardedBy("mLock") - private void sendStopMessageLocked(String reason) { + private void sendStopMessageLocked(@Nullable String reason) { removeOpTimeOutLocked(); if (mVerb != VERB_EXECUTING) { Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob); @@ -843,7 +836,7 @@ public final class JobServiceContext implements ServiceConnection { * we want to clean up internally. */ @GuardedBy("mLock") - private void closeAndCleanupJobLocked(boolean reschedule, String reason) { + private void closeAndCleanupJobLocked(boolean reschedule, @Nullable String reason) { final JobStatus completedJob; if (mVerb == VERB_FINISHED) { return; @@ -889,7 +882,7 @@ public final class JobServiceContext implements ServiceConnection { mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType); } - private void applyStoppedReasonLocked(String reason) { + private void applyStoppedReasonLocked(@Nullable String reason) { if (reason != null && mStoppedReason == null) { mStoppedReason = reason; mStoppedTime = sElapsedRealtimeClock.millis(); diff --git a/core/api/current.txt b/core/api/current.txt index 3f1586d1f38e..310e95990b85 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1072,6 +1072,7 @@ package android { field public static final int panelTextAppearance = 16842850; // 0x1010062 field public static final int parentActivityName = 16843687; // 0x10103a7 field @Deprecated public static final int password = 16843100; // 0x101015c + field public static final int passwordsActivity = 16844351; // 0x101063f field public static final int path = 16842794; // 0x101002a field public static final int pathAdvancedPattern = 16844320; // 0x1010620 field public static final int pathData = 16843781; // 0x1010405 @@ -8371,7 +8372,6 @@ package android.appwidget { public class AppWidgetHostView extends android.widget.FrameLayout { ctor public AppWidgetHostView(android.content.Context); ctor public AppWidgetHostView(android.content.Context, int, int); - method public void clearCurrentSize(); method public int getAppWidgetId(); method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(); method public static android.graphics.Rect getDefaultPaddingForWidget(android.content.Context, android.content.ComponentName, android.graphics.Rect); @@ -8381,13 +8381,12 @@ package android.appwidget { method public void resetColorResources(); method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo); method public void setColorResources(@NonNull android.util.SparseIntArray); - method public void setCurrentSize(@NonNull android.graphics.PointF); method public void setExecutor(java.util.concurrent.Executor); method public void setOnLightBackground(boolean); method public void updateAppWidget(android.widget.RemoteViews); method public void updateAppWidgetOptions(android.os.Bundle); method @Deprecated public void updateAppWidgetSize(android.os.Bundle, int, int, int, int); - method public void updateAppWidgetSize(@NonNull android.os.Bundle, @NonNull java.util.List<android.graphics.PointF>); + method public void updateAppWidgetSize(@NonNull android.os.Bundle, @NonNull java.util.List<android.util.SizeF>); } public class AppWidgetManager { @@ -9716,7 +9715,7 @@ package android.companion { } public final class CompanionDeviceManager { - method public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler); + method @RequiresPermission(value=android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler); method public void disassociate(@NonNull String); method @NonNull public java.util.List<java.lang.String> getAssociations(); method public boolean hasNotificationAccess(android.content.ComponentName); @@ -12896,18 +12895,18 @@ package android.content.pm { } public class ShortcutManager { - method public boolean addDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>); - method public android.content.Intent createShortcutResultIntent(@NonNull android.content.pm.ShortcutInfo); + method @WorkerThread public boolean addDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>); + method @WorkerThread public android.content.Intent createShortcutResultIntent(@NonNull android.content.pm.ShortcutInfo); method public void disableShortcuts(@NonNull java.util.List<java.lang.String>); method public void disableShortcuts(@NonNull java.util.List<java.lang.String>, CharSequence); method public void enableShortcuts(@NonNull java.util.List<java.lang.String>); - method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts(); + method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts(); method public int getIconMaxHeight(); method public int getIconMaxWidth(); - method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts(); + method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts(); method public int getMaxShortcutCountPerActivity(); - method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts(); - method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(int); + method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts(); + method @NonNull @WorkerThread public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(int); method public boolean isRateLimitingActive(); method public boolean isRequestPinShortcutSupported(); method public void pushDynamicShortcut(@NonNull android.content.pm.ShortcutInfo); @@ -12915,10 +12914,10 @@ package android.content.pm { method public void removeDynamicShortcuts(@NonNull java.util.List<java.lang.String>); method public void removeLongLivedShortcuts(@NonNull java.util.List<java.lang.String>); method public void reportShortcutUsed(String); - method public boolean requestPinShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.content.IntentSender); - method public boolean setDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>); + method @WorkerThread public boolean requestPinShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.content.IntentSender); + method @WorkerThread public boolean setDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>); method public void updateShortcutVisibility(@NonNull String, @Nullable byte[], boolean); - method public boolean updateShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>); + method @WorkerThread public boolean updateShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>); field public static final int FLAG_MATCH_CACHED = 8; // 0x8 field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2 field public static final int FLAG_MATCH_MANIFEST = 1; // 0x1 @@ -17785,6 +17784,7 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS; + field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.BlackLevelPattern> SENSOR_BLACK_LEVEL_PATTERN; @@ -18389,9 +18389,20 @@ package android.hardware.camera2 { field public static final int MAX_THUMBNAIL_DIMENSION = 256; // 0x100 } + public class MultiResolutionImageReader implements java.lang.AutoCloseable { + method public void close(); + method protected void finalize(); + method public void flush(); + method @NonNull public android.hardware.camera2.params.MultiResolutionStreamInfo getStreamInfoForImageReader(@NonNull android.media.ImageReader); + method @NonNull public android.view.Surface getSurface(); + method @NonNull public static android.hardware.camera2.MultiResolutionImageReader newInstance(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int, @IntRange(from=1) int); + method public void setOnImageAvailableListener(@Nullable android.media.ImageReader.OnImageAvailableListener, @Nullable java.util.concurrent.Executor); + } + public final class TotalCaptureResult extends android.hardware.camera2.CaptureResult { method @NonNull public java.util.List<android.hardware.camera2.CaptureResult> getPartialResults(); - method public java.util.Map<java.lang.String,android.hardware.camera2.CaptureResult> getPhysicalCameraResults(); + method @Deprecated public java.util.Map<java.lang.String,android.hardware.camera2.CaptureResult> getPhysicalCameraResults(); + method @NonNull public java.util.Map<java.lang.String,android.hardware.camera2.TotalCaptureResult> getPhysicalCameraTotalResults(); } } @@ -18440,9 +18451,11 @@ package android.hardware.camera2.params { public final class InputConfiguration { ctor public InputConfiguration(int, int, int); + ctor public InputConfiguration(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int); method public int getFormat(); method public int getHeight(); method public int getWidth(); + method public boolean isMultiResolution(); } public final class LensShadingMap { @@ -18485,6 +18498,20 @@ package android.hardware.camera2.params { field public static final int METERING_WEIGHT_MIN = 0; // 0x0 } + public final class MultiResolutionStreamConfigurationMap { + method @NonNull public int[] getInputFormats(); + method @NonNull public java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo> getInputInfo(int); + method @NonNull public int[] getOutputFormats(); + method @NonNull public java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo> getOutputInfo(int); + } + + public class MultiResolutionStreamInfo { + ctor public MultiResolutionStreamInfo(int, int, @NonNull String); + method public int getHeight(); + method @NonNull public String getPhysicalCameraId(); + method public int getWidth(); + } + public final class OisSample { ctor public OisSample(long, float, float); method public long getTimestamp(); @@ -18497,6 +18524,7 @@ package android.hardware.camera2.params { ctor public OutputConfiguration(int, @NonNull android.view.Surface); ctor public OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>); method public void addSurface(@NonNull android.view.Surface); + method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader); method public int describeContents(); method public void enableSurfaceSharing(); method public int getMaxSharedSurfaceCount(); @@ -41526,29 +41554,29 @@ package android.telephony { field public static final char WILD = 78; // 0x004e 'N' } - public class PhoneStateListener { - ctor public PhoneStateListener(); + @Deprecated public class PhoneStateListener { + ctor @Deprecated public PhoneStateListener(); ctor @Deprecated public PhoneStateListener(@NonNull java.util.concurrent.Executor); - method public void onActiveDataSubscriptionIdChanged(int); - method public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo); - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int); - method public void onCallForwardingIndicatorChanged(boolean); - method public void onCallStateChanged(int, String); - method public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>); - method public void onCellLocationChanged(android.telephony.CellLocation); - method public void onDataActivity(int); - method public void onDataConnectionStateChanged(int); - method public void onDataConnectionStateChanged(int, int); - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); - method public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>); - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); - method public void onMessageWaitingIndicatorChanged(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); - method public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); - method public void onServiceStateChanged(android.telephony.ServiceState); + method @Deprecated public void onActiveDataSubscriptionIdChanged(int); + method @Deprecated public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo); + method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int); + method @Deprecated public void onCallForwardingIndicatorChanged(boolean); + method @Deprecated public void onCallStateChanged(int, String); + method @Deprecated public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>); + method @Deprecated public void onCellLocationChanged(android.telephony.CellLocation); + method @Deprecated public void onDataActivity(int); + method @Deprecated public void onDataConnectionStateChanged(int); + method @Deprecated public void onDataConnectionStateChanged(int, int); + method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); + method @Deprecated public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>); + method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); + method @Deprecated public void onMessageWaitingIndicatorChanged(boolean); + method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); + method @Deprecated public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); + method @Deprecated public void onServiceStateChanged(android.telephony.ServiceState); method @Deprecated public void onSignalStrengthChanged(int); - method public void onSignalStrengthsChanged(android.telephony.SignalStrength); - method public void onUserMobileDataStateChanged(boolean); + method @Deprecated public void onSignalStrengthsChanged(android.telephony.SignalStrength); + method @Deprecated public void onUserMobileDataStateChanged(boolean); field @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000 field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000 field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 @@ -41562,7 +41590,7 @@ package android.telephony { field @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000 field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 field @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4 - field public static final int LISTEN_NONE = 0; // 0x0 + field @Deprecated public static final int LISTEN_NONE = 0; // 0x0 field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000 field @Deprecated public static final int LISTEN_SERVICE_STATE = 1; // 0x1 @@ -41571,90 +41599,6 @@ package android.telephony { field @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000 } - public static interface PhoneStateListener.ActiveDataSubscriptionIdChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int); - } - - public static interface PhoneStateListener.AlwaysReportedSignalStrengthChangedListener { - method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength); - } - - public static interface PhoneStateListener.BarringInfoChangedListener { - method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo); - } - - public static interface PhoneStateListener.CallDisconnectCauseChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int); - } - - public static interface PhoneStateListener.CallForwardingIndicatorChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean); - } - - public static interface PhoneStateListener.CallStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String); - } - - public static interface PhoneStateListener.CarrierNetworkChangeListener { - method public void onCarrierNetworkChange(boolean); - } - - public static interface PhoneStateListener.CellInfoChangedListener { - method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>); - } - - public static interface PhoneStateListener.CellLocationChangedListener { - method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(@NonNull android.telephony.CellLocation); - } - - public static interface PhoneStateListener.DataActivationStateChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int); - } - - public static interface PhoneStateListener.DataActivityListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int); - } - - public static interface PhoneStateListener.DataConnectionStateChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int); - } - - public static interface PhoneStateListener.DisplayInfoChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); - } - - public static interface PhoneStateListener.EmergencyNumberListChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>); - } - - public static interface PhoneStateListener.ImsCallDisconnectCauseChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); - } - - public static interface PhoneStateListener.MessageWaitingIndicatorChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean); - } - - public static interface PhoneStateListener.PreciseDataConnectionStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); - } - - public static interface PhoneStateListener.RegistrationFailedListener { - method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); - } - - public static interface PhoneStateListener.ServiceStateChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState); - } - - public static interface PhoneStateListener.SignalStrengthsChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength); - } - - public static interface PhoneStateListener.UserMobileDataStateChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean); - } - public final class PhysicalChannelConfig implements android.os.Parcelable { method public int describeContents(); method @IntRange(from=1, to=261) public int getBand(); @@ -42129,6 +42073,94 @@ package android.telephony { method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence); } + public class TelephonyCallback { + ctor public TelephonyCallback(); + } + + public static interface TelephonyCallback.ActiveDataSubscriptionIdListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int); + } + + public static interface TelephonyCallback.AlwaysReportedSignalStrengthListener { + method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength); + } + + public static interface TelephonyCallback.BarringInfoListener { + method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo); + } + + public static interface TelephonyCallback.CallDisconnectCauseListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int); + } + + public static interface TelephonyCallback.CallForwardingIndicatorListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean); + } + + public static interface TelephonyCallback.CallStateListener { + method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String); + } + + public static interface TelephonyCallback.CarrierNetworkListener { + method public void onCarrierNetworkChange(boolean); + } + + public static interface TelephonyCallback.CellInfoListener { + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>); + } + + public static interface TelephonyCallback.CellLocationListener { + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(@NonNull android.telephony.CellLocation); + } + + public static interface TelephonyCallback.DataActivationStateListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int); + } + + public static interface TelephonyCallback.DataActivityListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int); + } + + public static interface TelephonyCallback.DataConnectionStateListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int); + } + + public static interface TelephonyCallback.DisplayInfoListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); + } + + public static interface TelephonyCallback.EmergencyNumberListListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>); + } + + public static interface TelephonyCallback.ImsCallDisconnectCauseListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); + } + + public static interface TelephonyCallback.MessageWaitingIndicatorListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean); + } + + public static interface TelephonyCallback.PreciseDataConnectionStateListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); + } + + public static interface TelephonyCallback.RegistrationFailedListener { + method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); + } + + public static interface TelephonyCallback.ServiceStateListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState); + } + + public static interface TelephonyCallback.SignalStrengthsListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength); + } + + public static interface TelephonyCallback.UserMobileDataStateListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean); + } + public final class TelephonyDisplayInfo implements android.os.Parcelable { method public int describeContents(); method public int getNetworkType(); @@ -42241,7 +42273,7 @@ package android.telephony { method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle); method public boolean isWorldPhone(); method @Deprecated public void listen(android.telephony.PhoneStateListener, int); - method public void registerPhoneStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.PhoneStateListener); + method public void registerTelephonyCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyCallback); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback); method public void sendDialerSpecialCode(String); @@ -42265,7 +42297,7 @@ package android.telephony { method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri); method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int); - method public void unregisterPhoneStateListener(@NonNull android.telephony.PhoneStateListener); + method public void unregisterTelephonyCallback(@NonNull android.telephony.TelephonyCallback); method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>); method public void uploadCallComposerPicture(@NonNull java.nio.file.Path, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>); method public void uploadCallComposerPicture(@NonNull java.io.InputStream, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>); @@ -45928,11 +45960,14 @@ package android.util { method public static android.util.Size parseSize(String) throws java.lang.NumberFormatException; } - public final class SizeF { + public final class SizeF implements android.os.Parcelable { ctor public SizeF(float, float); + method public int describeContents(); method public float getHeight(); method public float getWidth(); method public static android.util.SizeF parseSizeF(String) throws java.lang.NumberFormatException; + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.util.SizeF> CREATOR; } public class SparseArray<E> implements java.lang.Cloneable { @@ -54824,7 +54859,7 @@ package android.widget { public class RemoteViews implements android.view.LayoutInflater.Filter android.os.Parcelable { ctor public RemoteViews(String, int); ctor public RemoteViews(android.widget.RemoteViews, android.widget.RemoteViews); - ctor public RemoteViews(@NonNull java.util.Map<android.graphics.PointF,android.widget.RemoteViews>); + ctor public RemoteViews(@NonNull java.util.Map<android.util.SizeF,android.widget.RemoteViews>); ctor public RemoteViews(android.widget.RemoteViews); ctor public RemoteViews(android.os.Parcel); method public void addView(@IdRes int, android.widget.RemoteViews); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index bc490afd8da9..3a238e29dd23 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2703,7 +2703,7 @@ package android.content.pm { } public class ShortcutManager { - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS) public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter); + method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS) @WorkerThread public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter); method public boolean hasShareTargets(@NonNull String); } @@ -3450,6 +3450,10 @@ package android.hardware.lights { field @Deprecated public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8 } + public static final class LightsRequest.Builder { + method @Deprecated @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState); + } + } package android.hardware.location { @@ -11073,51 +11077,16 @@ package android.telephony { method public static boolean isVoiceMailNumber(@NonNull android.content.Context, int, @Nullable String); } - public class PhoneStateListener { - method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); + @Deprecated public class PhoneStateListener { + method @Deprecated public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber); - method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); + method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber); - method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int); - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); - method public void onRadioPowerStateChanged(int); - method public void onSrvccStateChanged(int); - method public void onVoiceActivationStateChanged(int); - field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35; // 0x23 - field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa - field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a - field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4 - field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6 - field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11 - field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb - field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5 - field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13 - field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; // 0xe - field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22 - field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15 - field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c - field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf - field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d - field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; // 0x1e - field public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; // 0x16 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; // 0x21 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; // 0xc - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18 - field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f - field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1 - field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9 - field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10 - field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12 + method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int); + method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); + method @Deprecated public void onRadioPowerStateChanged(int); + method @Deprecated public void onSrvccStateChanged(int); + method @Deprecated public void onVoiceActivationStateChanged(int); field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000 field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000 @@ -11127,50 +11096,6 @@ package android.telephony { field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000 } - public static interface PhoneStateListener.AllowedNetworkTypesChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(@NonNull java.util.Map<java.lang.Integer,java.lang.Long>); - } - - public static interface PhoneStateListener.CallAttributesChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); - } - - public static interface PhoneStateListener.DataEnabledChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int); - } - - public static interface PhoneStateListener.OutgoingEmergencyCallListener { - method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); - } - - public static interface PhoneStateListener.OutgoingEmergencySmsListener { - method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int); - } - - public static interface PhoneStateListener.PhoneCapabilityChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability); - } - - public static interface PhoneStateListener.PhysicalChannelConfigChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>); - } - - public static interface PhoneStateListener.PreciseCallStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); - } - - public static interface PhoneStateListener.RadioPowerStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int); - } - - public static interface PhoneStateListener.SrvccStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int); - } - - public static interface PhoneStateListener.VoiceActivationStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int); - } - public final class PinResult implements android.os.Parcelable { method public int describeContents(); method public int getAttemptsRemaining(); @@ -11508,6 +11433,88 @@ package android.telephony { method @Deprecated public static android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime); } + public class TelephonyCallback { + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35; // 0x23 + field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa + field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4 + field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6 + field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11 + field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb + field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5 + field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13 + field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; // 0xe + field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22 + field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15 + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf + field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d + field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; // 0x1e + field public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; // 0x16 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; // 0x21 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; // 0xc + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18 + field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f + field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1 + field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9 + field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10 + field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12 + } + + public static interface TelephonyCallback.AllowedNetworkTypesListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(@NonNull java.util.Map<java.lang.Integer,java.lang.Long>); + } + + public static interface TelephonyCallback.CallAttributesListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); + } + + public static interface TelephonyCallback.DataEnabledListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int); + } + + public static interface TelephonyCallback.OutgoingEmergencyCallListener { + method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); + } + + public static interface TelephonyCallback.OutgoingEmergencySmsListener { + method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int); + } + + public static interface TelephonyCallback.PhoneCapabilityListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability); + } + + public static interface TelephonyCallback.PhysicalChannelConfigListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>); + } + + public static interface TelephonyCallback.PreciseCallStateListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); + } + + public static interface TelephonyCallback.RadioPowerStateListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int); + } + + public static interface TelephonyCallback.SrvccStateListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int); + } + + public static interface TelephonyCallback.VoiceActivationStateListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int); + } + public final class TelephonyHistogram implements android.os.Parcelable { ctor public TelephonyHistogram(int, int, int); ctor public TelephonyHistogram(android.telephony.TelephonyHistogram); diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index fc54c716d4ec..d79fac58cf12 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -39,6 +39,7 @@ import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; +import android.util.SizeF; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.Gravity; @@ -56,7 +57,6 @@ import android.widget.TextView; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.concurrent.Executor; /** @@ -93,7 +93,7 @@ public class AppWidgetHostView extends FrameLayout { int mLayoutId = -1; private InteractionHandler mInteractionHandler; private boolean mOnLightBackground; - private PointF mCurrentSize = null; + private SizeF mCurrentSize = null; private RemoteViews.ColorResources mColorResources = null; // Stores the last remote views last inflated. private RemoteViews mLastInflatedRemoteViews = null; @@ -253,9 +253,33 @@ public class AppWidgetHostView extends FrameLayout { } } + private SizeF computeSizeFromLayout(int left, int top, int right, int bottom) { + Rect padding = getDefaultPadding(); + float density = getResources().getDisplayMetrics().density; + return new SizeF( + (right - left - padding.right - padding.left) / density, + (bottom - top - padding.bottom - padding.top) / density + ); + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { try { + SizeF oldSize = mCurrentSize; + SizeF newSize = computeSizeFromLayout(left, top, right, bottom); + mCurrentSize = newSize; + if (mLastInflatedRemoteViews != null) { + RemoteViews toApply = mLastInflatedRemoteViews.getRemoteViewsToApplyIfDifferent( + oldSize, newSize); + if (toApply != null) { + applyRemoteViews(toApply, false); + measureChildWithMargins(mView, + MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), + 0 /* widthUsed */, + MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY), + 0 /* heightUsed */); + } + } super.onLayout(changed, left, top, right, bottom); } catch (final RuntimeException e) { Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e); @@ -309,7 +333,7 @@ public class AppWidgetHostView extends FrameLayout { * again. Typically, this will be size of the widget in landscape and portrait. * On some foldables, this might include the size on the outer and inner screens. */ - public void updateAppWidgetSize(@NonNull Bundle newOptions, @NonNull List<PointF> sizes) { + public void updateAppWidgetSize(@NonNull Bundle newOptions, @NonNull List<SizeF> sizes) { AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext); Rect padding = getDefaultPadding(); @@ -318,20 +342,20 @@ public class AppWidgetHostView extends FrameLayout { float xPaddingDips = (padding.left + padding.right) / density; float yPaddingDips = (padding.top + padding.bottom) / density; - ArrayList<PointF> paddedSizes = new ArrayList<>(sizes.size()); + ArrayList<SizeF> paddedSizes = new ArrayList<>(sizes.size()); float minWidth = Float.MAX_VALUE; float maxWidth = 0; float minHeight = Float.MAX_VALUE; float maxHeight = 0; for (int i = 0; i < sizes.size(); i++) { - PointF size = sizes.get(i); - PointF paddedPoint = new PointF(Math.max(0.f, size.x - xPaddingDips), - Math.max(0.f, size.y - yPaddingDips)); - paddedSizes.add(paddedPoint); - minWidth = Math.min(minWidth, paddedPoint.x); - maxWidth = Math.max(maxWidth, paddedPoint.x); - minHeight = Math.min(minHeight, paddedPoint.y); - maxHeight = Math.max(maxHeight, paddedPoint.y); + SizeF size = sizes.get(i); + SizeF paddedSize = new SizeF(Math.max(0.f, size.getWidth() - xPaddingDips), + Math.max(0.f, size.getHeight() - yPaddingDips)); + paddedSizes.add(paddedSize); + minWidth = Math.min(minWidth, paddedSize.getWidth()); + maxWidth = Math.max(maxWidth, paddedSize.getWidth()); + minHeight = Math.min(minHeight, paddedSize.getHeight()); + maxHeight = Math.max(maxHeight, paddedSize.getHeight()); } if (paddedSizes.equals( widgetManager.getAppWidgetOptions(mAppWidgetId).<PointF>getParcelableArrayList( @@ -348,35 +372,6 @@ public class AppWidgetHostView extends FrameLayout { } /** - * Set the current size of the widget. This should be the full area the AppWidgetHostView is - * given. Padding added by the framework will be accounted for automatically. - * - * This size will be used to choose the appropriate layout the next time the {@link RemoteViews} - * is re-inflated, if it was created with {@link RemoteViews#RemoteViews(Map)} . - */ - public void setCurrentSize(@NonNull PointF size) { - Rect padding = getDefaultPadding(); - float density = getResources().getDisplayMetrics().density; - float xPaddingDips = (padding.left + padding.right) / density; - float yPaddingDips = (padding.top + padding.bottom) / density; - PointF newSize = new PointF(size.x - xPaddingDips, size.y - yPaddingDips); - if (!newSize.equals(mCurrentSize)) { - mCurrentSize = newSize; - reapplyLastRemoteViews(); - } - } - - /** - * Clear the current size, indicating it is not currently known. - */ - public void clearCurrentSize() { - if (mCurrentSize != null) { - mCurrentSize = null; - reapplyLastRemoteViews(); - } - } - - /** * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -514,23 +509,22 @@ public class AppWidgetHostView extends FrameLayout { mLayoutId = -1; mViewMode = VIEW_MODE_DEFAULT; } else { + // Select the remote view we are actually going to apply. + RemoteViews rvToApply = remoteViews.getRemoteViewsToApply(mContext, mCurrentSize); if (mOnLightBackground) { - remoteViews = remoteViews.getDarkTextViews(); + rvToApply = rvToApply.getDarkTextViews(); } if (mAsyncExecutor != null && useAsyncIfPossible) { - inflateAsync(remoteViews); + inflateAsync(rvToApply); return; } - // Prepare a local reference to the remote Context so we're ready to - // inflate any requested LayoutParams. - mRemoteContext = getRemoteContext(); - int layoutId = remoteViews.getLayoutId(); + int layoutId = rvToApply.getLayoutId(); // If our stale view has been prepared to match active, and the new // layout matches, try recycling it if (content == null && layoutId == mLayoutId) { try { - remoteViews.reapply(mContext, mView, mInteractionHandler, mCurrentSize, + rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize, mColorResources); content = mView; recycled = true; @@ -543,7 +537,7 @@ public class AppWidgetHostView extends FrameLayout { // Try normal RemoteView inflation if (content == null) { try { - content = remoteViews.apply(mContext, this, mInteractionHandler, + content = rvToApply.apply(mContext, this, mInteractionHandler, mCurrentSize, mColorResources); if (LOGD) Log.d(TAG, "had to inflate new layout"); } catch (RuntimeException e) { diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 38919f61d9df..a88aed7f20a3 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -219,7 +219,7 @@ public class AppWidgetManager { public static final String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight"; /** - * A bundle extra ({@code List<PointF>}) that contains the list of possible sizes, in dips, a + * A bundle extra ({@code List<SizeF>}) that contains the list of possible sizes, in dips, a * widget instance can take. */ public static final String OPTION_APPWIDGET_SIZES = "appWidgetSizes"; diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 17bdd42a0f45..3b4bd9728db7 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -64,10 +64,12 @@ public final class AssociationRequest implements Parcelable { * set up is a specific kind of device, and some extra permissions may be granted to the app * as a result. * + * Using it requires declaring uses-permission + * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH} in the manifest. + * * @see AssociationRequest.Builder#setDeviceProfile */ - public static final String DEVICE_PROFILE_WATCH = - "android.app.role.COMPANION_DEVICE_WATCH"; + public static final String DEVICE_PROFILE_WATCH = "android.app.role.COMPANION_DEVICE_WATCH"; /** @hide */ @StringDef(value = { DEVICE_PROFILE_WATCH }) diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 59646f106db5..b441b364cec8 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -16,6 +16,7 @@ package android.companion; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -141,6 +142,10 @@ public final class CompanionDeviceManager { * <p>Calling this API requires a uses-feature * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p> * + * <p>When using {@link AssociationRequest#DEVICE_PROFILE_WATCH watch} + * {@link AssociationRequest.Builder#setDeviceProfile profile}, caller must also hold + * {@link Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH}</p> + * * @param request specific details about this request * @param callback will be called once there's at least one device found for user to choose from * @param handler A handler to control which thread the callback will be delivered on, or null, @@ -148,6 +153,9 @@ public final class CompanionDeviceManager { * * @see AssociationRequest */ + @RequiresPermission( + value = Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, + conditional = true) public void associate( @NonNull AssociationRequest request, @NonNull Callback callback, diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS index 144856b68e7f..d0d406a0c9e6 100644 --- a/core/java/android/content/OWNERS +++ b/core/java/android/content/OWNERS @@ -4,4 +4,7 @@ per-file ContextWrapper.java = * per-file IntentFilter.java = toddke@google.com per-file IntentFilter.java = patb@google.com per-file Intent.java = toddke@google.com -per-file Intent.java = patb@google.com
\ No newline at end of file +per-file Intent.java = patb@google.com +per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS +per-file ContentCaptureOptions* = file:/core/java/android/service/contentcapture/OWNERS +per-file LocusId* = file:/core/java/android/service/contentcapture/OWNERS diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl index b34574811bca..c91334a5470f 100644 --- a/core/java/android/content/pm/IShortcutService.aidl +++ b/core/java/android/content/pm/IShortcutService.aidl @@ -21,32 +21,34 @@ import android.content.IntentSender; import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutInfo; -/** - * {@hide} - */ +import com.android.internal.infra.AndroidFuture; + +/** {@hide} */ interface IShortcutService { - boolean setDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList, - int userId); + oneway void setDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList, + int userId, in AndroidFuture callback); - boolean addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList, - int userId); + oneway void addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList, + int userId, in AndroidFuture callback); - void removeDynamicShortcuts(String packageName, in List shortcutIds, int userId); + oneway void removeDynamicShortcuts(String packageName, in List shortcutIds, int userId); - void removeAllDynamicShortcuts(String packageName, int userId); + oneway void removeAllDynamicShortcuts(String packageName, int userId); - boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId); + oneway void updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId, + in AndroidFuture callback); - boolean requestPinShortcut(String packageName, in ShortcutInfo shortcut, - in IntentSender resultIntent, int userId); + oneway void requestPinShortcut(String packageName, in ShortcutInfo shortcut, + in IntentSender resultIntent, int userId, in AndroidFuture callback); - Intent createShortcutResultIntent(String packageName, in ShortcutInfo shortcut, int userId); + oneway void createShortcutResultIntent(String packageName, in ShortcutInfo shortcut, + int userId, in AndroidFuture callback); - void disableShortcuts(String packageName, in List shortcutIds, CharSequence disabledMessage, - int disabledMessageResId, int userId); + oneway void disableShortcuts(String packageName, in List shortcutIds, + CharSequence disabledMessage, int disabledMessageResId, int userId); - void enableShortcuts(String packageName, in List shortcutIds, int userId); + oneway void enableShortcuts(String packageName, in List shortcutIds, int userId); int getMaxShortcutCountPerActivity(String packageName, int userId); @@ -56,29 +58,31 @@ interface IShortcutService { int getIconMaxDimensions(String packageName, int userId); - void reportShortcutUsed(String packageName, String shortcutId, int userId); + oneway void reportShortcutUsed(String packageName, String shortcutId, int userId); - void resetThrottling(); // system only API for developer opsions + oneway void resetThrottling(); // system only API for developer opsions - void onApplicationActive(String packageName, int userId); // system only API for sysUI + oneway void onApplicationActive(String packageName, int userId); // system only API for sysUI byte[] getBackupPayload(int user); - void applyRestore(in byte[] payload, int user); + oneway void applyRestore(in byte[] payload, int user); boolean isRequestPinItemSupported(int user, int requestType); // System API used by framework's ShareSheet (ChooserActivity) - ParceledListSlice getShareTargets(String packageName, in IntentFilter filter, int userId); + oneway void getShareTargets(String packageName, in IntentFilter filter, int userId, + in AndroidFuture<ParceledListSlice> callback); boolean hasShareTargets(String packageName, String packageToCheck, int userId); - void removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId); + oneway void removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId); - ParceledListSlice getShortcuts(String packageName, int matchFlags, int userId); + oneway void getShortcuts(String packageName, int matchFlags, int userId, + in AndroidFuture<ParceledListSlice<ShortcutInfo>> callback); - void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId); + oneway void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId); - void updateShortcutVisibility(String callingPkg, String packageName, in byte[] certificate, - in boolean visible, int userId); -}
\ No newline at end of file + oneway void updateShortcutVisibility(String callingPkg, String packageName, + in byte[] certificate, in boolean visible, int userId); +} diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index d3bac79aa2b9..f584ff33fa50 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -24,6 +24,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; +import android.annotation.WorkerThread; import android.app.Notification; import android.app.usage.UsageStatsManager; import android.compat.annotation.UnsupportedAppUsage; @@ -42,10 +43,12 @@ import android.os.ServiceManager; import android.os.UserHandle; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.infra.AndroidFuture; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.concurrent.ExecutionException; /** * <p><code>ShortcutManager</code> executes operations on an app's set of <i>shortcuts</i>, which @@ -140,13 +143,16 @@ public class ShortcutManager { * * @throws IllegalStateException when the user is locked. */ + @WorkerThread public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { + final AndroidFuture<Boolean> future = new AndroidFuture<>(); try { - return mService.setDynamicShortcuts(mContext.getPackageName(), - new ParceledListSlice(shortcutInfoList), injectMyUserId()); + mService.setDynamicShortcuts(mContext.getPackageName(), + new ParceledListSlice(shortcutInfoList), injectMyUserId(), future); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + future.completeExceptionally(e); } + return getFutureOrThrow(future); } /** @@ -158,14 +164,17 @@ public class ShortcutManager { * * @throws IllegalStateException when the user is locked. */ + @WorkerThread @NonNull public List<ShortcutInfo> getDynamicShortcuts() { + final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>(); try { - return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_DYNAMIC, - injectMyUserId()).getList(); + mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_DYNAMIC, injectMyUserId(), + future); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + future.completeExceptionally(e); } + return getFutureOrThrow(future).getList(); } /** @@ -177,14 +186,17 @@ public class ShortcutManager { * * @throws IllegalStateException when the user is locked. */ + @WorkerThread @NonNull public List<ShortcutInfo> getManifestShortcuts() { + final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>(); try { - return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_MANIFEST, - injectMyUserId()).getList(); + mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_MANIFEST, injectMyUserId(), + future); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + future.completeExceptionally(e); } + return getFutureOrThrow(future).getList(); } /** @@ -205,14 +217,16 @@ public class ShortcutManager { * * @throws IllegalStateException when the user is locked. */ + @WorkerThread @NonNull public List<ShortcutInfo> getShortcuts(@ShortcutMatchFlags int matchFlags) { + final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>(); try { - return mService.getShortcuts(mContext.getPackageName(), matchFlags, injectMyUserId()) - .getList(); + mService.getShortcuts(mContext.getPackageName(), matchFlags, injectMyUserId(), future); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + future.completeExceptionally(e); } + return getFutureOrThrow(future).getList(); } /** @@ -228,13 +242,16 @@ public class ShortcutManager { * * @throws IllegalStateException when the user is locked. */ + @WorkerThread public boolean addDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { + final AndroidFuture<Boolean> future = new AndroidFuture<>(); try { - return mService.addDynamicShortcuts(mContext.getPackageName(), - new ParceledListSlice(shortcutInfoList), injectMyUserId()); + mService.addDynamicShortcuts(mContext.getPackageName(), + new ParceledListSlice(shortcutInfoList), injectMyUserId(), future); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + future.completeExceptionally(e); } + return getFutureOrThrow(future); } /** @@ -287,14 +304,17 @@ public class ShortcutManager { * * @throws IllegalStateException when the user is locked. */ + @WorkerThread @NonNull public List<ShortcutInfo> getPinnedShortcuts() { + final AndroidFuture<ParceledListSlice<ShortcutInfo>> future = new AndroidFuture<>(); try { - return mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_PINNED, - injectMyUserId()).getList(); + mService.getShortcuts(mContext.getPackageName(), FLAG_MATCH_PINNED, injectMyUserId(), + future); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + future.completeExceptionally(e); } + return getFutureOrThrow(future).getList(); } /** @@ -309,13 +329,16 @@ public class ShortcutManager { * * @throws IllegalStateException when the user is locked. */ + @WorkerThread public boolean updateShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) { + final AndroidFuture<Boolean> future = new AndroidFuture<>(); try { - return mService.updateShortcuts(mContext.getPackageName(), - new ParceledListSlice(shortcutInfoList), injectMyUserId()); + mService.updateShortcuts(mContext.getPackageName(), + new ParceledListSlice(shortcutInfoList), injectMyUserId(), future); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + future.completeExceptionally(e); } + return getFutureOrThrow(future); } /** @@ -584,14 +607,17 @@ public class ShortcutManager { * @throws IllegalStateException The caller doesn't have a foreground activity or a foreground * service, or the device is locked. */ + @WorkerThread public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut, @Nullable IntentSender resultIntent) { + final AndroidFuture<Boolean> future = new AndroidFuture<>(); try { - return mService.requestPinShortcut(mContext.getPackageName(), shortcut, - resultIntent, injectMyUserId()); + mService.requestPinShortcut(mContext.getPackageName(), shortcut, + resultIntent, injectMyUserId(), future); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + future.completeExceptionally(e); } + return getFutureOrThrow(future); } /** @@ -611,13 +637,16 @@ public class ShortcutManager { * * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled. */ + @WorkerThread public Intent createShortcutResultIntent(@NonNull ShortcutInfo shortcut) { + final AndroidFuture<Intent> future = new AndroidFuture<>(); try { - return mService.createShortcutResultIntent(mContext.getPackageName(), shortcut, - injectMyUserId()); + mService.createShortcutResultIntent(mContext.getPackageName(), shortcut, + injectMyUserId(), future); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + future.completeExceptionally(e); } + return getFutureOrThrow(future); } /** @@ -650,16 +679,18 @@ public class ShortcutManager { * @return List of {@link ShareShortcutInfo}s that match the given IntentFilter. * @hide */ + @WorkerThread @NonNull @SystemApi @RequiresPermission(Manifest.permission.MANAGE_APP_PREDICTIONS) public List<ShareShortcutInfo> getShareTargets(@NonNull IntentFilter filter) { + final AndroidFuture<ParceledListSlice> future = new AndroidFuture<>(); try { - return mService.getShareTargets(mContext.getPackageName(), filter, - injectMyUserId()).getList(); + mService.getShareTargets(mContext.getPackageName(), filter, injectMyUserId(), future); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + future.completeExceptionally(e); } + return getFutureOrThrow(future).getList(); } /** @@ -788,4 +819,21 @@ public class ShortcutManager { throw e.rethrowFromSystemServer(); } } + + private static <T> T getFutureOrThrow(@NonNull AndroidFuture<T> future) { + try { + return future.get(); + } catch (Throwable e) { + if (e instanceof ExecutionException) { + e = e.getCause(); + } + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + if (e instanceof Error) { + throw (Error) e; + } + throw new RuntimeException(e); + } + } } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 16ab900dee06..07ebbaff67ea 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -24,13 +24,11 @@ import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; import android.hardware.camera2.params.RecommendedStreamConfigurationMap; import android.hardware.camera2.params.SessionConfiguration; -import android.hardware.camera2.utils.ArrayUtils; import android.hardware.camera2.utils.TypeReference; import android.os.Build; import android.util.Rational; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -641,27 +639,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri */ @NonNull public Set<String> getPhysicalCameraIds() { - int[] availableCapabilities = get(REQUEST_AVAILABLE_CAPABILITIES); - if (availableCapabilities == null) { - throw new AssertionError("android.request.availableCapabilities must be non-null " - + "in the characteristics"); - } - - if (!ArrayUtils.contains(availableCapabilities, - REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) { - return Collections.emptySet(); - } - byte[] physicalCamIds = get(LOGICAL_MULTI_CAMERA_PHYSICAL_IDS); - - String physicalCamIdString = null; - try { - physicalCamIdString = new String(physicalCamIds, "UTF-8"); - } catch (java.io.UnsupportedEncodingException e) { - throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string"); - } - String[] physicalCameraIdArray = physicalCamIdString.split("\0"); - - return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(physicalCameraIdArray))); + return mProperties.getPhysicalCameraIds(); } /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ @@ -2931,6 +2909,74 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<android.util.Size>("android.scaler.defaultSecureImageSize", android.util.Size.class); /** + * <p>The available multi-resolution stream configurations that this + * physical camera device supports + * (i.e. format, width, height, output/input stream).</p> + * <p>This list contains a subset of the parent logical camera's multi-resolution stream + * configurations which belong to this physical camera, and it will advertise and will only + * advertise the maximum supported resolutions for a particular format.</p> + * <p>If this camera device isn't a physical camera device constituting a logical camera, + * but a standalone ULTRA_HIGH_RESOLUTION_SENSOR camera, this field represents the + * multi-resolution input/output stream configurations of default mode and max resolution + * modes. The sizes will be the maximum resolution of a particular format for default mode + * and max resolution mode.</p> + * <p>This field will only be advertised if the device is a physical camera of a + * logical multi-camera device or an ultra high resolution sensor camera. For a logical + * multi-camera, the camera API will derive the logical camera’s multi-resolution stream + * configurations from all physical cameras. For an ultra high resolution sensor camera, this + * is used directly as the camera’s multi-resolution stream configurations.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * <p><b>Limited capability</b> - + * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the + * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @hide + */ + public static final Key<android.hardware.camera2.params.StreamConfiguration[]> SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS = + new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.scaler.physicalCameraMultiResolutionStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class); + + /** + * <p>The multi-resolution stream configurations supported by this logical camera + * or ultra high resolution sensor camera device.</p> + * <p>Multi-resolution streams can be used by a LOGICAL_MULTI_CAMERA or an + * ULTRA_HIGH_RESOLUTION_SENSOR camera where the images sent or received can vary in + * resolution per frame. This is useful in cases where the camera device's effective full + * resolution changes depending on factors such as the current zoom level, lighting + * condition, focus distance, or pixel mode.</p> + * <ul> + * <li>For a logical multi-camera implementing optical zoom, at different zoom level, a + * different physical camera may be active, resulting in different full-resolution image + * sizes.</li> + * <li>For an ultra high resolution camera, depending on whether the camera operates in default + * mode, or maximum resolution mode, the output full-size images may be of either binned + * resolution or maximum resolution.</li> + * </ul> + * <p>To use multi-resolution output streams, the supported formats can be queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats }. + * A {@link android.hardware.camera2.MultiResolutionImageReader } can then be created for a + * supported format with the MultiResolutionStreamInfo group queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo }.</p> + * <p>If a camera device supports multi-resolution output streams for a particular format, for + * each of its mandatory stream combinations, the camera device will support using a + * MultiResolutionImageReader for the MAXIMUM stream of supported formats. Refer to + * {@link android.hardware.camera2.CameraDevice#createCaptureSession } for additional details.</p> + * <p>To use multi-resolution input streams, the supported formats can be queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getInputFormats }. + * A reprocessable CameraCaptureSession can then be created using an {@link android.hardware.camera2.params.InputConfiguration InputConfiguration} constructed with + * the input MultiResolutionStreamInfo group, queried by {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getInputInfo }.</p> + * <p>If a camera device supports multi-resolution {@code YUV} input and multi-resolution + * {@code YUV} output, or multi-resolution {@code PRIVATE} input and multi-resolution + * {@code PRIVATE} output, {@code JPEG} and {@code YUV} are guaranteed to be supported + * multi-resolution output stream formats. Refer to + * {@link android.hardware.camera2.CameraDevice#createCaptureSession } for + * details about the additional mandatory stream combinations in this case.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + */ + @PublicKey + @NonNull + @SyntheticKey + public static final Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP = + new Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap>("android.scaler.multiResolutionStreamConfigurationMap", android.hardware.camera2.params.MultiResolutionStreamConfigurationMap.class); + + /** * <p>The area of the image sensor which corresponds to active pixels after any geometric * distortion correction has been applied.</p> * <p>This is the rectangle representing the size of the active region of the sensor (i.e. diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index ac6ba0a4ac58..af48b71f9962 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -770,6 +770,8 @@ public abstract class CameraDevice implements AutoCloseable { * streams with {@code Y8} in all guaranteed stream combinations for the device's hardware level * and capabilities.</p> * + * <p>Clients can access the above mandatory stream combination tables via + * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p> * * <p>Devices capable of outputting HEIC formats ({@link StreamConfigurationMap#getOutputFormats} * contains {@link android.graphics.ImageFormat#HEIC}) will support substituting {@code JPEG} @@ -777,8 +779,33 @@ public abstract class CameraDevice implements AutoCloseable { * level and capabilities. Calling createCaptureSession with both JPEG and HEIC outputs is not * supported.</p> * - * <p>Clients can access the above mandatory stream combination tables via - * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p> + * <p>Devices capable of multi-resolution output for a particular format ( + * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo} + * returns a non-empty list) support using {@link MultiResolutionImageReader} for MAXIMUM + * resolution streams of that format for all mandatory stream combinations. For example, + * if a LIMITED camera device supports multi-resolution output streams for both {@code JPEG} and + * {@code PRIVATE}, in addition to the stream configurations + * in the LIMITED and Legacy table above, the camera device supports the following guaranteed + * stream combinations ({@code MULTI_RES} in the Max size column refers to a {@link + * MultiResolutionImageReader} created based on the variable max resolutions supported): + * + * <table> + * <tr><th colspan="7">LEGACY-level additional guaranteed combinations with MultiResolutionoutputs</th></tr> + * <tr> <th colspan="2" id="rb">Target 1</th> <th colspan="2" id="rb">Target 2</th> <th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> + * <tr> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th> <th>Type</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td colspan="2" id="rb"></td> <td>Simple preview, GPU video processing, or no-preview video recording.</td> </tr> + * <tr> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td colspan="2" id="rb"></td> <td>No-viewfinder still image capture.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td colspan="2" id="rb"></td> <td>Standard still imaging.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>Still capture plus in-app processing.</td> </tr> + * </table><br> + * <table> + * <tr><th colspan="7">LIMITED-level additional guaranteed configurations with MultiResolutionoutputs</th></tr> + * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>Two-input in-app processing with still capture.</td> </tr> + * </table><br> + * The same logic applies to other hardware levels and capabilities. + * </p> * * <p>Since the capabilities of camera devices vary greatly, a given camera device may support * target combinations with sizes outside of these guarantees, but this can only be tested for @@ -939,6 +966,32 @@ public abstract class CameraDevice implements AutoCloseable { * </table><br> * </p> * + * <p>If a camera device supports multi-resolution {@code YUV} input and multi-resolution + * {@code YUV} output or supports multi-resolution {@code PRIVATE} input and multi-resolution + * {@code PRIVATE} output, the additional mandatory stream combinations for LIMITED and FULL devices are listed + * below ({@code MULTI_RES} in the Max size column refers to a + * {@link MultiResolutionImageReader} for output, and a multi-resolution + * {@link InputConfiguration} for input): + * <table> + * <tr><th colspan="11">LIMITED-level additional guaranteed configurations for creating a reprocessable capture session with multi-resolution input and multi-resolution outputs<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr> + * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>No-viewfinder still image reprocessing.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>ZSL(Zero-Shutter-Lag) still imaging.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>ZSL still and in-app processing imaging.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>ZSL in-app processing with still capture.</td> </tr> + * </table><br> + * <table> + * <tr><th colspan="11">FULL-level additional guaranteed configurations for creating a reprocessable capture session with multi-resolution input and multi-resolution outputs<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr> + * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>Maximum-resolution ZSL in-app processing with regular preview.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>Maximum-resolution two-input ZSL in-app processing.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>ZSL still capture and in-app processing.</td> </tr> + * </table><br> + * No additional mandatory stream combinations for RAW capability and LEVEL-3 hardware level. + * </p> + * * <h3>Constrained high-speed recording</h3> * * <p>The application can use a diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 3e0e3f62574f..a3c6f2f1eafd 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -31,6 +31,7 @@ import android.hardware.camera2.impl.CameraDeviceImpl; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.params.ExtensionSessionConfiguration; import android.hardware.camera2.params.SessionConfiguration; +import android.hardware.camera2.params.StreamConfiguration; import android.hardware.camera2.utils.CameraIdAndSessionConfiguration; import android.hardware.camera2.utils.ConcurrentCameraIdCombination; import android.hardware.display.DisplayManager; @@ -51,6 +52,7 @@ import android.view.Display; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; @@ -372,6 +374,47 @@ public final class CameraManager { } /** + * Get all physical cameras' multi-resolution stream configuration map + * + * <p>For a logical multi-camera, query the map between physical camera id and + * the physical camera's multi-resolution stream configuration. This map is in turn + * combined to form the logical camera's multi-resolution stream configuration map.</p> + */ + private Map<String, StreamConfiguration[]> getPhysicalCameraMultiResolutionConfigs( + CameraMetadataNative info, ICameraService cameraService) + throws CameraAccessException { + HashMap<String, StreamConfiguration[]> multiResolutionStreamConfigurations = + new HashMap<String, StreamConfiguration[]>(); + + // Query the characteristics of all physical sub-cameras, and combine the multi-resolution + // stream configurations. Note that framework derived formats such as HEIC and DEPTH_JPEG + // aren't supported as multi-resolution input or output formats. + Set<String> physicalCameraIds = info.getPhysicalCameraIds(); + try { + for (String physicalCameraId : physicalCameraIds) { + CameraMetadataNative physicalCameraInfo = + cameraService.getCameraCharacteristics(physicalCameraId); + StreamConfiguration[] configs = physicalCameraInfo.get( + CameraCharacteristics. + SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS); + if (configs != null) { + multiResolutionStreamConfigurations.put(physicalCameraId, configs); + } + } + + // TODO: If this is an ultra high resolution sensor camera, combine the multi-resolution + // stream combination from "info" as well. + } catch (RemoteException e) { + ServiceSpecificException sse = new ServiceSpecificException( + ICameraService.ERROR_DISCONNECTED, + "Camera service is currently unavailable"); + throwAsPublicException(sse); + } + + return multiResolutionStreamConfigurations; + } + + /** * <p>Query the capabilities of a camera device. These capabilities are * immutable for a given camera.</p> * @@ -418,12 +461,19 @@ public final class CameraManager { } catch (NumberFormatException e) { Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer"); } + boolean hasConcurrentStreams = CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId); info.setHasMandatoryConcurrentStreams(hasConcurrentStreams); info.setDisplaySize(displaySize); - characteristics = new CameraCharacteristics(info); + Map<String, StreamConfiguration[]> multiResolutionSizeMap = + getPhysicalCameraMultiResolutionConfigs(info, cameraService); + if (multiResolutionSizeMap.size() > 0) { + info.setMultiResolutionStreamConfigurationMap(multiResolutionSizeMap); + } + + characteristics = new CameraCharacteristics(info); } catch (ServiceSpecificException e) { throwAsPublicException(e); } catch (RemoteException e) { diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java new file mode 100644 index 000000000000..c592f19bc45c --- /dev/null +++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2; + +import android.annotation.CallbackExecutor; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.graphics.ImageFormat; +import android.graphics.ImageFormat.Format; +import android.hardware.HardwareBuffer; +import android.hardware.HardwareBuffer.Usage; +import android.media.Image; +import android.media.ImageReader; +import android.hardware.camera2.params.MultiResolutionStreamInfo; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.view.Surface; + + +import java.nio.NioUtils; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * <p>The MultiResolutionImageReader class wraps a group of {@link ImageReader ImageReaders} with + * the same format and different sizes, source camera Id, or camera sensor modes.</p> + * + * <p>The main use case of this class is for a + * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA logical + * multi-camera} or an ultra high resolution sensor camera to output variable-size images. For a + * logical multi-camera which implements optical zoom, different physical cameras may have different + * maximum resolutions. As a result, when the camera device switches between physical cameras + * depending on zoom ratio, the maximum resolution for a particular format may change. For an + * ultra high resolution sensor camera, the camera device may deem it better or worse to run in + * maximum resolution mode / default mode depending on lighting conditions. So the application may + * choose to let the camera device decide on its behalf.</p> + * + * <p>MultiResolutionImageReader should be used for a camera device only if the camera device + * supports multi-resolution output stream by advertising the specified output format in {@link + * CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP}.</p> + * + * <p>To acquire images from the MultiResolutionImageReader, the application must use the + * {@link ImageReader} object passed by + * {@link ImageReader.OnImageAvailableListener#onImageAvailable} callback to call + * {@link ImageReader#acquireNextImage} or {@link ImageReader#acquireLatestImage}. The application + * must not use the {@link ImageReader} passed by an {@link + * ImageReader.OnImageAvailableListener#onImageAvailable} callback to acquire future images + * because future images may originate from a different {@link ImageReader} contained within the + * {@code MultiResolutionImageReader}.</p> + * + * + * @see ImageReader + * @see android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP + */ +public class MultiResolutionImageReader implements AutoCloseable { + + private static final String TAG = "MultiResolutionImageReader"; + + /** + * <p> + * Create a new multi-resolution reader based on a group of camera stream properties returned + * by a camera device. + * </p> + * <p> + * The valid size and formats depend on the camera characteristics. + * {@code MultiResolutionImageReader} for an image format is supported by the camera device if + * the format is in the supported multi-resolution output stream formats returned by + * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats}. + * If the image format is supported, the {@code MultiResolutionImageReader} object can be + * created with the {@code streams} objects returned by + * {@link android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputInfo}. + * </p> + * <p> + * The {@code maxImages} parameter determines the maximum number of + * {@link Image} objects that can be be acquired from each of the {@code ImageReader} + * within the {@code MultiResolutionImageReader}. However, requesting more buffers will + * use up more memory, so it is important to use only the minimum number necessary. The + * application is strongly recommended to acquire no more than {@code maxImages} images + * from all of the internal ImageReader objects combined. By keeping track of the number of + * acquired images for the MultiResolutionImageReader, the application doesn't need to do the + * bookkeeping for each internal ImageReader returned from {@link + * ImageReader.OnImageAvailableListener#onImageAvailable onImageAvailable} callback. + * </p> + * <p> + * Unlike the normal ImageReader, the MultiResolutionImageReader has a more complex + * configuration sequence. Instead of passing the same surface to OutputConfiguration and + * CaptureRequest, the + * {@link android.hardware.camera2.params.OutputConfiguration#createInstancesForMultiResolutionOutput} + * call needs to be used to create the OutputConfigurations for session creation, and then + * {@link #getSurface} is used to get {@link CaptureRequest.Builder#addTarget the target for + * CaptureRequest}. + * </p> + * @param streams The group of multi-resolution stream info, which is used to create + * a multi-resolution reader containing a number of ImageReader objects. Each + * ImageReader object represents a multi-resolution stream in the group. + * @param format The format of the Image that this multi-resolution reader will produce. + * This must be one of the {@link android.graphics.ImageFormat} or + * {@link android.graphics.PixelFormat} constants. Note that not all formats are + * supported, like ImageFormat.NV21. The supported multi-resolution + * reader format can be queried by {@link + * android.hardware.camera2.params.MultiResolutionStreamConfigurationMap#getOutputFormats}. + * @param maxImages The maximum number of images the user will want to + * access simultaneously. This should be as small as possible to + * limit memory use. Once maxImages images are obtained by the + * user from any given internal ImageReader, one of them has to be released before + * a new Image will become available for access through the ImageReader's + * {@link ImageReader#acquireLatestImage()} or + * {@link ImageReader#acquireNextImage()}. Must be greater than 0. + * @see Image + * @see + * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP + * @see + * android.hardware.camera2.params.MultiResolutionStreamConfigurationMap + */ + public static @NonNull MultiResolutionImageReader newInstance( + @NonNull Collection<MultiResolutionStreamInfo> streams, + @Format int format, + @IntRange(from = 1) int maxImages) { + return new MultiResolutionImageReader(streams, format, maxImages); + } + + /** + * @hide + */ + protected MultiResolutionImageReader(Collection<MultiResolutionStreamInfo> streams, + int format, int maxImages) { + mFormat = format; + mMaxImages = maxImages; + + if (streams == null || streams.size() <= 1) { + throw new IllegalArgumentException( + "The streams info collection must contain at least 2 entries"); + } + if (mMaxImages < 1) { + throw new IllegalArgumentException( + "Maximum outstanding image count must be at least 1"); + } + + if (format == ImageFormat.NV21) { + throw new IllegalArgumentException( + "NV21 format is not supported"); + } + + int numImageReaders = streams.size(); + mReaders = new ImageReader[numImageReaders]; + mStreamInfo = new MultiResolutionStreamInfo[numImageReaders]; + int index = 0; + for (MultiResolutionStreamInfo streamInfo : streams) { + mReaders[index] = ImageReader.newInstance(streamInfo.getWidth(), + streamInfo.getHeight(), format, maxImages); + mStreamInfo[index] = streamInfo; + index++; + } + } + + /** + * Set onImageAvailableListener callback. + * + * <p>This function sets the onImageAvailableListener for all the internal + * {@link ImageReader} objects.</p> + * + * <p>For a multi-resolution ImageReader, the timestamps of images acquired in + * onImageAvailable callback from different internal ImageReaders may become + * out-of-order due to the asynchronous callbacks between the different resolution + * image queues.</p> + * + * @param listener + * The listener that will be run. + * @param executor + * The executor which will be used when invoking the callback. + */ + @SuppressLint({"ExecutorRegistration", "SamShouldBeLast"}) + public void setOnImageAvailableListener( + @Nullable ImageReader.OnImageAvailableListener listener, + @Nullable @CallbackExecutor Executor executor) { + for (int i = 0; i < mReaders.length; i++) { + mReaders[i].setOnImageAvailableListenerWithExecutor(listener, executor); + } + } + + @Override + public void close() { + flush(); + + for (int i = 0; i < mReaders.length; i++) { + mReaders[i].close(); + } + } + + @Override + protected void finalize() { + close(); + } + + /** + * Flush pending images from all internal ImageReaders + * + * <p>Acquire and close pending images from all internal ImageReaders. This has the same + * effect as calling acquireLatestImage() on all internal ImageReaders, and closing all + * latest images.</p> + */ + public void flush() { + flushOther(null); + } + + /** + * Flush pending images from other internal ImageReaders + * + * <p>Acquire and close pending images from all internal ImageReaders except for the + * one specified.</p> + * + * @param reader The ImageReader object that won't be flushed. + * + * @hide + */ + public void flushOther(ImageReader reader) { + for (int i = 0; i < mReaders.length; i++) { + if (reader != null && reader == mReaders[i]) { + continue; + } + + while (true) { + Image image = mReaders[i].acquireNextImageNoThrowISE(); + if (image == null) { + break; + } else { + image.close(); + } + } + } + } + + /** + * Get the internal ImageReader objects + * + * @hide + */ + public @NonNull ImageReader[] getReaders() { + return mReaders; + } + + /** + * Get the surface that is used as a target for {@link CaptureRequest} + * + * <p>The application must use the surface returned by this function as a target for + * {@link CaptureRequest}. The camera device makes the decision on which internal + * {@code ImageReader} will receive the output image.</p> + * + * <p>Please note that holding on to the Surface objects returned by this method is not enough + * to keep their parent MultiResolutionImageReaders from being reclaimed. In that sense, a + * Surface acts like a {@link java.lang.ref.WeakReference weak reference} to the + * MultiResolutionImageReader that provides it.</p> + * + * @return a {@link Surface} to use as the target for a capture request. + */ + public @NonNull Surface getSurface() { + //TODO: Pick the surface from the reader for default mode stream. + return mReaders[0].getSurface(); + } + + /** + * Get the MultiResolutionStreamInfo describing the ImageReader an image originates from + * + *<p>An image from a {@code MultiResolutionImageReader} is produced from one of the underlying + *{@code ImageReader}s. This function returns the {@link MultiResolutionStreamInfo} to describe + *the property for that {@code ImageReader}, such as width, height, and physical camera Id.</p> + * + * @param reader An internal ImageReader within {@code MultiResolutionImageReader}. + * + * @return The stream info describing the internal {@code ImageReader}. + */ + public @NonNull MultiResolutionStreamInfo getStreamInfoForImageReader( + @NonNull ImageReader reader) { + for (int i = 0; i < mReaders.length; i++) { + if (reader == mReaders[i]) { + return mStreamInfo[i]; + } + } + + throw new IllegalArgumentException("ImageReader doesn't belong to this multi-resolution " + + "imagereader"); + } + + // mReaders and mStreamInfo has the same length, and their entries are 1:1 mapped. + private final ImageReader[] mReaders; + private final MultiResolutionStreamInfo[] mStreamInfo; + + private final int mFormat; + private final int mMaxImages; +} diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java index da65f71ce02c..df8eeccbe800 100644 --- a/core/java/android/hardware/camera2/TotalCaptureResult.java +++ b/core/java/android/hardware/camera2/TotalCaptureResult.java @@ -61,8 +61,8 @@ public final class TotalCaptureResult extends CaptureResult { private final List<CaptureResult> mPartialResults; private final int mSessionId; - // The map between physical camera id and capture result - private final HashMap<String, CaptureResult> mPhysicalCaptureResults; + // The map between physical camera ids and their total capture result + private final HashMap<String, TotalCaptureResult> mPhysicalCaptureResults; /** * Takes ownership of the passed-in camera metadata and the partial results @@ -83,10 +83,11 @@ public final class TotalCaptureResult extends CaptureResult { mSessionId = sessionId; - mPhysicalCaptureResults = new HashMap<String, CaptureResult>(); + mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>(); for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) { - CaptureResult physicalResult = new CaptureResult(onePhysicalResult.getCameraId(), - onePhysicalResult.getCameraMetadata(), parent, extras); + TotalCaptureResult physicalResult = new TotalCaptureResult( + onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(), + parent, extras, /*partials*/null, sessionId, new PhysicalCaptureResultInfo[0]); mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(), physicalResult); } @@ -103,7 +104,7 @@ public final class TotalCaptureResult extends CaptureResult { mPartialResults = new ArrayList<>(); mSessionId = CameraCaptureSession.SESSION_ID_NONE; - mPhysicalCaptureResults = new HashMap<String, CaptureResult>(); + mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>(); } /** @@ -146,8 +147,37 @@ public final class TotalCaptureResult extends CaptureResult { * cameras. Otherwise, an empty map is returned.</p> * @return unmodifiable map between physical camera ids and their capture result metadata + * + * @deprecated + * <p>Please use {@link #getPhysicalCameraTotalResults() instead to get the + * physical cameras' {@code TotalCaptureResult}.</p> */ public Map<String, CaptureResult> getPhysicalCameraResults() { return Collections.unmodifiableMap(mPhysicalCaptureResults); } + + /** + * Get the map between physical camera ids and their total capture result metadata + * + * <p>This function can be called for logical multi-camera devices, which are devices that have + * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability.</p> + * + * <p>If one or more streams from the underlying physical cameras were requested by the + * corresponding capture request, this function returns the total result metadata for those + * physical cameras. Otherwise, an empty map is returned.</p> + * + * <p>This function replaces the deprecated {@link #getPhysicalCameraResults}, and its return + * value is a map of TotalCaptureResult rather than CaptureResult. </p> + * + * <p>To reprocess an image from a physical camera stream, typically returned from a + * {@link MultiResolutionImageReader}, the application must look up this map to get the {@link + * TotalCaptureResult} from the physical camera and pass it to {@link + * CameraDevice#createReprocessCaptureRequest}.</p> + * + * @return unmodifiable map between physical camera ids and their total capture result metadata + */ + @NonNull + public Map<String, TotalCaptureResult> getPhysicalCameraTotalResults() { + return Collections.unmodifiableMap(mPhysicalCaptureResults); + } } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index ce3c81a2bfd6..4defd23231b8 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -36,6 +36,8 @@ import android.hardware.camera2.ICameraOfflineSession; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.ExtensionSessionConfiguration; import android.hardware.camera2.params.InputConfiguration; +import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap; +import android.hardware.camera2.params.MultiResolutionStreamInfo; import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; @@ -468,7 +470,8 @@ public class CameraDeviceImpl extends CameraDevice } if (inputConfig != null) { int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(), - inputConfig.getHeight(), inputConfig.getFormat()); + inputConfig.getHeight(), inputConfig.getFormat(), + inputConfig.isMultiResolution()); mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>( streamId, inputConfig); } @@ -1355,7 +1358,42 @@ public class CameraDeviceImpl extends CameraDevice } private void checkInputConfiguration(InputConfiguration inputConfig) { - if (inputConfig != null) { + if (inputConfig == null) { + return; + } + + if (inputConfig.isMultiResolution()) { + MultiResolutionStreamConfigurationMap configMap = mCharacteristics.get( + CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP); + + int[] inputFormats = configMap.getInputFormats(); + boolean validFormat = false; + for (int format : inputFormats) { + if (format == inputConfig.getFormat()) { + validFormat = true; + } + } + + if (validFormat == false) { + throw new IllegalArgumentException("multi-resolution input format " + + inputConfig.getFormat() + " is not valid"); + } + + boolean validSize = false; + Collection<MultiResolutionStreamInfo> inputStreamInfo = + configMap.getInputInfo(inputConfig.getFormat()); + for (MultiResolutionStreamInfo info : inputStreamInfo) { + if (inputConfig.getWidth() == info.getWidth() && + inputConfig.getHeight() == info.getHeight()) { + validSize = true; + } + } + + if (validSize == false) { + throw new IllegalArgumentException("Multi-resolution input size " + + inputConfig.getWidth() + "x" + inputConfig.getHeight() + " is not valid"); + } + } else { StreamConfigurationMap configMap = mCharacteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index e9bae0b621ce..0cdf744ecd68 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -16,6 +16,7 @@ package android.hardware.camera2.impl; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.ImageFormat; import android.graphics.Point; @@ -53,6 +54,7 @@ import android.hardware.camera2.params.Face; import android.hardware.camera2.params.HighSpeedVideoConfiguration; import android.hardware.camera2.params.LensShadingMap; import android.hardware.camera2.params.MandatoryStreamCombination; +import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap; import android.hardware.camera2.params.OisSample; import android.hardware.camera2.params.RecommendedStreamConfiguration; import android.hardware.camera2.params.RecommendedStreamConfigurationMap; @@ -61,6 +63,7 @@ import android.hardware.camera2.params.StreamConfiguration; import android.hardware.camera2.params.StreamConfigurationDuration; import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.params.TonemapCurve; +import android.hardware.camera2.utils.ArrayUtils; import android.hardware.camera2.utils.TypeReference; import android.location.Location; import android.location.LocationManager; @@ -79,9 +82,14 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Implementation of camera metadata marshal/unmarshal across Binder to @@ -747,6 +755,15 @@ public class CameraMetadataNative implements Parcelable { return (T) metadata.getExtendedSceneModeCapabilities(); } }); + sGetCommandMap.put( + CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP.getNativeKey(), + new GetCommand() { + @Override + @SuppressWarnings("unchecked") + public <T> T getValue(CameraMetadataNative metadata, Key<T> key) { + return (T) metadata.getMultiResolutionStreamConfigurationMap(); + } + }); } private int[] getAvailableFormats() { @@ -1688,6 +1705,7 @@ public class CameraMetadataNative implements Parcelable { private boolean mHasMandatoryConcurrentStreams = false; private Size mDisplaySize = new Size(0, 0); private long mBufferSize = 0; + private MultiResolutionStreamConfigurationMap mMultiResolutionStreamConfigurationMap = null; /** * Set the current camera Id. @@ -1723,6 +1741,30 @@ public class CameraMetadataNative implements Parcelable { mDisplaySize = displaySize; } + /** + * Set the multi-resolution stream configuration map. + * + * @param multiResolutionMap The multi-resolution stream configuration map. + * + * @hide + */ + public void setMultiResolutionStreamConfigurationMap( + @NonNull Map<String, StreamConfiguration[]> multiResolutionMap) { + mMultiResolutionStreamConfigurationMap = + new MultiResolutionStreamConfigurationMap(multiResolutionMap); + } + + /** + * Get the multi-resolution stream configuration map. + * + * @return The multi-resolution stream configuration map. + * + * @hide + */ + public MultiResolutionStreamConfigurationMap getMultiResolutionStreamConfigurationMap() { + return mMultiResolutionStreamConfigurationMap; + } + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private long mMetadataPtr; // native std::shared_ptr<CameraMetadata>* @@ -1777,6 +1819,7 @@ public class CameraMetadataNative implements Parcelable { mCameraId = other.mCameraId; mHasMandatoryConcurrentStreams = other.mHasMandatoryConcurrentStreams; mDisplaySize = other.mDisplaySize; + mMultiResolutionStreamConfigurationMap = other.mMultiResolutionStreamConfigurationMap; updateNativeAllocation(); other.updateNativeAllocation(); } @@ -1980,6 +2023,39 @@ public class CameraMetadataNative implements Parcelable { return true; } + /** + * Return the set of physical camera ids that this logical {@link CameraDevice} is made + * up of. + * + * If the camera device isn't a logical camera, return an empty set. + * + * @hide + */ + public Set<String> getPhysicalCameraIds() { + int[] availableCapabilities = get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); + if (availableCapabilities == null) { + throw new AssertionError("android.request.availableCapabilities must be non-null " + + "in the characteristics"); + } + + if (!ArrayUtils.contains(availableCapabilities, + CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) { + return Collections.emptySet(); + } + byte[] physicalCamIds = get(CameraCharacteristics.LOGICAL_MULTI_CAMERA_PHYSICAL_IDS); + + String physicalCamIdString = null; + try { + physicalCamIdString = new String(physicalCamIds, "UTF-8"); + } catch (java.io.UnsupportedEncodingException e) { + throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string"); + } + String[] physicalCameraIdArray = physicalCamIdString.split("\0"); + + return Collections.unmodifiableSet( + new HashSet<String>(Arrays.asList(physicalCameraIdArray))); + } + static { registerAllMarshalers(); } diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java index ba4395f70214..b6b1968bfcdd 100644 --- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java +++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java @@ -140,9 +140,10 @@ public class ICameraDeviceUserWrapper { } } - public int createInputStream(int width, int height, int format) throws CameraAccessException { + public int createInputStream(int width, int height, int format, boolean isMultiResolution) + throws CameraAccessException { try { - return mRemoteDevice.createInputStream(width, height, format); + return mRemoteDevice.createInputStream(width, height, format, isMultiResolution); } catch (Throwable t) { CameraManager.throwAsPublicException(t); throw new UnsupportedOperationException("Unexpected exception", t); diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java index 0a50f974aca8..d63683feed9b 100644 --- a/core/java/android/hardware/camera2/params/InputConfiguration.java +++ b/core/java/android/hardware/camera2/params/InputConfiguration.java @@ -16,9 +16,17 @@ package android.hardware.camera2.params; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.graphics.ImageFormat.Format; +import android.hardware.camera2.params.MultiResolutionStreamInfo; import android.hardware.camera2.utils.HashCodeHelpers; +import java.util.Collection; +import java.util.List; + +import static com.android.internal.util.Preconditions.*; + /** * Immutable class to store an input configuration that is used to create a reprocessable capture * session. @@ -31,11 +39,12 @@ public final class InputConfiguration { private final int mWidth; private final int mHeight; private final int mFormat; + private final boolean mIsMultiResolution; /** * Create an input configration with the width, height, and user-defined format. * - * <p>Images of an user-defined format are accessible by applications. Use + * <p>Images of a user-defined format are accessible by applications. Use * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP} * to query supported input formats</p> * @@ -51,6 +60,52 @@ public final class InputConfiguration { mWidth = width; mHeight = height; mFormat = format; + mIsMultiResolution = false; + } + + /** + * Create an input configration with the format and a list of multi-resolution input stream + * info. + * + * <p>Use {@link + * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP} + * to query supported multi-resolution input formats.</p> + * + * <p>To do reprocessing with variable resolution input, the application calls + * {@link android.media.ImageWriter#queueInputImage ImageWriter.queueInputImage} + * using an image from an {@link android.media.ImageReader ImageReader} or {@link + * android.hardware.camera2.MultiResolutionImageReader MultiResolutionImageReader}. See + * {@link android.hardware.camera2.CameraDevice#createReprocessCaptureRequest} for more + * details on camera reprocessing. + * </p> + * + * @param multiResolutionInputs A group of multi-resolution input info for the specified format. + * @param format Format of the input buffers. One of ImageFormat or PixelFormat constants. + * + * @see android.graphics.ImageFormat + * @see android.graphics.PixelFormat + * @see + * android.hardware.camera2.CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP + */ + public InputConfiguration(@NonNull Collection<MultiResolutionStreamInfo> multiResolutionInputs, + @Format int format) { + checkCollectionNotEmpty(multiResolutionInputs, "Input multi-resolution stream info"); + //TODO: Pick the default mode stream info for ultra-high resolution sensor camera + MultiResolutionStreamInfo info = multiResolutionInputs.iterator().next(); + mWidth = info.getWidth(); + mHeight = info.getHeight(); + mFormat = format; + mIsMultiResolution = true; + } + + /** + * @hide + */ + public InputConfiguration(int width, int height, int format, boolean isMultiResolution) { + mWidth = width; + mHeight = height; + mFormat = format; + mIsMultiResolution = isMultiResolution; } /** @@ -81,6 +136,18 @@ public final class InputConfiguration { } /** + * Whether this input configuration is of multi-resolution. + * + * <p>An multi-resolution InputConfiguration means that the reprocessing session created from it + * allows input images of different sizes.</p> + * + * @return this input configuration is multi-resolution or not. + */ + public boolean isMultiResolution() { + return mIsMultiResolution; + } + + /** * Check if this InputConfiguration is equal to another InputConfiguration. * * <p>Two input configurations are equal if and only if they have the same widths, heights, and @@ -100,7 +167,8 @@ public final class InputConfiguration { if (otherInputConfig.getWidth() == mWidth && otherInputConfig.getHeight() == mHeight && - otherInputConfig.getFormat() == mFormat) { + otherInputConfig.getFormat() == mFormat && + otherInputConfig.isMultiResolution() == mIsMultiResolution) { return true; } return false; @@ -111,19 +179,21 @@ public final class InputConfiguration { */ @Override public int hashCode() { - return HashCodeHelpers.hashCode(mWidth, mHeight, mFormat); + return HashCodeHelpers.hashCode(mWidth, mHeight, mFormat, mIsMultiResolution ? 1 : 0); } /** * Return this {@link InputConfiguration} as a string representation. * - * <p> {@code "InputConfiguration(w:%d, h:%d, format:%d)"}, where {@code %d} represents - * the width, height, and format, respectively.</p> + * <p> {@code "InputConfiguration(w:%d, h:%d, format:%d, isMultiResolution:%d)"}, + * where {@code %d} represents the width, height, format, and multi-resolution flag + * respectively.</p> * * @return string representation of {@link InputConfiguration} */ @Override public String toString() { - return String.format("InputConfiguration(w:%d, h:%d, format:%d)", mWidth, mHeight, mFormat); + return String.format("InputConfiguration(w:%d, h:%d, format:%d, isMultiResolution %b)", + mWidth, mHeight, mFormat, mIsMultiResolution); } } diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java index 776d155e5b3e..8a0172ee8018 100644 --- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java +++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java @@ -1297,19 +1297,6 @@ public final class MandatoryStreamCombination { } /** - * Size comparison method used by size comparators. - */ - private static int compareSizes(int widthA, int heightA, int widthB, int heightB) { - long left = widthA * (long) heightA; - long right = widthB * (long) heightB; - if (left == right) { - left = widthA; - right = widthB; - } - return (left < right) ? -1 : (left > right ? 1 : 0); - } - - /** * Size comparator that compares the number of pixels it covers. * * <p>If two the areas of two sizes are same, compare the widths.</p> @@ -1317,8 +1304,8 @@ public final class MandatoryStreamCombination { public static class SizeComparator implements Comparator<Size> { @Override public int compare(@NonNull Size lhs, @NonNull Size rhs) { - return compareSizes(lhs.getWidth(), lhs.getHeight(), rhs.getWidth(), - rhs.getHeight()); + return StreamConfigurationMap.compareSizes(lhs.getWidth(), lhs.getHeight(), + rhs.getWidth(), rhs.getHeight()); } } diff --git a/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java new file mode 100644 index 000000000000..1b368fb8c010 --- /dev/null +++ b/core/java/android/hardware/camera2/params/MultiResolutionStreamConfigurationMap.java @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.camera2.params; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import android.graphics.ImageFormat; +import android.graphics.ImageFormat.Format; +import android.graphics.PixelFormat; +import android.hardware.camera2.params.MultiResolutionStreamInfo; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.hardware.camera2.utils.HashCodeHelpers; + +import android.util.Size; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.Set; + +import static com.android.internal.util.Preconditions.*; + +/** + * Immutable class to store the information of the multi-resolution streams supported by + * the camera device. + * + * <p>For a {@link + * android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA + * logical multi-camera} or an ultra high resolution sensor camera, the maximum resolution of images + * produced by the camera device may be variable. For example, for a logical multi-camera, depending + * on factors such as current zoom ratio, the camera device may be backed by different physical + * cameras. If the physical cameras are of different resolutions, the application may intend to + * consume the variable full resolution images from the physical cameras. For an ultra high + * resolution sensor camera, the same use case exists where depending on lighting conditions, the + * camera device may deem it better to run in default mode and maximum resolution mode. + * </p> + * + * <p>For the use cases described above, multi-resolution output streams can be used by + * {@link android.hardware.camera2.MultiResolutionImageReader} to allow the + * camera device to output variable size maximum-resolution images.</p> + * + * <p>Similarly, multi-resolution input streams can be used for reprocessing of variable size + * images. In order to reprocess input images of different sizes, the {@link InputConfiguration} + * used for creating reprocessable session can be initialized using the group of input stream + * configurations returned by {@link #getInputInfo}.</p> + */ +public final class MultiResolutionStreamConfigurationMap { + /** + * Create a new {@link MultiResolutionStreamConfigurationMap}. + * + * @param configurations a non-{@code null} array of multi-resolution stream + * configurations supported by this camera device + * @hide + */ + public MultiResolutionStreamConfigurationMap( + @NonNull Map<String, StreamConfiguration[]> configurations) { + checkNotNull(configurations, "multi-resolution configurations must not be null"); + if (configurations.size() == 0) { + throw new IllegalArgumentException("multi-resolution configurations must not be empty"); + } + + mConfigurations = configurations; + + // For each multi-resolution stream configuration, track how many formats and sizes there + // are available to configure + for (Map.Entry<String, StreamConfiguration[]> entry : + mConfigurations.entrySet()) { + String cameraId = entry.getKey(); + StreamConfiguration[] configs = entry.getValue(); + + for (int i = 0; i < configs.length; i++) { + StreamConfiguration config = configs[i]; + int format = config.getFormat(); + + MultiResolutionStreamInfo multiResolutionStreamInfo = new MultiResolutionStreamInfo( + config.getWidth(), config.getHeight(), cameraId); + Map<Integer, List<MultiResolutionStreamInfo>> destMap; + if (config.isInput()) { + destMap = mMultiResolutionInputConfigs; + } else { + destMap = mMultiResolutionOutputConfigs; + } + + if (!destMap.containsKey(format)) { + List<MultiResolutionStreamInfo> multiResolutionStreamInfoList = + new ArrayList<MultiResolutionStreamInfo>(); + destMap.put(format, multiResolutionStreamInfoList); + } + destMap.get(format).add(multiResolutionStreamInfo); + } + } + } + + /** + * Size comparator that compares the number of pixels two MultiResolutionStreamInfo size covers. + * + * <p>If two the areas of two sizes are same, compare the widths.</p> + * + * @hide + */ + public static class SizeComparator implements Comparator<MultiResolutionStreamInfo> { + @Override + public int compare(@NonNull MultiResolutionStreamInfo lhs, + @NonNull MultiResolutionStreamInfo rhs) { + return StreamConfigurationMap.compareSizes( + lhs.getWidth(), lhs.getHeight(), rhs.getWidth(), rhs.getHeight()); + } + } + + /** + * Get the output formats in this multi-resolution stream configuration. + * + * <p>A logical multi-camera or an ultra high resolution sensor camera may support + * {@link android.hardware.camera2.MultiResolutionImageReader} to dynamically output maximum + * resolutions of different sizes (when switching between physical cameras, or between different + * modes of an ultra high resolution sensor camera). This function returns the formats + * supported for such case.</p> + * + * <p>All image formats returned by this function will be defined in either {@link ImageFormat} + * or in {@link PixelFormat} (and there is no possibility of collision).</p> + * + * @return an array of integer format, or empty array if multi-resolution output is not + * supported + * + * @see ImageFormat + * @see PixelFormat + * @see android.hardware.camera2.MultiResolutionImageReader + */ + public @NonNull @Format int[] getOutputFormats() { + return getPublicImageFormats(/*output*/true); + } + + /** + * Get the input formats in this multi-resolution stream configuration. + * + * <p>A logical multi-camera or ultra high resolution sensor camera may support reprocessing + * images of different resolutions when switching between physical cameras, or between + * different modes of the ultra high resolution sensor camera. This function returns the + * formats supported for such case.</p> + * + * <p>The supported output format for an input format can be queried by calling the camera + * device's {@link StreamConfigurationMap#getValidOutputFormatsForInput}.</p> + * + * <p>All image formats returned by this function will be defined in either {@link ImageFormat} + * or in {@link PixelFormat} (and there is no possibility of collision).</p> + * + * @return an array of integer format, or empty array if no multi-resolution reprocessing is + * supported + * + * @see ImageFormat + * @see PixelFormat + */ + public @NonNull @Format int[] getInputFormats() { + return getPublicImageFormats(/*output*/false); + } + + // Get the list of publicly visible multi-resolution input/output stream formats + private int[] getPublicImageFormats(boolean output) { + Map<Integer, List<MultiResolutionStreamInfo>> multiResolutionConfigs = + output ? mMultiResolutionOutputConfigs : mMultiResolutionInputConfigs; + int formatCount = multiResolutionConfigs.size(); + + int[] formats = new int[formatCount]; + int i = 0; + for (Integer format : multiResolutionConfigs.keySet()) { + formats[i++] = StreamConfigurationMap.imageFormatToPublic(format); + } + + return formats; + } + + /** + * Get a group of {@code MultiResolutionStreamInfo} with the requested output image + * {@code format} + * + * <p>The {@code format} should be a supported format (one of the formats returned by + * {@link #getOutputFormats}).</p> + * + * @param format an image format from {@link ImageFormat} or {@link PixelFormat} + * @return + * a group of supported {@link MultiResolutionStreamInfo}. If the {@code format} is not + * a supported multi-resolution output, an empty group is returned. + * + * @see ImageFormat + * @see PixelFormat + * @see #getOutputFormats + */ + public @NonNull Collection<MultiResolutionStreamInfo> getOutputInfo(@Format int format) { + return getInfo(format, /*false*/ true); + } + + /** + * Get a group of {@code MultiResolutionStreamInfo} with the requested input image {@code format} + * + * <p>The {@code format} should be a supported format (one of the formats returned by + * {@link #getInputFormats}).</p> + * + * @param format an image format from {@link ImageFormat} or {@link PixelFormat} + * @return + * a group of supported {@link MultiResolutionStreamInfo}. If the {@code format} is not + * a supported multi-resolution input, an empty group is returned. + * + * @see ImageFormat + * @see PixelFormat + * @see #getInputFormats + */ + public @NonNull Collection<MultiResolutionStreamInfo> getInputInfo(@Format int format) { + return getInfo(format, /*false*/ false); + } + + // Get multi-resolution stream info for a particular format + private @NonNull Collection<MultiResolutionStreamInfo> getInfo(int format, boolean output) { + int internalFormat = StreamConfigurationMap.imageFormatToInternal(format); + Map<Integer, List<MultiResolutionStreamInfo>> multiResolutionConfigs = + output ? mMultiResolutionOutputConfigs : mMultiResolutionInputConfigs; + if (multiResolutionConfigs.containsKey(internalFormat)) { + return Collections.unmodifiableCollection(multiResolutionConfigs.get(internalFormat)); + } else { + return Collections.emptyList(); + } + } + + private void appendConfigurationsString(StringBuilder sb, boolean output) { + sb.append(output ? "Outputs(" : "Inputs("); + int[] formats = getPublicImageFormats(output); + if (formats != null) { + for (int format : formats) { + Collection<MultiResolutionStreamInfo> streamInfoList = + getInfo(format, output); + sb.append("[" + StreamConfigurationMap.formatToString(format) + ":"); + for (MultiResolutionStreamInfo streamInfo : streamInfoList) { + sb.append(String.format("[w:%d, h:%d, id:%s], ", + streamInfo.getWidth(), streamInfo.getHeight(), + streamInfo.getPhysicalCameraId())); + } + // Remove the pending ", " + if (sb.charAt(sb.length() - 1) == ' ') { + sb.delete(sb.length() - 2, sb.length()); + } + sb.append("]"); + } + } + sb.append(")"); + } + + /** + * Check if this {@link MultiResolutionStreamConfigurationMap} is equal to another + * {@link MultiResolutionStreamConfigurationMap}. + * + * @return {@code true} if the objects were equal, {@code false} otherwise + */ + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj instanceof MultiResolutionStreamConfigurationMap) { + final MultiResolutionStreamConfigurationMap other = + (MultiResolutionStreamConfigurationMap) obj; + if (!mConfigurations.keySet().equals(other.mConfigurations.keySet())) { + return false; + } + + for (String id : mConfigurations.keySet()) { + if (!Arrays.equals(mConfigurations.get(id), other.mConfigurations.get(id))) { + return false; + } + } + + return true; + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return HashCodeHelpers.hashCodeGeneric( + mConfigurations, mMultiResolutionOutputConfigs, mMultiResolutionInputConfigs); + } + + /** + * Return this {@link MultiResolutionStreamConfigurationMap} as a string representation. + * + * <p>{@code "MultiResolutionStreamConfigurationMap(Outputs([format1: [w:%d, h:%d, id:%s], ... + * ... [w:%d, h:%d, id:%s]), [format2: [w:%d, h:%d, id:%s], ... [w:%d, h:%d, id:%s]], ...), + * Inputs([format1: [w:%d, h:%d, id:%s], ... [w:%d, h:%d, id:%s], ...).</p> + * + * @return string representation of {@link MultiResolutionStreamConfigurationMap} + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder("MultiResolutionStreamConfigurationMap("); + appendConfigurationsString(sb, /*output*/ true); + sb.append(","); + appendConfigurationsString(sb, /*output*/ false); + sb.append(")"); + + return sb.toString(); + } + + + private final Map<String, StreamConfiguration[]> mConfigurations; + + /** Format -> list of MultiResolutionStreamInfo used to create MultiResolutionImageReader */ + private final Map<Integer, List<MultiResolutionStreamInfo>> mMultiResolutionOutputConfigs + = new HashMap<Integer, List<MultiResolutionStreamInfo>>(); + /** Format -> list of MultiResolutionStreamInfo used for multi-resolution reprocessing */ + private final Map<Integer, List<MultiResolutionStreamInfo>> mMultiResolutionInputConfigs + = new HashMap<Integer, List<MultiResolutionStreamInfo>>(); +} diff --git a/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java new file mode 100644 index 000000000000..aa1d1d4aaa18 --- /dev/null +++ b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2.params; + +import android.annotation.NonNull; + +import java.util.Objects; + +/** + * A utility class describing the properties of one stream of fixed-size image buffers + * backing a multi-resolution image stream. + * + * <p>A group of {@link MultiResolutionStreamInfo} are used to describe the properties of a + * multi-resolution image stream for a particular format. The + * {@link android.hardware.camera2.MultiResolutionImageReader} class represents a + * multi-resolution output stream, and is constructed using a group of + * {@link MultiResolutionStreamInfo}. A group of {@link MultiResolutionStreamInfo} can also be used + * to create a multi-resolution reprocessable camera capture session. See + * {@link android.hardware.camera2.params.InputConfiguration} for details.</p> + * + * @see InputConfiguration + * @see android.hardware.camera2.MultiResolutionImageReader + */ +public class MultiResolutionStreamInfo { + private int mStreamWidth; + private int mStreamHeight; + private String mPhysicalCameraId; + + /** + * Create a new {@link MultiResolutionStreamInfo}. + * + * <p>This class creates a {@link MultiResolutionStreamInfo} using image width, image height, + * and the physical camera Id images originate from.</p> + * + * <p>Normally applications do not need to create these directly. Use {@link + * MultiResolutionStreamConfigurationMap#getOutputInfo} or {@link + * MultiResolutionStreamConfigurationMap#getInputInfo} to obtain them for a particular format + * instead.</p> + */ + public MultiResolutionStreamInfo(int streamWidth, int streamHeight, + @NonNull String physicalCameraId) { + mStreamWidth = streamWidth; + mStreamHeight = streamHeight; + mPhysicalCameraId = physicalCameraId; + } + + /** + * The width of this particular image buffer stream in pixels. + */ + public int getWidth() { + return mStreamWidth; + } + + /** + * The height of this particular image buffer stream in pixels. + */ + public int getHeight() { + return mStreamHeight; + } + + /** + * The physical camera Id of this particular image buffer stream. + */ + public @NonNull String getPhysicalCameraId() { + return mPhysicalCameraId; + } + + /** + * Check if this {@link MultiResolutionStreamInfo} is equal to another + * {@link MultiResolutionStreamInfo}. + * + * @return {@code true} if the objects were equal, {@code false} otherwise + */ + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj instanceof MultiResolutionStreamInfo) { + final MultiResolutionStreamInfo other = (MultiResolutionStreamInfo) obj; + return mStreamWidth == other.mStreamWidth && + mStreamHeight == other.mStreamHeight && + mPhysicalCameraId.equals(other.mPhysicalCameraId); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Objects.hash( + mStreamWidth, mStreamHeight, mPhysicalCameraId); + } +} diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index a20a1bf194ea..e31bd601fc03 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -24,9 +24,13 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.graphics.ImageFormat; import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.MultiResolutionImageReader; +import android.hardware.camera2.params.MultiResolutionStreamInfo; import android.hardware.camera2.utils.HashCodeHelpers; import android.hardware.camera2.utils.SurfaceUtils; +import android.media.ImageReader; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -34,6 +38,7 @@ import android.util.Size; import android.view.Surface; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -81,6 +86,13 @@ import java.util.Objects; * {@link CameraCaptureSession#updateOutputConfiguration} can be called after the configuration * finalize method returns without exceptions.</li> * + * <li>If the camera device supports multi-resolution output streams, {@link + * CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP} will contain the + * formats and their corresponding stream info. The application can use an OutputConfiguration + * created with the multi-resolution stream info queried from {@link + * MultiResolutionStreamConfigurationMap#getOutputInfo} and + * {@link android.hardware.camera2.MultiResolutionImageReader} to capture variable size images. + * * </ul> * * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats except @@ -88,6 +100,7 @@ import java.util.Objects; * device support. On prior API levels, only {@link ImageFormat#PRIVATE} format may be used.</p> * * @see CameraDevice#createCaptureSessionByOutputConfigurations + * @see CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP * */ public final class OutputConfiguration implements Parcelable { @@ -206,6 +219,33 @@ public final class OutputConfiguration implements Parcelable { } /** + * Set the multi-resolution output flag. + * + * <p>Specify that this OutputConfiguration is part of a multi-resolution output stream group + * used by {@link android.hardware.camera2.MultiResolutionImageReader}.</p> + * + * <p>This function must only be called for an OutputConfiguration with a non-negative + * group ID. And all OutputConfigurations of a MultiResolutionImageReader will have the same + * group ID and have this flag set.</p> + * + * @throws IllegalStateException If surface sharing is enabled via {@link #enableSurfaceSharing} + * call, or no non-negative group ID has been set. + * @hide + */ + void setMultiResolutionOutput() { + if (mIsShared) { + throw new IllegalStateException("Multi-resolution output flag must not be set for " + + "configuration with surface sharing"); + } + if (mSurfaceGroupId == SURFACE_GROUP_ID_NONE) { + throw new IllegalStateException("Multi-resolution output flag should only be set for " + + "surface with non-negative group ID"); + } + + mIsMultiResolution = true; + } + + /** * Create a new {@link OutputConfiguration} instance. * * <p>This constructor takes an argument for desired camera rotation</p> @@ -265,6 +305,45 @@ public final class OutputConfiguration implements Parcelable { mIsDeferredConfig = false; mIsShared = false; mPhysicalCameraId = null; + mIsMultiResolution = false; + } + + /** + * Create a list of {@link OutputConfiguration} instances for the outputs used by a + * {@link android.hardware.camera2.MultiResolutionImageReader}. + * + * <p>This constructor takes an argument for a + * {@link android.hardware.camera2.MultiResolutionImageReader}.</p> + * + * @param multiResolutionImageReader + * The multi-resolution image reader object. + */ + public static @NonNull Collection<OutputConfiguration> createInstancesForMultiResolutionOutput( + @NonNull MultiResolutionImageReader multiResolutionImageReader) { + checkNotNull(multiResolutionImageReader, "Multi-resolution image reader must not be null"); + + int groupId = MULTI_RESOLUTION_GROUP_ID_COUNTER; + MULTI_RESOLUTION_GROUP_ID_COUNTER++; + // Skip in case the group id counter overflows to -1, the invalid value. + if (MULTI_RESOLUTION_GROUP_ID_COUNTER == -1) { + MULTI_RESOLUTION_GROUP_ID_COUNTER++; + } + + ImageReader[] imageReaders = multiResolutionImageReader.getReaders(); + ArrayList<OutputConfiguration> configs = new ArrayList<OutputConfiguration>(); + for (int i = 0; i < imageReaders.length; i++) { + MultiResolutionStreamInfo streamInfo = + multiResolutionImageReader.getStreamInfoForImageReader(imageReaders[i]); + + OutputConfiguration config = new OutputConfiguration( + groupId, imageReaders[i].getSurface()); + config.setPhysicalCameraId(streamInfo.getPhysicalCameraId()); + config.setMultiResolutionOutput(); + configs.add(config); + // TODO: Set sensor pixel mode for ultra high resolution sensor camera. + } + + return configs; } /** @@ -319,6 +398,7 @@ public final class OutputConfiguration implements Parcelable { mIsDeferredConfig = true; mIsShared = false; mPhysicalCameraId = null; + mIsMultiResolution = false; } /** @@ -355,8 +435,18 @@ public final class OutputConfiguration implements Parcelable { * <p>Up to {@link #getMaxSharedSurfaceCount} surfaces can be shared for an OutputConfiguration. * The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView, * MediaRecorder, MediaCodec, or implementation defined ImageReader.</p> + * + * <p>This function must not be called from OuptutConfigurations created by {@link + * #createInstancesForMultiResolutionOutput}.</p> + * + * @throws IllegalStateException If this OutputConfiguration is created via {@link + * #createInstancesForMultiResolutionOutput} to back a MultiResolutionImageReader. */ public void enableSurfaceSharing() { + if (mIsMultiResolution) { + throw new IllegalStateException("Cannot enable surface sharing on " + + "multi-resolution output configurations"); + } mIsShared = true; } @@ -368,8 +458,7 @@ public final class OutputConfiguration implements Parcelable { * This call achieves it by mapping the OutputConfiguration to the physical camera id.</p> * * <p>The valid physical camera ids can be queried by {@link - * android.hardware.camera2.CameraCharacteristics#getPhysicalCameraIds}. - * </p> + * CameraCharacteristics#getPhysicalCameraIds}.</p> * * <p>Passing in a null physicalCameraId means that the OutputConfiguration is for a logical * stream.</p> @@ -380,8 +469,16 @@ public final class OutputConfiguration implements Parcelable { * after {@link CameraDevice#createCaptureSessionByOutputConfigurations} or {@link * CameraDevice#createReprocessableCaptureSessionByConfigurations} has no effect.</p> * - * <p>The surface belonging to a physical camera OutputConfiguration must not be used as input - * or output of a reprocessing request. </p> + * <p>As of {@link android.os.Build.VERSION_CODES#S Android 12}, an image buffer from a + * physical camera stream can be used for reprocessing to logical camera streams and streams + * from the same physical camera if the camera device supports multi-resolution input and output + * streams. See {@link CameraCharacteristics#SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP} + * for details. The behaviors of reprocessing from a non-physical camera stream to a physical + * camera stream, and from a physical camera stream to a physical camera stream of different + * physical camera, are device-specific and not guaranteed to be supported.</p> + * + * <p>On prior API levels, the surface belonging to a physical camera OutputConfiguration must + * not be used as input or output of a reprocessing request. </p> */ public void setPhysicalCameraId(@Nullable String physicalCameraId) { mPhysicalCameraId = physicalCameraId; @@ -527,6 +624,7 @@ public final class OutputConfiguration implements Parcelable { this.mIsDeferredConfig = other.mIsDeferredConfig; this.mIsShared = other.mIsShared; this.mPhysicalCameraId = other.mPhysicalCameraId; + this.mIsMultiResolution = other.mIsMultiResolution; } /** @@ -543,6 +641,7 @@ public final class OutputConfiguration implements Parcelable { ArrayList<Surface> surfaces = new ArrayList<Surface>(); source.readTypedList(surfaces, Surface.CREATOR); String physicalCameraId = source.readString(); + boolean isMultiResolutionOutput = source.readInt() == 1; checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant"); @@ -566,6 +665,7 @@ public final class OutputConfiguration implements Parcelable { mConfiguredGenerationId = 0; } mPhysicalCameraId = physicalCameraId; + mIsMultiResolution = isMultiResolutionOutput; } /** @@ -665,6 +765,7 @@ public final class OutputConfiguration implements Parcelable { dest.writeInt(mIsShared ? 1 : 0); dest.writeTypedList(mSurfaces); dest.writeString(mPhysicalCameraId); + dest.writeInt(mIsMultiResolution ? 1 : 0); } /** @@ -694,7 +795,8 @@ public final class OutputConfiguration implements Parcelable { mConfiguredFormat != other.mConfiguredFormat || mConfiguredDataspace != other.mConfiguredDataspace || mConfiguredGenerationId != other.mConfiguredGenerationId || - !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId)) + !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) || + mIsMultiResolution != other.mIsMultiResolution) return false; int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size()); @@ -720,17 +822,24 @@ public final class OutputConfiguration implements Parcelable { return HashCodeHelpers.hashCode( mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0, - mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode()); + mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(), + mIsMultiResolution ? 1 : 0); } return HashCodeHelpers.hashCode( mRotation, mSurfaces.hashCode(), mConfiguredGenerationId, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0, - mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode()); + mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(), + mIsMultiResolution ? 1 : 0); } private static final String TAG = "OutputConfiguration"; + + // A surfaceGroupId counter used for MultiResolutionImageReader. Its value is + // incremented everytime {@link createInstancesForMultiResolutionOutput} is called. + private static int MULTI_RESOLUTION_GROUP_ID_COUNTER = 0; + private ArrayList<Surface> mSurfaces; private final int mRotation; private final int mSurfaceGroupId; @@ -749,4 +858,7 @@ public final class OutputConfiguration implements Parcelable { private boolean mIsShared; // The physical camera id that this output configuration is for. private String mPhysicalCameraId; + // Flag indicating if this config is for a multi-resolution output with a + // MultiResolutionImageReader + private boolean mIsMultiResolution; } diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java index 8fc919f142a2..ea6b92d4f33e 100644 --- a/core/java/android/hardware/camera2/params/SessionConfiguration.java +++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java @@ -130,11 +130,13 @@ public final class SessionConfiguration implements Parcelable { int inputWidth = source.readInt(); int inputHeight = source.readInt(); int inputFormat = source.readInt(); + boolean isInputMultiResolution = source.readBoolean(); ArrayList<OutputConfiguration> outConfigs = new ArrayList<OutputConfiguration>(); source.readTypedList(outConfigs, OutputConfiguration.CREATOR); if ((inputWidth > 0) && (inputHeight > 0) && (inputFormat != -1)) { - mInputConfig = new InputConfiguration(inputWidth, inputHeight, inputFormat); + mInputConfig = new InputConfiguration(inputWidth, inputHeight, + inputFormat, isInputMultiResolution); } mSessionType = sessionType; mOutputConfigurations = outConfigs; @@ -169,10 +171,12 @@ public final class SessionConfiguration implements Parcelable { dest.writeInt(mInputConfig.getWidth()); dest.writeInt(mInputConfig.getHeight()); dest.writeInt(mInputConfig.getFormat()); + dest.writeBoolean(mInputConfig.isMultiResolution()); } else { dest.writeInt(/*inputWidth*/ 0); dest.writeInt(/*inputHeight*/ 0); dest.writeInt(/*inputFormat*/ -1); + dest.writeBoolean(/*isMultiResolution*/ false); } dest.writeTypedList(mOutputConfigurations); } diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index 10a814acd70b..a25ae6041d77 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -1575,7 +1575,7 @@ public final class StreamConfigurationMap { return sizes; } - /** Get the list of publically visible output formats; does not include IMPL_DEFINED */ + /** Get the list of publicly visible output formats */ private int[] getPublicFormats(boolean output) { int[] formats = new int[getPublicFormatCount(output)]; @@ -1746,6 +1746,21 @@ public final class StreamConfigurationMap { return sb.toString(); } + /** + * Size comparison method used by size comparators. + * + * @hide + */ + public static int compareSizes(int widthA, int heightA, int widthB, int heightB) { + long left = widthA * (long) heightA; + long right = widthB * (long) heightB; + if (left == right) { + left = widthA; + right = widthB; + } + return (left < right) ? -1 : (left > right ? 1 : 0); + } + private void appendOutputsString(StringBuilder sb) { sb.append("Outputs("); int[] formats = getOutputFormats(); @@ -1843,7 +1858,10 @@ public final class StreamConfigurationMap { sb.append(")"); } - private String formatToString(int format) { + /** + * @hide + */ + public static String formatToString(int format) { switch (format) { case ImageFormat.YV12: return "YV12"; diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java index 2626a461aaf5..6fb0eb5df59a 100644 --- a/core/java/android/hardware/lights/LightsRequest.java +++ b/core/java/android/hardware/lights/LightsRequest.java @@ -17,6 +17,7 @@ package android.hardware.lights; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.util.SparseArray; import com.android.internal.util.Preconditions; @@ -94,6 +95,20 @@ public final class LightsRequest { } /** + * Overrides the color and intensity of a given light. + * + * @param light the light to modify + * @param state the desired color and intensity of the light * + * @deprecated Use {@link #addLight(Light, LightState)} instead. + * @hide + */ + @SystemApi + @Deprecated + public @NonNull Builder setLight(@NonNull Light light, @NonNull LightState state) { + return addLight(light, state); + } + + /** * Removes the override for the color and intensity of a given light. * * @param light the light to modify diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index b016ed67c4d9..9bf791ba33e0 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -19,8 +19,6 @@ package android.net; import android.net.INetworkPolicyListener; import android.net.Network; import android.net.NetworkPolicy; -import android.net.NetworkQuotaInfo; -import android.net.NetworkState; import android.net.NetworkTemplate; import android.telephony.SubscriptionPlan; @@ -70,9 +68,6 @@ interface INetworkPolicyManager { int getMultipathPreference(in Network network); - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - NetworkQuotaInfo getNetworkQuotaInfo(in NetworkState state); - SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage); void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage); String getSubscriptionPlansOwner(int subId); diff --git a/core/java/android/net/NetworkStateSnapshot.java b/core/java/android/net/NetworkStateSnapshot.java index b3d8d4e614da..0d26c2de8698 100644 --- a/core/java/android/net/NetworkStateSnapshot.java +++ b/core/java/android/net/NetworkStateSnapshot.java @@ -24,6 +24,8 @@ import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import com.android.net.module.util.NetworkIdentityUtils; + import java.util.Objects; /** @@ -124,4 +126,15 @@ public final class NetworkStateSnapshot implements Parcelable { public int hashCode() { return Objects.hash(network, networkCapabilities, linkProperties, subscriberId, legacyType); } + + @Override + public String toString() { + return "NetworkStateSnapshot{" + + "network=" + network + + ", networkCapabilities=" + networkCapabilities + + ", linkProperties=" + linkProperties + + ", subscriberId='" + NetworkIdentityUtils.scrubSubscriberId(subscriberId) + '\'' + + ", legacyType=" + legacyType + + '}'; + } } diff --git a/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl index f8ae492016f0..62de8216ce54 100644 --- a/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl +++ b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl @@ -17,6 +17,6 @@ package android.net.vcn; /** @hide */ -interface IVcnUnderlyingNetworkPolicyListener { +oneway interface IVcnUnderlyingNetworkPolicyListener { void onPolicyChanged(); }
\ No newline at end of file diff --git a/core/java/android/os/BytesMatcher.java b/core/java/android/os/BytesMatcher.java new file mode 100644 index 000000000000..8537f47eb575 --- /dev/null +++ b/core/java/android/os/BytesMatcher.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.bluetooth.BluetoothUuid; +import android.net.MacAddress; +import android.util.Log; + +import com.android.internal.util.HexDump; + +import java.util.ArrayList; +import java.util.function.Predicate; + +/** + * Predicate that tests if a given {@code byte[]} value matches a set of + * configured rules. + * <p> + * Rules are tested in the order in which they were originally added, which + * means a narrow rule can reject a specific value before a later broader rule + * might accept that same value, or vice versa. + * <p> + * Matchers can contain rules of varying lengths, and tested values will only be + * matched against rules of the exact same length. This is designed to support + * {@link BluetoothUuid} style values which can be variable length. + * + * @hide + */ +public class BytesMatcher implements Predicate<byte[]> { + private static final String TAG = "BytesMatcher"; + + private static final char TYPE_ACCEPT = '+'; + private static final char TYPE_REJECT = '-'; + + private final ArrayList<Rule> mRules = new ArrayList<>(); + + private static class Rule { + public final char type; + public final @NonNull byte[] value; + public final @Nullable byte[] mask; + + public Rule(char type, @NonNull byte[] value, @Nullable byte[] mask) { + if (mask != null && value.length != mask.length) { + throw new IllegalArgumentException( + "Expected length " + value.length + " but found " + mask.length); + } + this.type = type; + this.value = value; + this.mask = mask; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + encode(builder); + return builder.toString(); + } + + public void encode(@NonNull StringBuilder builder) { + builder.append(type); + builder.append(HexDump.toHexString(value)); + if (mask != null) { + builder.append('/'); + builder.append(HexDump.toHexString(mask)); + } + } + + public boolean test(@NonNull byte[] value) { + if (value.length != this.value.length) { + return false; + } + for (int i = 0; i < this.value.length; i++) { + byte local = this.value[i]; + byte remote = value[i]; + if (this.mask != null) { + local &= this.mask[i]; + remote &= this.mask[i]; + } + if (local != remote) { + return false; + } + } + return true; + } + } + + /** + * Add a rule that will result in {@link #test(byte[])} returning + * {@code true} when a value being tested matches it. + * <p> + * Rules are tested in the order in which they were originally added, which + * means a narrow rule can reject a specific value before a later broader + * rule might accept that same value, or vice versa. + * + * @param value to be matched + * @param mask to be applied to both values before testing for equality; if + * {@code null} then both values must match exactly + */ + public void addAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) { + mRules.add(new Rule(TYPE_ACCEPT, value, mask)); + } + + /** + * Add a rule that will result in {@link #test(byte[])} returning + * {@code false} when a value being tested matches it. + * <p> + * Rules are tested in the order in which they were originally added, which + * means a narrow rule can reject a specific value before a later broader + * rule might accept that same value, or vice versa. + * + * @param value to be matched + * @param mask to be applied to both values before testing for equality; if + * {@code null} then both values must match exactly + */ + public void addRejectRule(@NonNull byte[] value, @Nullable byte[] mask) { + mRules.add(new Rule(TYPE_REJECT, value, mask)); + } + + /** + * Test if the given {@code ParcelUuid} value matches the set of rules + * configured in this matcher. + */ + public boolean testBluetoothUuid(@NonNull ParcelUuid value) { + return test(BluetoothUuid.uuidToBytes(value)); + } + + /** + * Test if the given {@code MacAddress} value matches the set of rules + * configured in this matcher. + */ + public boolean testMacAddress(@NonNull MacAddress value) { + return test(value.toByteArray()); + } + + /** + * Test if the given {@code byte[]} value matches the set of rules + * configured in this matcher. + */ + @Override + public boolean test(@NonNull byte[] value) { + return test(value, false); + } + + /** + * Test if the given {@code byte[]} value matches the set of rules + * configured in this matcher. + */ + public boolean test(@NonNull byte[] value, boolean defaultValue) { + final int size = mRules.size(); + for (int i = 0; i < size; i++) { + final Rule rule = mRules.get(i); + if (rule.test(value)) { + return (rule.type == TYPE_ACCEPT); + } + } + return defaultValue; + } + + /** + * Encode the given matcher into a human-readable {@link String} which can + * be used to transport matchers across device boundaries. + * <p> + * The human-readable format is an ordered list separated by commas, where + * each rule is a {@code +} or {@code -} symbol indicating if the match + * should be accepted or rejected, then followed by a hex value and an + * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid + * encoded matcher. + * + * @see #decode(String) + */ + public static @NonNull String encode(@NonNull BytesMatcher matcher) { + final StringBuilder builder = new StringBuilder(); + final int size = matcher.mRules.size(); + for (int i = 0; i < size; i++) { + final Rule rule = matcher.mRules.get(i); + rule.encode(builder); + builder.append(','); + } + builder.deleteCharAt(builder.length() - 1); + return builder.toString(); + } + + /** + * Decode the given human-readable {@link String} used to transport matchers + * across device boundaries. + * <p> + * The human-readable format is an ordered list separated by commas, where + * each rule is a {@code +} or {@code -} symbol indicating if the match + * should be accepted or rejected, then followed by a hex value and an + * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid + * encoded matcher. + * + * @see #encode(BytesMatcher) + */ + public static @NonNull BytesMatcher decode(@NonNull String value) { + final BytesMatcher matcher = new BytesMatcher(); + final int length = value.length(); + for (int i = 0; i < length;) { + final char type = value.charAt(i); + + int nextRule = value.indexOf(',', i); + int nextMask = value.indexOf('/', i); + + if (nextRule == -1) nextRule = length; + if (nextMask > nextRule) nextMask = -1; + + final byte[] ruleValue; + final byte[] ruleMask; + if (nextMask >= 0) { + ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextMask)); + ruleMask = HexDump.hexStringToByteArray(value.substring(nextMask + 1, nextRule)); + } else { + ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextRule)); + ruleMask = null; + } + + switch (type) { + case TYPE_ACCEPT: + matcher.addAcceptRule(ruleValue, ruleMask); + break; + case TYPE_REJECT: + matcher.addRejectRule(ruleValue, ruleMask); + break; + default: + Log.w(TAG, "Ignoring unknown type " + type); + break; + } + + i = nextRule + 1; + } + return matcher; + } +} diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 8f6161329e53..ae7d94cbad79 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -42,8 +42,7 @@ interface IPowerManager void updateWakeLockWorkSource(IBinder lock, in WorkSource ws, String historyTag); boolean isWakeLockLevelSupported(int level); - @UnsupportedAppUsage - void userActivity(long time, int event, int flags); + void userActivity(int displayId, long time, int event, int flags); void wakeUp(long time, int reason, String details, String opPackageName); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void goToSleep(long time, int reason, int flags); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index e5163d83de69..786a7d08047e 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1277,7 +1277,7 @@ public final class PowerManager { }) public void userActivity(long when, int event, int flags) { try { - mService.userActivity(when, event, flags); + mService.userActivity(mContext.getDisplayId(), when, event, flags); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 87dced8a3437..dc6f63a94685 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -58,6 +58,8 @@ public final class IncrementalManager { private static final String ALLOWED_PROPERTY = "incremental.allowed"; + public static final int MIN_VERSION_TO_SUPPORT_FSVERITY = 2; + public static final int CREATE_MODE_TEMPORARY_BIND = IIncrementalService.CREATE_MODE_TEMPORARY_BIND; public static final int CREATE_MODE_PERMANENT_BIND = diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java index fbc25a6aaf74..4c8ee598f512 100644 --- a/core/java/android/service/autofill/AutofillServiceInfo.java +++ b/core/java/android/service/autofill/AutofillServiceInfo.java @@ -18,10 +18,13 @@ package android.service.autofill; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.AppGlobals; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; @@ -44,6 +47,8 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; /** * {@link ServiceInfo} and meta-data about an {@link AutofillService}. @@ -76,6 +81,8 @@ public final class AutofillServiceInfo { @Nullable private final String mSettingsActivity; + @Nullable + private final String mPasswordsActivity; @Nullable private final ArrayMap<String, Long> mCompatibilityPackages; @@ -113,12 +120,14 @@ public final class AutofillServiceInfo { AutofillService.SERVICE_META_DATA); if (parser == null) { mSettingsActivity = null; + mPasswordsActivity = null; mCompatibilityPackages = null; mInlineSuggestionsEnabled = false; return; } String settingsActivity = null; + String passwordsActivity = null; ArrayMap<String, Long> compatibilityPackages = null; boolean inlineSuggestionsEnabled = false; // false by default. @@ -139,6 +148,8 @@ public final class AutofillServiceInfo { com.android.internal.R.styleable.AutofillService); settingsActivity = afsAttributes.getString( R.styleable.AutofillService_settingsActivity); + passwordsActivity = afsAttributes.getString( + R.styleable.AutofillService_passwordsActivity); inlineSuggestionsEnabled = afsAttributes.getBoolean( R.styleable.AutofillService_supportsInlineSuggestions, false); } finally { @@ -155,6 +166,7 @@ public final class AutofillServiceInfo { } mSettingsActivity = settingsActivity; + mPasswordsActivity = passwordsActivity; mCompatibilityPackages = compatibilityPackages; mInlineSuggestionsEnabled = inlineSuggestionsEnabled; } @@ -221,6 +233,7 @@ public final class AutofillServiceInfo { return compatibilityPackages; } + @NonNull public ServiceInfo getServiceInfo() { return mServiceInfo; } @@ -230,6 +243,12 @@ public final class AutofillServiceInfo { return mSettingsActivity; } + @Nullable + public String getPasswordsActivity() { + return mPasswordsActivity; + } + + @Nullable public ArrayMap<String, Long> getCompatibilityPackages() { return mCompatibilityPackages; } @@ -238,12 +257,37 @@ public final class AutofillServiceInfo { return mInlineSuggestionsEnabled; } + /** + * Queries the valid autofill services available for the user. + */ + public static List<AutofillServiceInfo> getAvailableServices( + Context context, @UserIdInt int user) { + final List<AutofillServiceInfo> services = new ArrayList<>(); + + final List<ResolveInfo> resolveInfos = + context.getPackageManager().queryIntentServicesAsUser( + new Intent(AutofillService.SERVICE_INTERFACE), + PackageManager.GET_META_DATA, + user); + for (ResolveInfo resolveInfo : resolveInfos) { + final ServiceInfo serviceInfo = resolveInfo.serviceInfo; + try { + services.add(new AutofillServiceInfo(context, serviceInfo)); + } catch (SecurityException e) { + // Service does not declare the proper permission, ignore it. + Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e); + } + } + return services; + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append(getClass().getSimpleName()); builder.append("[").append(mServiceInfo); builder.append(", settings:").append(mSettingsActivity); + builder.append(", passwords activity:").append(mPasswordsActivity); builder.append(", hasCompatPckgs:").append(mCompatibilityPackages != null && !mCompatibilityPackages.isEmpty()).append("]"); builder.append(", inline suggestions enabled:").append(mInlineSuggestionsEnabled); @@ -256,6 +300,7 @@ public final class AutofillServiceInfo { public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("Component: "); pw.println(getServiceInfo().getComponentName()); pw.print(prefix); pw.print("Settings: "); pw.println(mSettingsActivity); + pw.print(prefix); pw.print("Passwords activity: "); pw.println(mPasswordsActivity); pw.print(prefix); pw.print("Compat packages: "); pw.println(mCompatibilityPackages); pw.print(prefix); pw.print("Inline Suggestions Enabled: "); pw.println(mInlineSuggestionsEnabled); diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index e7ceada6180a..e37921ec03cc 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -17,10 +17,7 @@ package android.telephony; import android.Manifest; -import android.annotation.CallbackExecutor; -import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.compat.annotation.ChangeId; @@ -31,16 +28,12 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.Looper; import android.telephony.Annotation.CallState; -import android.telephony.Annotation.DataActivityType; import android.telephony.Annotation.DisconnectCauses; -import android.telephony.Annotation.NetworkType; import android.telephony.Annotation.PreciseDisconnectCauses; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SimActivationState; import android.telephony.Annotation.SrvccState; -import android.telephony.NetworkRegistrationInfo.Domain; import android.telephony.TelephonyManager.DataEnabledReason; -import android.telephony.TelephonyManager.DataState; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; @@ -49,8 +42,6 @@ import com.android.internal.telephony.IPhoneStateListener; import dalvik.system.VMRuntime; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; @@ -71,49 +62,15 @@ import java.util.concurrent.Executor; * information unless it has the appropriate permissions declared in * its manifest file. Where permissions apply, they are noted in the * appropriate LISTEN_ flags. + * + * @deprecated Use {@link TelephonyCallback} instead. */ +@Deprecated public class PhoneStateListener { private static final String LOG_TAG = "PhoneStateListener"; private static final boolean DBG = false; // STOPSHIP if true /** - * Experiment flag to set the per-pid registration limit for PhoneStateListeners - * - * Limit on registrations of {@link PhoneStateListener}s on a per-pid - * basis. When this limit is exceeded, any calls to {@link TelephonyManager#listen} will fail - * with an {@link IllegalStateException}. - * - * {@link android.os.Process#PHONE_UID}, {@link android.os.Process#SYSTEM_UID}, and the uid that - * TelephonyRegistry runs under are exempt from this limit. - * - * If the value of the flag is less than 1, enforcement of the limit will be disabled. - * @hide - */ - public static final String FLAG_PER_PID_REGISTRATION_LIMIT = - "phone_state_listener_per_pid_registration_limit"; - - /** - * Default value for the per-pid registation limit. - * See {@link #FLAG_PER_PID_REGISTRATION_LIMIT}. - * @hide - */ - public static final int DEFAULT_PER_PID_REGISTRATION_LIMIT = 50; - - /** - * This change enables a limit on the number of {@link PhoneStateListener} objects any process - * may register via {@link TelephonyManager#listen}. The default limit is 50, which may change - * via remote device config updates. - * - * This limit is enforced via an {@link IllegalStateException} thrown from - * {@link TelephonyManager#listen} when the offending process attempts to register one too many - * listeners. - * - * @hide - */ - @ChangeId - public static final long PHONE_STATE_LISTENER_LIMIT_CHANGE_ID = 150880553L; - - /** * Stop listening for updates. * * The PhoneStateListener is not tied to any subscription and unregistered for any update. @@ -125,7 +82,7 @@ public class PhoneStateListener { * * @see #onServiceStateChanged * @see ServiceState - * @deprecated Use {@link ServiceStateChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.ServiceStateListener} instead. */ @Deprecated public static final int LISTEN_SERVICE_STATE = 0x00000001; @@ -135,7 +92,7 @@ public class PhoneStateListener { * {@more} * * @see #onSignalStrengthChanged - * @deprecated Use {@link SignalStrengthsChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead. */ @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 0x00000002; @@ -151,7 +108,7 @@ public class PhoneStateListener { * voicemail icon. * * @see #onMessageWaitingIndicatorChanged - * @deprecated Use {@link MessageWaitingIndicatorChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead. */ @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 0x00000004; @@ -164,7 +121,7 @@ public class PhoneStateListener { * {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onCallForwardingIndicatorChanged - * @deprecated Use {@link CallForwardingIndicatorChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead. */ @Deprecated public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008; @@ -182,7 +139,7 @@ public class PhoneStateListener { * instead. * * @see #onCellLocationChanged - * @deprecated Use {@link CellLocationChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead. */ @Deprecated public static final int LISTEN_CELL_LOCATION = 0x00000010; @@ -192,7 +149,7 @@ public class PhoneStateListener { * {@more} * * @see #onCallStateChanged - * @deprecated Use {@link CallStateChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.CallStateListener} instead. */ @Deprecated public static final int LISTEN_CALL_STATE = 0x00000020; @@ -201,7 +158,7 @@ public class PhoneStateListener { * Listen for changes to the data connection state (cellular). * * @see #onDataConnectionStateChanged - * @deprecated Use {@link DataConnectionStateChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead. */ @Deprecated public static final int LISTEN_DATA_CONNECTION_STATE = 0x00000040; @@ -214,7 +171,7 @@ public class PhoneStateListener { * data-traffic icon. * * @see #onDataActivity - * @deprecated Use {@link DataActivityListener} instead. + * @deprecated Use {@link TelephonyCallback.DataActivityListener} instead. */ @Deprecated public static final int LISTEN_DATA_ACTIVITY = 0x00000080; @@ -226,7 +183,7 @@ public class PhoneStateListener { * icon. * * @see #onSignalStrengthsChanged - * @deprecated Use {@link SignalStrengthsChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead. */ @Deprecated public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100; @@ -238,7 +195,8 @@ public class PhoneStateListener { * @see #onSignalStrengthsChanged * * @hide - * @deprecated Use {@link AlwaysReportedSignalStrengthChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.AlwaysReportedSignalStrengthListener} + * instead. */ @Deprecated @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) @@ -251,7 +209,7 @@ public class PhoneStateListener { * permission. * * @see #onCellInfoChanged - * @deprecated Use {@link CellInfoChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead. */ @Deprecated public static final int LISTEN_CELL_INFO = 0x00000400; @@ -265,7 +223,7 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @hide - * @deprecated Use {@link PreciseCallStateChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.PreciseCallStateListener} instead. */ @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @@ -280,7 +238,7 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onPreciseDataConnectionStateChanged - * @deprecated Use {@link PreciseDataConnectionStateChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.PreciseDataConnectionStateListener} instead. */ @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @@ -306,7 +264,7 @@ public class PhoneStateListener { * * @see #onServiceStateChanged(ServiceState) * @hide - * @deprecated Use {@link SrvccStateChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.SrvccStateListener} instead. */ @Deprecated @SystemApi @@ -328,7 +286,7 @@ public class PhoneStateListener { * * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean) * @hide - * @deprecated Use {@link CarrierNetworkChangeListener} instead. + * @deprecated Use {@link TelephonyCallback.CarrierNetworkListener} instead. */ @Deprecated public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000; @@ -349,7 +307,7 @@ public class PhoneStateListener { * * @see #onVoiceActivationStateChanged * @hide - * @deprecated Use {@link VoiceActivationStateChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.VoiceActivationStateListener} instead. */ @Deprecated @SystemApi @@ -369,7 +327,7 @@ public class PhoneStateListener { * * @see #onDataActivationStateChanged * @hide - * @deprecated Use {@link DataActivationStateChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.DataActivationStateListener} instead. */ @Deprecated public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000; @@ -378,7 +336,7 @@ public class PhoneStateListener { * Listen for changes to the user mobile data state * * @see #onUserMobileDataStateChanged - * @deprecated Use {@link UserMobileDataStateChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.UserMobileDataStateListener} instead. */ @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000; @@ -391,7 +349,7 @@ public class PhoneStateListener { * {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onDisplayInfoChanged - * @deprecated Use {@link DisplayInfoChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead. */ @Deprecated public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000; @@ -401,7 +359,7 @@ public class PhoneStateListener { * * @see #onPhoneCapabilityChanged * @hide - * @deprecated Use {@link PhoneCapabilityChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.PhoneCapabilityListener} instead. */ @Deprecated public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000; @@ -413,7 +371,7 @@ public class PhoneStateListener { * subscription user selected as default data subscription in DSDS mode. * * @see #onActiveDataSubscriptionIdChanged - * @deprecated Use {@link ActiveDataSubscriptionIdChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead. */ @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000; @@ -423,7 +381,7 @@ public class PhoneStateListener { * * @see #onRadioPowerStateChanged * @hide - * @deprecated Use {@link RadioPowerStateChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.RadioPowerStateListener} instead. */ @Deprecated @SystemApi @@ -436,7 +394,7 @@ public class PhoneStateListener { * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). * - * @deprecated Use {@link EmergencyNumberListChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead. */ @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000; @@ -449,7 +407,7 @@ public class PhoneStateListener { * or the calling app has carrier privileges * (see {@link TelephonyManager#hasCarrierPrivileges}). * - * @deprecated Use {@link CallDisconnectCauseChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.CallDisconnectCauseListener} instead. */ @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @@ -464,7 +422,7 @@ public class PhoneStateListener { * * @see #onCallAttributesChanged * @hide - * @deprecated Use {@link CallAttributesChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.CallAttributesListener} instead. */ @Deprecated @SystemApi @@ -480,7 +438,7 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo) - * @deprecated Use {@link ImsCallDisconnectCauseChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.ImsCallDisconnectCauseListener} instead. */ @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @@ -491,7 +449,7 @@ public class PhoneStateListener { * * @see #onOutgoingEmergencyCall * @hide - * @deprecated Use {@link OutgoingEmergencyCallListener} instead. + * @deprecated Use {@link TelephonyCallback.OutgoingEmergencyCallListener} instead. */ @Deprecated @SystemApi @@ -503,7 +461,7 @@ public class PhoneStateListener { * * @see #onOutgoingEmergencySms * @hide - * @deprecated Use {@link OutgoingEmergencySmsListener} instead. + * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead. */ @Deprecated @SystemApi @@ -524,7 +482,7 @@ public class PhoneStateListener { * of whether the calling app has carrier privileges. * * @see #onRegistrationFailed - * @deprecated Use {@link RegistrationFailedListener} instead. + * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead. */ @Deprecated @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) @@ -540,540 +498,12 @@ public class PhoneStateListener { * of whether the calling app has carrier privileges. * * @see #onBarringInfoChanged - * @deprecated Use {@link BarringInfoChangedListener} instead. + * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead. */ @Deprecated @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = 0x80000000; - /** - * Event for changes to the network service state (cellular). - * - * @see ServiceStateChangedListener#onServiceStateChanged - * @see ServiceState - * - * @hide - */ - @SystemApi - public static final int EVENT_SERVICE_STATE_CHANGED = 1; - - /** - * Event for changes to the network signal strength (cellular). - * - * @see SignalStrengthsChangedListener#onSignalStrengthsChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; - - /** - * Event for changes to the message-waiting indicator. - * - * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that - * the calling app has carrier privileges (see - * {@link TelephonyManager#hasCarrierPrivileges}). - * <p> - * Example: The status bar uses this to determine when to display the - * voicemail icon. - * - * @see MessageWaitingIndicatorChangedListener#onMessageWaitingIndicatorChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; - - /** - * Event for changes to the call-forwarding indicator. - * - * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that - * the calling app has carrier privileges (see - * {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see CallForwardingIndicatorChangedListener#onCallForwardingIndicatorChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; - - /** - * Event for changes to the device's cell location. Note that - * this will result in frequent callbacks to the listener. - * - * If you need regular location updates but want more control over - * the update interval or location precision, you can set up a listener - * through the {@link android.location.LocationManager location manager} - * instead. - * - * @see CellLocationChangedListener#onCellLocationChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) - public static final int EVENT_CELL_LOCATION_CHANGED = 5; - - /** - * Event for changes to the device call state. - * - * @see CallStateChangedListener#onCallStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) - public static final int EVENT_CALL_STATE_CHANGED = 6; - - /** - * Event for changes to the data connection state (cellular). - * - * @see DataConnectionStateChangedListener#onDataConnectionStateChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; - - /** - * Event for changes to the direction of data traffic on the data - * connection (cellular). - * - * Example: The status bar uses this to display the appropriate - * data-traffic icon. - * - * @see DataActivityListener#onDataActivity - * - * @hide - */ - @SystemApi - public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; - - /** - * Event for changes to the network signal strengths (cellular). - * <p> - * Example: The status bar uses this to control the signal-strength - * icon. - * - * @see SignalStrengthsChangedListener#onSignalStrengthsChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; - - /** - * Event for changes of the network signal strengths (cellular) always reported from modem, - * even in some situations such as the screen of the device is off. - * - * @see AlwaysReportedSignalStrengthChangedListener#onSignalStrengthsChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) - public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; - - /** - * Event for changes to observed cell info. - * - * @see CellInfoChangedListener#onCellInfoChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) - public static final int EVENT_CELL_INFO_CHANGED = 11; - - /** - * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing, - * background and foreground calls. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see PreciseCallStateChangedListener#onPreciseCallStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; - - /** - * Event for {@link PreciseDataConnectionState} on the data connection (cellular). - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see PreciseDataConnectionStateChangedListener#onPreciseDataConnectionStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; - - /** - * Event for real time info for all data connections (cellular)). - * - * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo) - * - * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} - * @hide - */ - @Deprecated - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; - - /** - * Event for OEM hook raw event - * - * @see #onOemHookRawEvent - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public static final int EVENT_OEM_HOOK_RAW = 15; - - /** - * Event for changes to the SRVCC state of the active call. - * - * @see SrvccStateChangedListener#onSrvccStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public static final int EVENT_SRVCC_STATE_CHANGED = 16; - - /** - * Event for carrier network changes indicated by a carrier app. - * - * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean) - * @see CarrierNetworkChangeListener#onCarrierNetworkChange - * - * @hide - */ - @SystemApi - public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; - - /** - * Event for changes to the sim voice activation state - * - * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING - * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED - * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED - * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED - * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN - * - * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been - * fully activated - * - * @see VoiceActivationStateChangedListener#onVoiceActivationStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; - - /** - * Event for changes to the sim data activation state - * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING - * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED - * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED - * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED - * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN - * - * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been - * fully activated - * - * @see DataActivationStateChangedListener#onDataActivationStateChanged - * @hide - */ - @SystemApi - public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; - - /** - * Event for changes to the user mobile data state - * - * @see UserMobileDataStateChangedListener#onUserMobileDataStateChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; - - /** - * Event for display info changed event. - * - * @see DisplayInfoChangedListener#onDisplayInfoChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_DISPLAY_INFO_CHANGED = 21; - - /** - * Event for changes to the phone capability. - * - * @see PhoneCapabilityChangedListener#onPhoneCapabilityChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; - - /** - * Event for changes to active data subscription ID. Active data subscription is - * the current subscription used to setup Cellular Internet data. For example, - * it could be the current active opportunistic subscription in use, or the - * subscription user selected as default data subscription in DSDS mode. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling - * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see ActiveDataSubscriptionIdChangedListener#onActiveDataSubscriptionIdChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; - - /** - * Event for changes to the radio power state. - * - * @see RadioPowerStateChangedListener#onRadioPowerStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; - - /** - * Event for changes to emergency number list based on all active subscriptions. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling - * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see EmergencyNumberListChangedListener#onEmergencyNumberListChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; - - /** - * Event for call disconnect causes which contains {@link DisconnectCause} and - * {@link PreciseDisconnectCause}. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see CallDisconnectCauseChangedListener#onCallDisconnectCauseChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; - - /** - * Event for changes to the call attributes of a currently active call. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see CallAttributesChangedListener#onCallAttributesChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; - - /** - * Event for IMS call disconnect causes which contains - * {@link android.telephony.ims.ImsReasonInfo} - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see ImsCallDisconnectCauseChangedListener#onImsCallDisconnectCauseChanged(ImsReasonInfo) - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; - - /** - * Event for the emergency number placed from an outgoing call. - * - * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) - public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; - - /** - * Event for the emergency number placed from an outgoing SMS. - * - * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) - public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; - - /** - * Event for registration failures. - * - * Event for indications that a registration procedure has failed in either the CS or PS - * domain. This indication does not necessarily indicate a change of service state, which should - * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or - * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless - * of whether the calling app has carrier privileges. - * - * @see RegistrationFailedListener#onRegistrationFailed - * - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - Manifest.permission.READ_PRECISE_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION - }) - public static final int EVENT_REGISTRATION_FAILURE = 31; - - /** - * Event for Barring Information for the current registered / camped cell. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or - * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless - * of whether the calling app has carrier privileges. - * - * @see BarringInfoChangedListener#onBarringInfoChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - Manifest.permission.READ_PRECISE_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION - }) - public static final int EVENT_BARRING_INFO_CHANGED = 32; - - /** - * Event for changes to the physical channel configuration. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see PhysicalChannelConfigChangedListener#onPhysicalChannelConfigChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; - - - /** - * Event for changes to the data enabled. - * - * Event for indications that the enabled status of current data has changed. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see DataEnabledChangedListener#onDataEnabledChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_DATA_ENABLED_CHANGED = 34; - - /** - * Event for changes to allowed network list based on all active subscriptions. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling - * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @hide - * @see AllowedNetworkTypesChangedListener#onAllowedNetworkTypesChanged - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35; - - /** @hide */ - @IntDef(prefix = { "EVENT_" }, value = { - EVENT_SERVICE_STATE_CHANGED, - EVENT_SIGNAL_STRENGTH_CHANGED, - EVENT_MESSAGE_WAITING_INDICATOR_CHANGED, - EVENT_CALL_FORWARDING_INDICATOR_CHANGED, - EVENT_CELL_LOCATION_CHANGED, - EVENT_CALL_STATE_CHANGED, - EVENT_DATA_CONNECTION_STATE_CHANGED, - EVENT_DATA_ACTIVITY_CHANGED, - EVENT_SIGNAL_STRENGTHS_CHANGED, - EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED, - EVENT_CELL_INFO_CHANGED, - EVENT_PRECISE_CALL_STATE_CHANGED, - EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED, - EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED, - EVENT_OEM_HOOK_RAW, - EVENT_SRVCC_STATE_CHANGED, - EVENT_CARRIER_NETWORK_CHANGED, - EVENT_VOICE_ACTIVATION_STATE_CHANGED, - EVENT_DATA_ACTIVATION_STATE_CHANGED, - EVENT_USER_MOBILE_DATA_STATE_CHANGED, - EVENT_DISPLAY_INFO_CHANGED, - EVENT_PHONE_CAPABILITY_CHANGED, - EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED, - EVENT_RADIO_POWER_STATE_CHANGED, - EVENT_EMERGENCY_NUMBER_LIST_CHANGED, - EVENT_CALL_DISCONNECT_CAUSE_CHANGED, - EVENT_CALL_ATTRIBUTES_CHANGED, - EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED, - EVENT_OUTGOING_EMERGENCY_CALL, - EVENT_OUTGOING_EMERGENCY_SMS, - EVENT_REGISTRATION_FAILURE, - EVENT_BARRING_INFO_CHANGED, - EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, - EVENT_DATA_ENABLED_CHANGED, - EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TelephonyEvent {} - /* * Subscription used to listen to the phone state changes * @hide @@ -1085,19 +515,16 @@ public class PhoneStateListener { /** * @hide */ - //TODO: The maxTargetSdk should be S if the build time tool updates it. @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @UnsupportedAppUsage( maxTargetSdk = Build.VERSION_CODES.R, - publicAlternatives = "Use {@code TelephonyManager#registerPhoneStateListener(" + - "Executor, PhoneStateListener)} instead") - public IPhoneStateListener callback; + publicAlternatives = "Use {@code TelephonyManager#registerTelephonyCallback(" + + "Executor, TelephonyCallback)} instead") + public final IPhoneStateListener callback; /** * Create a PhoneStateListener for the Phone with the default subscription. - * If this is created for use with deprecated API - * {@link TelephonyManager#listen(PhoneStateListener, int)}, then this class requires - * Looper.myLooper() not return null. + * This class requires Looper.myLooper() not return null. */ public PhoneStateListener() { this(null, Looper.myLooper()); @@ -1135,10 +562,7 @@ public class PhoneStateListener { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public PhoneStateListener(Integer subId, Looper looper) { - if (looper != null) { - setExecutor(new HandlerExecutor(new Handler(looper))); - } - mSubId = subId; + this(subId, new HandlerExecutor(new Handler(looper))); if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) { throw new IllegalArgumentException("PhoneStateListener with subId: " @@ -1153,783 +577,18 @@ public class PhoneStateListener { * The Executor must not be null. * * @param executor a non-null Executor that will execute callbacks for the PhoneStateListener. - * @deprecated Use - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} instead. */ @Deprecated public PhoneStateListener(@NonNull Executor executor) { - setExecutor(executor); - mSubId = null; + this(null, executor); } - private @NonNull Executor mExecutor; - - /** - * @hide - */ - public void setExecutor(@NonNull @CallbackExecutor Executor executor) { - if (executor == null) { + private PhoneStateListener(Integer subId, Executor e) { + if (e == null) { throw new IllegalArgumentException("PhoneStateListener Executor must be non-null"); } - mExecutor = executor; - callback = new IPhoneStateListenerStub(this, mExecutor); - } - - /** - * @hide - */ - public boolean isExecutorSet() { - return mExecutor != null; - } - - /** - * Interface for service state listener. - */ - public interface ServiceStateChangedListener { - /** - * Callback invoked when device service state changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * The instance of {@link ServiceState} passed as an argument here will have various - * levels of location information stripped from it depending on the location permissions - * that your app holds. - * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will - * receive all the information in {@link ServiceState}. - * - * @see ServiceState#STATE_EMERGENCY_ONLY - * @see ServiceState#STATE_IN_SERVICE - * @see ServiceState#STATE_OUT_OF_SERVICE - * @see ServiceState#STATE_POWER_OFF - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onServiceStateChanged(@NonNull ServiceState serviceState); - } - - /** - * Interface for message waiting indicator listener. - */ - public interface MessageWaitingIndicatorChangedListener { - /** - * Callback invoked when the message-waiting indicator changes on the registered - * subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public void onMessageWaitingIndicatorChanged(boolean mwi); - } - - /** - * Interface for call-forwarding indicator listener. - */ - public interface CallForwardingIndicatorChangedListener { - /** - * Callback invoked when the call-forwarding indicator changes on the registered - * subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public void onCallForwardingIndicatorChanged(boolean cfi); - } - - /** - * Interface for device cell location listener. - */ - public interface CellLocationChangedListener { - /** - * Callback invoked when device cell location changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) - public void onCellLocationChanged(@NonNull CellLocation location); - } - - /** - * Interface for call state listener. - */ - public interface CallStateChangedListener { - /** - * Callback invoked when device call state changes. - * <p> - * Reports the state of Telephony (mobile) calls on the device for the registered s - * ubscription. - * <p> - * Note: the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * <p> - * Note: The state returned here may differ from that returned by - * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that - * calling {@link TelephonyManager#getCallState()} from within this callback may return a - * different state than the callback reports. - * - * @param state call state - * @param phoneNumber call phone number. If application does not have - * {@link android.Manifest.permission#READ_CALL_LOG} permission or carrier - * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be - * passed as an argument. - */ - @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) - public void onCallStateChanged(@CallState int state, @Nullable String phoneNumber); - } - - /** - * Interface for data connection state listener. - */ - public interface DataConnectionStateChangedListener { - /** - * Callback invoked when connection state changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @see TelephonyManager#DATA_DISCONNECTED - * @see TelephonyManager#DATA_CONNECTING - * @see TelephonyManager#DATA_CONNECTED - * @see TelephonyManager#DATA_SUSPENDED - * - * @param state is the current state of data connection. - * @param networkType is the current network type of data connection. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onDataConnectionStateChanged(@DataState int state, - @NetworkType int networkType); - } - - /** - * Interface for data activity state listener. - */ - public interface DataActivityListener { - /** - * Callback invoked when data activity state changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @see TelephonyManager#DATA_ACTIVITY_NONE - * @see TelephonyManager#DATA_ACTIVITY_IN - * @see TelephonyManager#DATA_ACTIVITY_OUT - * @see TelephonyManager#DATA_ACTIVITY_INOUT - * @see TelephonyManager#DATA_ACTIVITY_DORMANT - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onDataActivity(@DataActivityType int direction); - } - - /** - * Interface for network signal strengths listener. - */ - public interface SignalStrengthsChangedListener { - /** - * Callback invoked when network signal strengths changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength); - } - - /** - * Interface for network signal strengths listener which always reported from modem. - */ - public interface AlwaysReportedSignalStrengthChangedListener { - /** - * Callback always invoked from modem when network signal strengths changes on the - * registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) - public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength); - } - - /** - * Interface for cell info listener. - */ - public interface CellInfoChangedListener { - /** - * Callback invoked when a observed cell info has changed or new cells have been added - * or removed on the registered subscription. - * Note, the registration subscription ID s from {@link TelephonyManager} object - * which registersPhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param cellInfo is the list of currently visible cells. - */ - @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) - public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo); - } - - /** - * Interface for precise device call state listener. - * - * @hide - */ - @SystemApi - public interface PreciseCallStateChangedListener { - /** - * Callback invoked when precise device call state changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param callState {@link PreciseCallState} - */ - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onPreciseCallStateChanged(@NonNull PreciseCallState callState); - } - - /** - * Interface for call disconnect cause listener. - */ - public interface CallDisconnectCauseChangedListener { - /** - * Callback invoked when call disconnect cause changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param disconnectCause {@link DisconnectCause}. - * @param preciseDisconnectCause {@link PreciseDisconnectCause}. - */ - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause, - @PreciseDisconnectCauses int preciseDisconnectCause); - } - - /** - * Interface for IMS call disconnect cause listener. - */ - public interface ImsCallDisconnectCauseChangedListener { - /** - * Callback invoked when IMS call disconnect cause changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed. - * - */ - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo); - } - - /** - * Interface for precise data connection state listener. - */ - public interface PreciseDataConnectionStateChangedListener { - /** - * Callback providing update about the default/internet data connection on the registered - * subscription. - * - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @param dataConnectionState {@link PreciseDataConnectionState} - */ - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onPreciseDataConnectionStateChanged( - @NonNull PreciseDataConnectionState dataConnectionState); - } - - /** - * Interface for Single Radio Voice Call Continuity listener. - * - * @hide - */ - @SystemApi - public interface SrvccStateChangedListener { - /** - * Callback invoked when there has been a change in the Single Radio Voice Call Continuity - * (SRVCC) state for the currently active call on the registered subscription. - * - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void onSrvccStateChanged(@SrvccState int srvccState); - } - - /** - * Interface for SIM voice activation state listener. - * - * @hide - */ - @SystemApi - public interface VoiceActivationStateChangedListener { - /** - * Callback invoked when the SIM voice activation state has changed on the registered - * subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param state is the current SIM voice activation state - */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void onVoiceActivationStateChanged(@SimActivationState int state); - - } - - /** - * Interface for SIM data activation state listener. - */ - public interface DataActivationStateChangedListener { - /** - * Callback invoked when the SIM data activation state has changed on the registered - * subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param state is the current SIM data activation state - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onDataActivationStateChanged(@SimActivationState int state); - } - - /** - * Interface for user mobile data state listener. - */ - public interface UserMobileDataStateChangedListener { - /** - * Callback invoked when the user mobile data state has changed on the registered - * subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param enabled indicates whether the current user mobile data state is enabled or - * disabled. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onUserMobileDataStateChanged(boolean enabled); - } - - /** - * Interface for display info listener. - */ - public interface DisplayInfoChangedListener { - /** - * Callback invoked when the display info has changed on the registered subscription. - * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user - * based on carrier policy. - * - * @param telephonyDisplayInfo The display information. - */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo); - } - - /** - * Interface for the current emergency number list listener. - */ - public interface EmergencyNumberListChangedListener { - /** - * Callback invoked when the current emergency number list has changed on the registered - * subscription. - * - * Note, the registered subscription is associated with {@link TelephonyManager} object - * on which - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} - * was called. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * given subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param emergencyNumberList Map associating all active subscriptions on the device with - * the list of emergency numbers originating from that - * subscription. - * If there are no active subscriptions, the map will contain a - * single entry with - * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as - * the key and a list of emergency numbers as the value. If no - * emergency number information is available, the value will be - * empty. - */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public void onEmergencyNumberListChanged( - @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList); - } - - /** - * Interface for outgoing emergency call listener. - * - * @hide - */ - @SystemApi - public interface OutgoingEmergencyCallListener { - /** - * Callback invoked when an outgoing call is placed to an emergency number. - * - * This method will be called when an emergency call is placed on any subscription - * (including the no-SIM case), regardless of which subscription this listener was - * registered on. - * - * The default implementation of this method calls - * {@link #onOutgoingEmergencyCall(EmergencyNumber)} for backwards compatibility purposes. - * Do not call {@code super(...)} from within your implementation unless you want - * {@link #onOutgoingEmergencyCall(EmergencyNumber)} to be called as well. - * - * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was - * placed to. - * @param subscriptionId The subscription ID used to place the emergency call. If the - * emergency call was placed without a valid subscription - * (e.g. when there are no SIM cards in the device), this will be - * equal to {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. - */ - @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) - public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber, - int subscriptionId); - } - - /** - * Interface for outgoing emergency sms listener. - * - * @hide - */ - @SystemApi - public interface OutgoingEmergencySmsListener { - /** - * Smsback invoked when an outgoing sms is sent to an emergency number. - * - * This method will be called when an emergency sms is sent on any subscription, - * regardless of which subscription this listener was registered on. - * - * The default implementation of this method calls - * {@link #onOutgoingEmergencySms(EmergencyNumber)} for backwards compatibility purposes. Do - * not call {@code super(...)} from within your implementation unless you want - * {@link #onOutgoingEmergencySms(EmergencyNumber)} to be called as well. - * - * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to. - * @param subscriptionId The subscription ID used to send the emergency sms. - */ - @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) - public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber, - int subscriptionId); - } - - /** - * Interface for phone capability listener. - * @hide - */ - @SystemApi - public interface PhoneCapabilityChangedListener { - /** - * Callback invoked when phone capability changes. - * Note, this callback triggers regardless of registered subscription. - * - * @param capability the new phone capability - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability); - } - - /** - * Interface for active data subscription ID listener. - */ - public interface ActiveDataSubscriptionIdChangedListener { - /** - * Callback invoked when active data subscription ID changes. - * Note, this callback triggers regardless of registered subscription. - * - * @param subId current subscription used to setup Cellular Internet data. - * For example, it could be the current active opportunistic subscription - * in use, or the subscription user selected as default data subscription in - * DSDS mode. - */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public void onActiveDataSubscriptionIdChanged(int subId); - } - - /** - * Interface for modem radio power state listener. - * - * @hide - */ - @SystemApi - public interface RadioPowerStateChangedListener { - /** - * Callback invoked when modem radio power state changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param state the modem radio power state - */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void onRadioPowerStateChanged(@RadioPowerState int state); - } - - /** - * Interface for carrier network listener. - */ - public interface CarrierNetworkChangeListener { - /** - * Callback invoked when telephony has received notice from a carrier - * app that a network action that could result in connectivity loss - * has been requested by an app using - * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)} - * - * This is optional and is only used to allow the system to provide alternative UI while - * telephony is performing an action that may result in intentional, temporary network - * lack of connectivity. - * - * Note, this callback is pinned to the registered subscription and will be invoked when - * the notifying carrier app has carrier privilege rule on the registered - * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges} - * - * @param active If the carrier network change is or shortly will be active, - * {@code true} indicate that showing alternative UI, {@code false} otherwise. - */ - public void onCarrierNetworkChange(boolean active); - } - - /** - * Interface for registration failures listener. - */ - public interface RegistrationFailedListener { - /** - * Report that Registration or a Location/Routing/Tracking Area update has failed. - * - * <p>Indicate whenever a registration procedure, including a location, routing, or tracking - * area update fails. This includes procedures that do not necessarily result in a change of - * the modem's registration status. If the modem's registration status changes, that is - * reflected in the onNetworkStateChanged() and subsequent - * get{Voice/Data}RegistrationState(). - * - * <p>Because registration failures are ephemeral, this callback is not sticky. - * Registrants will not receive the most recent past value when registering. - * - * @param cellIdentity the CellIdentity, which must include the globally unique identifier - * for the cell (for example, all components of the CGI or ECGI). - * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the - * cell that was chosen for the failed registration attempt. - * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure. - * @param causeCode the primary failure cause code of the procedure. - * For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95 - * For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147 - * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9 - * For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2 - * Integer.MAX_VALUE if this value is unused. - * @param additionalCauseCode the cause code of any secondary/combined procedure - * if appropriate. For UMTS, if a combined attach succeeds for - * PS only, then the GMM cause code shall be included as an - * additionalCauseCode. For LTE (ESM), cause codes are in - * TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused. - */ - @RequiresPermission(allOf = { - Manifest.permission.READ_PRECISE_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION - }) - public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, - @NonNull String chosenPlmn, @Domain int domain, - int causeCode, int additionalCauseCode); - } - - /** - * Interface for the current allowed network type list listener. This list involves values of - * allowed network type for each of reasons. - * - * @hide - */ - @SystemApi - public interface AllowedNetworkTypesChangedListener { - /** - * Callback invoked when the current allowed network type list has changed on the - * registered subscription. - * Note, the registered subscription is associated with {@link TelephonyManager} object - * on which - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} - * was called. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * given subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param allowedNetworkTypesList Map associating all allowed network type reasons - * ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER}, - * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER}, - * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}, and - * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}) with reason's allowed - * network type values. - * For example: - * map{{TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER, long type value}, - * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER, long type value}, - * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER, long type value}, - * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}} - */ - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - void onAllowedNetworkTypesChanged( - @NonNull Map<Integer, Long> allowedNetworkTypesList); - } - - /** - * Interface for call attributes listener. - * - * @hide - */ - @SystemApi - public interface CallAttributesChangedListener { - /** - * Callback invoked when the call attributes changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param callAttributes the call attributes - */ - @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - void onCallAttributesChanged(@NonNull CallAttributes callAttributes); - } - - /** - * Interface for barring information listener. - */ - public interface BarringInfoChangedListener { - /** - * Report updated barring information for the current camped/registered cell. - * - * <p>Barring info is provided for all services applicable to the current camped/registered - * cell, for the registered PLMN and current access class/access category. - * - * @param barringInfo for all services on the current cell. - * @see android.telephony.BarringInfo - */ - @RequiresPermission(allOf = { - Manifest.permission.READ_PRECISE_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION - }) - public void onBarringInfoChanged(@NonNull BarringInfo barringInfo); - } - - /** - * Interface for current physical channel configuration listener. - * @hide - */ - @SystemApi - public interface PhysicalChannelConfigChangedListener { - /** - * Callback invoked when the current physical channel configuration has changed - * - * @param configs List of the current {@link PhysicalChannelConfig}s - */ - @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs); - } - - /** - * Interface for data enabled listener. - * - * @hide - */ - @SystemApi - public interface DataEnabledChangedListener { - /** - * Callback invoked when the data enabled changes. - * - * @param enabled {@code true} if data is enabled, otherwise disabled. - * @param reason Reason for data enabled/disabled. - * See {@link TelephonyManager.DataEnabledReason}. - */ - @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onDataEnabledChanged(boolean enabled, - @DataEnabledReason int reason); + mSubId = subId; + callback = new IPhoneStateListenerStub(this, e); } /** @@ -1950,7 +609,9 @@ public class PhoneStateListener { * @see ServiceState#STATE_IN_SERVICE * @see ServiceState#STATE_OUT_OF_SERVICE * @see ServiceState#STATE_POWER_OFF + * @deprecated Use {@link TelephonyCallback.ServiceStateListener} instead. */ + @Deprecated public void onServiceStateChanged(ServiceState serviceState) { // default implementation empty } @@ -1983,7 +644,10 @@ public class PhoneStateListener { * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the * subId. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead. */ + @Deprecated public void onMessageWaitingIndicatorChanged(boolean mwi) { // default implementation empty } @@ -1996,7 +660,10 @@ public class PhoneStateListener { * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the * subId. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead. */ + @Deprecated public void onCallForwardingIndicatorChanged(boolean cfi) { // default implementation empty } @@ -2009,7 +676,10 @@ public class PhoneStateListener { * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the * subId. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead. */ + @Deprecated public void onCellLocationChanged(CellLocation location) { // default implementation empty } @@ -2036,7 +706,10 @@ public class PhoneStateListener { * {@link android.Manifest.permission#READ_CALL_LOG READ_CALL_LOG} permission or carrier * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be * passed as an argument. + * + * @deprecated Use {@link TelephonyCallback.CallStateListener} instead. */ + @Deprecated public void onCallStateChanged(@CallState int state, String phoneNumber) { // default implementation empty } @@ -2054,14 +727,19 @@ public class PhoneStateListener { * @see TelephonyManager#DATA_CONNECTING * @see TelephonyManager#DATA_CONNECTED * @see TelephonyManager#DATA_SUSPENDED + * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead. */ + @Deprecated public void onDataConnectionStateChanged(int state) { // default implementation empty } /** * same as above, but with the network type. Both called. + * + * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead. */ + @Deprecated public void onDataConnectionStateChanged(int state, int networkType) { // default implementation empty } @@ -2080,7 +758,9 @@ public class PhoneStateListener { * @see TelephonyManager#DATA_ACTIVITY_OUT * @see TelephonyManager#DATA_ACTIVITY_INOUT * @see TelephonyManager#DATA_ACTIVITY_DORMANT + * @deprecated Use {@link TelephonyCallback.DataActivityListener} instead. */ + @Deprecated public void onDataActivity(int direction) { // default implementation empty } @@ -2093,7 +773,10 @@ public class PhoneStateListener { * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the * subId. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead. */ + @Deprecated public void onSignalStrengthsChanged(SignalStrength signalStrength) { // default implementation empty } @@ -2109,7 +792,9 @@ public class PhoneStateListener { * {@link SubscriptionManager#getDefaultSubscriptionId()}. * * @param cellInfo is the list of currently visible cells. + * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead. */ + @Deprecated public void onCellInfoChanged(List<CellInfo> cellInfo) { // default implementation empty } @@ -2122,11 +807,14 @@ public class PhoneStateListener { * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the * subId. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * * @param callState {@link PreciseCallState} * @hide + * @deprecated Use {@link TelephonyCallback.PreciseCallStateListener} instead. */ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @SystemApi + @Deprecated public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) { // default implementation empty } @@ -2142,9 +830,10 @@ public class PhoneStateListener { * * @param disconnectCause {@link DisconnectCause}. * @param preciseDisconnectCause {@link PreciseDisconnectCause}. - * + * @deprecated Use {@link TelephonyCallback.CallDisconnectCauseListener} instead. */ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + @Deprecated public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause, @PreciseDisconnectCauses int preciseDisconnectCause) { // default implementation empty @@ -2160,9 +849,10 @@ public class PhoneStateListener { * {@link SubscriptionManager#getDefaultSubscriptionId()}. * * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed. - * + * @deprecated Use {@link TelephonyCallback.ImsCallDisconnectCauseListener} instead. */ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + @Deprecated public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) { // default implementation empty } @@ -2183,8 +873,10 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @param dataConnectionState {@link PreciseDataConnectionState} + * @deprecated Use {@link TelephonyCallback.PreciseDataConnectionStateListener} instead. */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @Deprecated public void onPreciseDataConnectionStateChanged( @NonNull PreciseDataConnectionState dataConnectionState) { // default implementation empty @@ -2200,8 +892,10 @@ public class PhoneStateListener { * {@link SubscriptionManager#getDefaultSubscriptionId()}. * * @hide + * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Deprecated public void onDataConnectionRealTimeInfoChanged( DataConnectionRealTimeInfo dcRtInfo) { // default implementation empty @@ -2219,11 +913,12 @@ public class PhoneStateListener { * {@link SubscriptionManager#getDefaultSubscriptionId()}. * * @hide + * @deprecated Use {@link TelephonyCallback.SrvccStateListener} instead. */ @SystemApi + @Deprecated public void onSrvccStateChanged(@SrvccState int srvccState) { // default implementation empty - } /** @@ -2238,8 +933,10 @@ public class PhoneStateListener { * * @param state is the current SIM voice activation state * @hide + * @deprecated Use {@link TelephonyCallback.VoiceActivationStateListener} instead. */ @SystemApi + @Deprecated public void onVoiceActivationStateChanged(@SimActivationState int state) { // default implementation empty } @@ -2256,7 +953,9 @@ public class PhoneStateListener { * * @param state is the current SIM data activation state * @hide + * @deprecated Use {@link TelephonyCallback.DataActivationStateListener} instead. */ + @Deprecated public void onDataActivationStateChanged(@SimActivationState int state) { // default implementation empty } @@ -2271,7 +970,9 @@ public class PhoneStateListener { * {@link SubscriptionManager#getDefaultSubscriptionId()}. * * @param enabled indicates whether the current user mobile data state is enabled or disabled. + * @deprecated Use {@link TelephonyCallback.UserMobileDataStateListener} instead. */ + @Deprecated public void onUserMobileDataStateChanged(boolean enabled) { // default implementation empty } @@ -2285,8 +986,10 @@ public class PhoneStateListener { * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). * * @param telephonyDisplayInfo The display information. + * @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead. */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @Deprecated public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) { // default implementation empty } @@ -2309,7 +1012,9 @@ public class PhoneStateListener { * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as * the key and a list of emergency numbers as the value. If no * emergency number information is available, the value will be null. + * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead. */ + @Deprecated public void onEmergencyNumberListChanged( @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) { // default implementation empty @@ -2322,7 +1027,6 @@ public class PhoneStateListener { * the no-SIM case), regardless of which subscription this listener was registered on. * * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was placed to. - * * @deprecated Use {@link #onOutgoingEmergencyCall(EmergencyNumber, int)}. * @hide */ @@ -2349,8 +1053,10 @@ public class PhoneStateListener { * are no SIM cards in the device), this will be equal to * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. * @hide + * @deprecated Use {@link TelephonyCallback.OutgoingEmergencyCallListener} instead. */ @SystemApi + @Deprecated public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber, int subscriptionId) { // Default implementation for backwards compatibility @@ -2365,6 +1071,7 @@ public class PhoneStateListener { * * @deprecated Use {@link #onOutgoingEmergencySms(EmergencyNumber, int)}. * @hide + * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead. */ @SystemApi @Deprecated @@ -2386,8 +1093,10 @@ public class PhoneStateListener { * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to. * @param subscriptionId The subscription ID used to send the emergency sms. * @hide + * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead. */ @SystemApi + @Deprecated public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber, int subscriptionId) { // Default implementation for backwards compatibility @@ -2397,8 +1106,7 @@ public class PhoneStateListener { /** * Callback invoked when OEM hook raw event is received on the registered subscription. * Note, the registration subId comes from {@link TelephonyManager} object which registers - * PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}. * If this TelephonyManager object was created with * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the * subId. Otherwise, this callback applies to @@ -2407,8 +1115,10 @@ public class PhoneStateListener { * Requires the READ_PRIVILEGED_PHONE_STATE permission. * @param rawData is the byte array of the OEM hook raw data. * @hide + * @deprecated OEM needs a vendor-extension hal and their apps should use that instead */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @Deprecated public void onOemHookRawEvent(byte[] rawData) { // default implementation empty } @@ -2419,7 +1129,9 @@ public class PhoneStateListener { * * @param capability the new phone capability * @hide + * @deprecated Use {@link TelephonyCallback.PhoneCapabilityListener} instead. */ + @Deprecated public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability) { // default implementation empty } @@ -2432,7 +1144,9 @@ public class PhoneStateListener { * @param subId current subscription used to setup Cellular Internet data. * For example, it could be the current active opportunistic subscription in use, * or the subscription user selected as default data subscription in DSDS mode. + * @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead. */ + @Deprecated public void onActiveDataSubscriptionIdChanged(int subId) { // default implementation empty } @@ -2449,8 +1163,10 @@ public class PhoneStateListener { * Requires the READ_PRECISE_PHONE_STATE permission. * @param callAttributes the call attributes * @hide + * @deprecated Use {@link TelephonyCallback.CallAttributesListener} instead. */ @SystemApi + @Deprecated public void onCallAttributesChanged(@NonNull CallAttributes callAttributes) { // default implementation empty } @@ -2468,8 +1184,10 @@ public class PhoneStateListener { * * @param state the modem radio power state * @hide + * @deprecated Use {@link TelephonyCallback.RadioPowerStateListener} instead. */ @SystemApi + @Deprecated public void onRadioPowerStateChanged(@RadioPowerState int state) { // default implementation empty } @@ -2487,9 +1205,10 @@ public class PhoneStateListener { * @param active Whether the carrier network change is or shortly * will be active. This value is true to indicate * showing alternative UI and false to stop. - * * @hide + * @deprecated Use {@link TelephonyCallback.CarrierNetworkListener} instead. */ + @Deprecated public void onCarrierNetworkChange(boolean active) { // default implementation empty } @@ -2520,7 +1239,9 @@ public class PhoneStateListener { * For UMTS, if a combined attach succeeds for PS only, then the GMM cause code shall be * included as an additionalCauseCode. For LTE (ESM), cause codes are in * TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused. + * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead. */ + @Deprecated public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) { // default implementation empty @@ -2533,9 +1254,10 @@ public class PhoneStateListener { * cell, for the registered PLMN and current access class/access category. * * @param barringInfo for all services on the current cell. - * * @see android.telephony.BarringInfo + * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead. */ + @Deprecated public void onBarringInfoChanged(@NonNull BarringInfo barringInfo) { // default implementation empty } @@ -2831,7 +1553,7 @@ public class PhoneStateListener { Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onRegistrationFailed( - cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode))); + cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode))); // default implementation empty } @@ -2844,33 +1566,15 @@ public class PhoneStateListener { } public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) { - PhysicalChannelConfigChangedListener listener = - (PhysicalChannelConfigChangedListener) mPhoneStateListenerWeakRef.get(); - if (listener == null) return; - - Binder.withCleanCallingIdentity( - () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged( - configs))); + // default implementation empty } public void onDataEnabledChanged(boolean enabled, @DataEnabledReason int reason) { - DataEnabledChangedListener listener = - (DataEnabledChangedListener) mPhoneStateListenerWeakRef.get(); - if (listener == null) return; - - Binder.withCleanCallingIdentity( - () -> mExecutor.execute(() -> listener.onDataEnabledChanged( - enabled, reason))); + // default implementation empty } public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) { - AllowedNetworkTypesChangedListener listener = - (AllowedNetworkTypesChangedListener) mPhoneStateListenerWeakRef.get(); - if (listener == null) return; - - Binder.withCleanCallingIdentity( - () -> mExecutor.execute( - () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList))); + // default implementation empty } } diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java new file mode 100644 index 000000000000..a2584cae1b9c --- /dev/null +++ b/core/java/android/telephony/TelephonyCallback.java @@ -0,0 +1,1710 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.compat.annotation.ChangeId; +import android.compat.annotation.UnsupportedAppUsage; +import android.os.Binder; +import android.os.Build; +import android.telephony.emergency.EmergencyNumber; +import android.telephony.ims.ImsReasonInfo; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.IPhoneStateListener; + +import dalvik.system.VMRuntime; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; + +/** + * A callback class for monitoring changes in specific telephony states + * on the device, including service state, signal strength, message + * waiting indicator (voicemail), and others. + * <p> + * To register a callback, use a {@link TelephonyCallback} which implements interfaces regarding + * EVENT_*. For example, + * FakeServiceStateCallback extends {@link TelephonyCallback} implements + * {@link TelephonyCallback.ServiceStateListener}. + * <p> + * Then override the methods for the state that you wish to receive updates for, and + * pass the executor and your TelephonyCallback object to + * {@link TelephonyManager#registerTelephonyCallback}. + * Methods are called when the state changes, as well as once on initial registration. + * <p> + * Note that access to some telephony information is + * permission-protected. Your application won't receive updates for protected + * information unless it has the appropriate permissions declared in + * its manifest file. Where permissions apply, they are noted in the + * appropriate sub-interfaces. + */ +public class TelephonyCallback { + + /** + * Experiment flag to set the per-pid registration limit for TelephonyCallback + * + * Limit on registrations of {@link TelephonyCallback}s on a per-pid basis. When this limit is + * exceeded, any calls to {@link TelephonyManager#registerTelephonyCallback} will fail with an + * {@link IllegalStateException}. + * + * {@link android.os.Process#PHONE_UID}, {@link android.os.Process#SYSTEM_UID}, and the uid that + * TelephonyRegistry runs under are exempt from this limit. + * + * If the value of the flag is less than 1, enforcement of the limit will be disabled. + * @hide + */ + public static final String FLAG_PER_PID_REGISTRATION_LIMIT = + "phone_state_listener_per_pid_registration_limit"; + + /** + * Default value for the per-pid registration limit. + * See {@link #FLAG_PER_PID_REGISTRATION_LIMIT}. + * @hide + */ + public static final int DEFAULT_PER_PID_REGISTRATION_LIMIT = 50; + + /** + * This change enables a limit on the number of {@link TelephonyCallback} objects any process + * may register via {@link TelephonyManager#registerTelephonyCallback}. The default limit is 50, + * which may change via remote device config updates. + * + * This limit is enforced via an {@link IllegalStateException} thrown from + * {@link TelephonyManager#registerTelephonyCallback} when the offending process attempts to + * register one too many callbacks. + * + * @hide + */ + @ChangeId + public static final long PHONE_STATE_LISTENER_LIMIT_CHANGE_ID = 150880553L; + + /** + * Event for changes to the network service state (cellular). + * + * @hide + * @see ServiceStateListener#onServiceStateChanged + * @see ServiceState + */ + @SystemApi + public static final int EVENT_SERVICE_STATE_CHANGED = 1; + + /** + * Event for changes to the network signal strength (cellular). + * + * @hide + * @see SignalStrengthsListener#onSignalStrengthsChanged + */ + @SystemApi + public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; + + /** + * Event for changes to the message-waiting indicator. + * <p> + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that + * the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * <p> + * Example: The status bar uses this to determine when to display the + * voicemail icon. + * + * @hide + * @see MessageWaitingIndicatorListener#onMessageWaitingIndicatorChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; + + /** + * Event for changes to the call-forwarding indicator. + * <p> + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that + * the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * @hide + * @see CallForwardingIndicatorListener#onCallForwardingIndicatorChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; + + /** + * Event for changes to the device's cell location. Note that + * this will result in frequent listeners to the listener. + * <p> + * If you need regular location updates but want more control over + * the update interval or location precision, you can set up a callback + * through the {@link android.location.LocationManager location manager} + * instead. + * + * @hide + * @see CellLocationListener#onCellLocationChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public static final int EVENT_CELL_LOCATION_CHANGED = 5; + + /** + * Event for changes to the device call state. + * + * @hide + * @see CallStateListener#onCallStateChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) + public static final int EVENT_CALL_STATE_CHANGED = 6; + + /** + * Event for changes to the data connection state (cellular). + * + * @hide + * @see DataConnectionStateListener#onDataConnectionStateChanged + */ + @SystemApi + public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; + + /** + * Event for changes to the direction of data traffic on the data + * connection (cellular). + * <p> + * Example: The status bar uses this to display the appropriate + * data-traffic icon. + * + * @hide + * @see DataActivityListener#onDataActivity + */ + @SystemApi + public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; + + /** + * Event for changes to the network signal strengths (cellular). + * <p> + * Example: The status bar uses this to control the signal-strength + * icon. + * + * @hide + * @see SignalStrengthsListener#onSignalStrengthsChanged + */ + @SystemApi + public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; + + /** + * Event for changes of the network signal strengths (cellular) always reported from modem, + * even in some situations such as the screen of the device is off. + * + * @hide + * @see AlwaysReportedSignalStrengthListener#onSignalStrengthsChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) + public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; + + /** + * Event for changes to observed cell info. + * + * @hide + * @see CellInfoListener#onCellInfoChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public static final int EVENT_CELL_INFO_CHANGED = 11; + + /** + * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing, + * background and foreground calls. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @hide + * @see PreciseCallStateListener#onPreciseCallStateChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; + + /** + * Event for {@link PreciseDataConnectionState} on the data connection (cellular). + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @hide + * @see PreciseDataConnectionStateListener#onPreciseDataConnectionStateChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; + + /** + * Event for real time info for all data connections (cellular)). + * + * @hide + * @see PhoneStateListener#onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo) + * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; + + /** + * Event for OEM hook raw event + * + * @hide + * @see PhoneStateListener#onOemHookRawEvent + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_OEM_HOOK_RAW = 15; + + /** + * Event for changes to the SRVCC state of the active call. + * + * @hide + * @see SrvccStateListener#onSrvccStateChanged + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_SRVCC_STATE_CHANGED = 16; + + /** + * Event for carrier network changes indicated by a carrier app. + * + * @hide + * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean) + * @see CarrierNetworkListener#onCarrierNetworkChange + */ + @SystemApi + public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; + + /** + * Event for changes to the sim voice activation state + * + * @hide + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED + * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN + * <p> + * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been + * fully activated + * @see VoiceActivationStateListener#onVoiceActivationStateChanged + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; + + /** + * Event for changes to the sim data activation state + * + * @hide + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED + * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN + * <p> + * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been + * fully activated + * @see DataActivationStateListener#onDataActivationStateChanged + */ + @SystemApi + public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; + + /** + * Event for changes to the user mobile data state + * + * @hide + * @see UserMobileDataStateListener#onUserMobileDataStateChanged + */ + @SystemApi + public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; + + /** + * Event for display info changed event. + * + * @hide + * @see DisplayInfoListener#onDisplayInfoChanged + */ + @SystemApi + public static final int EVENT_DISPLAY_INFO_CHANGED = 21; + + /** + * Event for changes to the phone capability. + * + * @hide + * @see PhoneCapabilityListener#onPhoneCapabilityChanged + */ + @SystemApi + public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; + + /** + * Event for changes to active data subscription ID. Active data subscription is + * the current subscription used to setup Cellular Internet data. For example, + * it could be the current active opportunistic subscription in use, or the + * subscription user selected as default data subscription in DSDS mode. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling + * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @hide + * @see ActiveDataSubscriptionIdListener#onActiveDataSubscriptionIdChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; + + /** + * Event for changes to the radio power state. + * + * @hide + * @see RadioPowerStateListener#onRadioPowerStateChanged + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; + + /** + * Event for changes to emergency number list based on all active subscriptions. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling + * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @hide + * @see EmergencyNumberListListener#onEmergencyNumberListChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; + + /** + * Event for call disconnect causes which contains {@link DisconnectCause} and + * {@link PreciseDisconnectCause}. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @hide + * @see CallDisconnectCauseListener#onCallDisconnectCauseChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; + + /** + * Event for changes to the call attributes of a currently active call. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @hide + * @see CallAttributesListener#onCallAttributesChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; + + /** + * Event for IMS call disconnect causes which contains + * {@link android.telephony.ims.ImsReasonInfo} + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @hide + * @see ImsCallDisconnectCauseListener#onImsCallDisconnectCauseChanged(ImsReasonInfo) + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; + + /** + * Event for the emergency number placed from an outgoing call. + * + * @hide + * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; + + /** + * Event for the emergency number placed from an outgoing SMS. + * + * @hide + * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; + + /** + * Event for registration failures. + * <p> + * Event for indications that a registration procedure has failed in either the CS or PS + * domain. This indication does not necessarily indicate a change of service state, which should + * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or + * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless + * of whether the calling app has carrier privileges. + * + * @hide + * @see RegistrationFailedListener#onRegistrationFailed + */ + @SystemApi + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public static final int EVENT_REGISTRATION_FAILURE = 31; + + /** + * Event for Barring Information for the current registered / camped cell. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or + * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless + * of whether the calling app has carrier privileges. + * + * @hide + * @see BarringInfoListener#onBarringInfoChanged + */ + @SystemApi + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public static final int EVENT_BARRING_INFO_CHANGED = 32; + + /** + * Event for changes to the physical channel configuration. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @hide + * @see PhysicalChannelConfigListener#onPhysicalChannelConfigChanged + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; + + + /** + * Event for changes to the data enabled. + * <p> + * Event for indications that the enabled status of current data has changed. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @hide + * @see DataEnabledListener#onDataEnabledChanged + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_DATA_ENABLED_CHANGED = 34; + + /** + * Event for changes to allowed network list based on all active subscriptions. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling + * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @hide + * @see AllowedNetworkTypesListener#onAllowedNetworkTypesChanged + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35; + + /** + * @hide + */ + @IntDef(prefix = {"EVENT_"}, value = { + EVENT_SERVICE_STATE_CHANGED, + EVENT_SIGNAL_STRENGTH_CHANGED, + EVENT_MESSAGE_WAITING_INDICATOR_CHANGED, + EVENT_CALL_FORWARDING_INDICATOR_CHANGED, + EVENT_CELL_LOCATION_CHANGED, + EVENT_CALL_STATE_CHANGED, + EVENT_DATA_CONNECTION_STATE_CHANGED, + EVENT_DATA_ACTIVITY_CHANGED, + EVENT_SIGNAL_STRENGTHS_CHANGED, + EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED, + EVENT_CELL_INFO_CHANGED, + EVENT_PRECISE_CALL_STATE_CHANGED, + EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED, + EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED, + EVENT_OEM_HOOK_RAW, + EVENT_SRVCC_STATE_CHANGED, + EVENT_CARRIER_NETWORK_CHANGED, + EVENT_VOICE_ACTIVATION_STATE_CHANGED, + EVENT_DATA_ACTIVATION_STATE_CHANGED, + EVENT_USER_MOBILE_DATA_STATE_CHANGED, + EVENT_DISPLAY_INFO_CHANGED, + EVENT_PHONE_CAPABILITY_CHANGED, + EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED, + EVENT_RADIO_POWER_STATE_CHANGED, + EVENT_EMERGENCY_NUMBER_LIST_CHANGED, + EVENT_CALL_DISCONNECT_CAUSE_CHANGED, + EVENT_CALL_ATTRIBUTES_CHANGED, + EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED, + EVENT_OUTGOING_EMERGENCY_CALL, + EVENT_OUTGOING_EMERGENCY_SMS, + EVENT_REGISTRATION_FAILURE, + EVENT_BARRING_INFO_CHANGED, + EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, + EVENT_DATA_ENABLED_CHANGED, + EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TelephonyEvent { + } + + /** + * @hide + */ + //TODO: The maxTargetSdk should be S if the build time tool updates it. + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public IPhoneStateListener callback; + + /** + * @hide + */ + public void init(@NonNull @CallbackExecutor Executor executor) { + if (executor == null) { + throw new IllegalArgumentException("TelephonyCallback Executor must be non-null"); + } + callback = new IPhoneStateListenerStub(this, executor); + } + + /** + * Interface for service state listener. + */ + public interface ServiceStateListener { + /** + * Callback invoked when device service state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * <p> + * The instance of {@link ServiceState} passed as an argument here will have various + * levels of location information stripped from it depending on the location permissions + * that your app holds. + * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will + * receive all the information in {@link ServiceState}. + * + * @see ServiceState#STATE_EMERGENCY_ONLY + * @see ServiceState#STATE_IN_SERVICE + * @see ServiceState#STATE_OUT_OF_SERVICE + * @see ServiceState#STATE_POWER_OFF + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onServiceStateChanged(@NonNull ServiceState serviceState); + } + + /** + * Interface for message waiting indicator listener. + */ + public interface MessageWaitingIndicatorListener { + /** + * Callback invoked when the message-waiting indicator changes on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onMessageWaitingIndicatorChanged(boolean mwi); + } + + /** + * Interface for call-forwarding indicator listener. + */ + public interface CallForwardingIndicatorListener { + /** + * Callback invoked when the call-forwarding indicator changes on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onCallForwardingIndicatorChanged(boolean cfi); + } + + /** + * Interface for device cell location listener. + */ + public interface CellLocationListener { + /** + * Callback invoked when device cell location changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public void onCellLocationChanged(@NonNull CellLocation location); + } + + /** + * Interface for call state listener. + */ + public interface CallStateListener { + /** + * Callback invoked when device call state changes. + * <p> + * Reports the state of Telephony (mobile) calls on the device for the registered + * subscription. + * <p> + * Note: the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * <p> + * Note: The state returned here may differ from that returned by + * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that + * calling {@link TelephonyManager#getCallState()} from within this callback may return a + * different state than the callback reports. + * + * @param state call state + * @param phoneNumber call phone number. If application does not have + * {@link android.Manifest.permission#READ_CALL_LOG} permission or + * carrier + * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an + * empty string will be + * passed as an argument. + */ + @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) + public void onCallStateChanged(@Annotation.CallState int state, + @Nullable String phoneNumber); + } + + /** + * Interface for data connection state listener. + */ + public interface DataConnectionStateListener { + /** + * Callback invoked when connection state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param state is the current state of data connection. + * @param networkType is the current network type of data connection. + * @see TelephonyManager#DATA_DISCONNECTED + * @see TelephonyManager#DATA_CONNECTING + * @see TelephonyManager#DATA_CONNECTED + * @see TelephonyManager#DATA_SUSPENDED + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onDataConnectionStateChanged(@TelephonyManager.DataState int state, + @Annotation.NetworkType int networkType); + } + + /** + * Interface for data activity state listener. + */ + public interface DataActivityListener { + /** + * Callback invoked when data activity state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @see TelephonyManager#DATA_ACTIVITY_NONE + * @see TelephonyManager#DATA_ACTIVITY_IN + * @see TelephonyManager#DATA_ACTIVITY_OUT + * @see TelephonyManager#DATA_ACTIVITY_INOUT + * @see TelephonyManager#DATA_ACTIVITY_DORMANT + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onDataActivity(@Annotation.DataActivityType int direction); + } + + /** + * Interface for network signal strengths listener. + */ + public interface SignalStrengthsListener { + /** + * Callback invoked when network signal strengths changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength); + } + + /** + * Interface for network signal strengths callback which always reported from modem. + */ + public interface AlwaysReportedSignalStrengthListener { + /** + * Callback always invoked from modem when network signal strengths changes on the + * registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) + public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength); + } + + /** + * Interface for cell info listener. + */ + public interface CellInfoListener { + /** + * Callback invoked when a observed cell info has changed or new cells have been added + * or removed on the registered subscription. + * Note, the registration subscription ID s from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param cellInfo is the list of currently visible cells. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo); + } + + /** + * Interface for precise device call state listener. + * + * @hide + */ + @SystemApi + public interface PreciseCallStateListener { + /** + * Callback invoked when precise device call state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param callState {@link PreciseCallState} + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onPreciseCallStateChanged(@NonNull PreciseCallState callState); + } + + /** + * Interface for call disconnect cause listener. + */ + public interface CallDisconnectCauseListener { + /** + * Callback invoked when call disconnect cause changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param disconnectCause {@link DisconnectCause}. + * @param preciseDisconnectCause {@link PreciseDisconnectCause}. + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause, + @Annotation.PreciseDisconnectCauses int preciseDisconnectCause); + } + + /** + * Interface for IMS call disconnect cause listener. + */ + public interface ImsCallDisconnectCauseListener { + /** + * Callback invoked when IMS call disconnect cause changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed. + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo); + } + + /** + * Interface for precise data connection state listener. + */ + public interface PreciseDataConnectionStateListener { + /** + * Callback providing update about the default/internet data connection on the registered + * subscription. + * <p> + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @param dataConnectionState {@link PreciseDataConnectionState} + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onPreciseDataConnectionStateChanged( + @NonNull PreciseDataConnectionState dataConnectionState); + } + + /** + * Interface for Single Radio Voice Call Continuity listener. + * + * @hide + */ + @SystemApi + public interface SrvccStateListener { + /** + * Callback invoked when there has been a change in the Single Radio Voice Call Continuity + * (SRVCC) state for the currently active call on the registered subscription. + * <p> + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onSrvccStateChanged(@Annotation.SrvccState int srvccState); + } + + /** + * Interface for SIM voice activation state listener. + * + * @hide + */ + @SystemApi + public interface VoiceActivationStateListener { + /** + * Callback invoked when the SIM voice activation state has changed on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param state is the current SIM voice activation state + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onVoiceActivationStateChanged(@Annotation.SimActivationState int state); + + } + + /** + * Interface for SIM data activation state listener. + */ + public interface DataActivationStateListener { + /** + * Callback invoked when the SIM data activation state has changed on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param state is the current SIM data activation state + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onDataActivationStateChanged(@Annotation.SimActivationState int state); + } + + /** + * Interface for user mobile data state listener. + */ + public interface UserMobileDataStateListener { + /** + * Callback invoked when the user mobile data state has changed on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param enabled indicates whether the current user mobile data state is enabled or + * disabled. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onUserMobileDataStateChanged(boolean enabled); + } + + /** + * Interface for display info listener. + */ + public interface DisplayInfoListener { + /** + * Callback invoked when the display info has changed on the registered subscription. + * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user + * based on carrier policy. + * + * @param telephonyDisplayInfo The display information. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo); + } + + /** + * Interface for the current emergency number list listener. + */ + public interface EmergencyNumberListListener { + /** + * Callback invoked when the current emergency number list has changed on the registered + * subscription. + * <p> + * Note, the registered subscription is associated with {@link TelephonyManager} object + * on which + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)} + * was called. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * given subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param emergencyNumberList Map associating all active subscriptions on the device with + * the list of emergency numbers originating from that + * subscription. + * If there are no active subscriptions, the map will contain a + * single entry with + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as + * the key and a list of emergency numbers as the value. If no + * emergency number information is available, the value will be + * empty. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onEmergencyNumberListChanged( + @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList); + } + + /** + * Interface for outgoing emergency call listener. + * + * @hide + */ + @SystemApi + public interface OutgoingEmergencyCallListener { + /** + * Callback invoked when an outgoing call is placed to an emergency number. + * <p> + * This method will be called when an emergency call is placed on any subscription + * (including the no-SIM case), regardless of which subscription this callback was + * registered on. + * <p> + * + * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was + * placed to. + * @param subscriptionId The subscription ID used to place the emergency call. If the + * emergency call was placed without a valid subscription + * (e.g. when there are no SIM cards in the device), this + * will be + * equal to + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + */ + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber, + int subscriptionId); + } + + /** + * Interface for outgoing emergency sms listener. + * + * @hide + */ + @SystemApi + public interface OutgoingEmergencySmsListener { + /** + * Smsback invoked when an outgoing sms is sent to an emergency number. + * <p> + * This method will be called when an emergency sms is sent on any subscription, + * regardless of which subscription this callback was registered on. + * + * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to. + * @param subscriptionId The subscription ID used to send the emergency sms. + */ + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber, + int subscriptionId); + } + + /** + * Interface for phone capability listener. + * + * @hide + */ + @SystemApi + public interface PhoneCapabilityListener { + /** + * Callback invoked when phone capability changes. + * Note, this callback triggers regardless of registered subscription. + * + * @param capability the new phone capability + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability); + } + + /** + * Interface for active data subscription ID listener. + */ + public interface ActiveDataSubscriptionIdListener { + /** + * Callback invoked when active data subscription ID changes. + * Note, this callback triggers regardless of registered subscription. + * + * @param subId current subscription used to setup Cellular Internet data. + * For example, it could be the current active opportunistic subscription + * in use, or the subscription user selected as default data subscription in + * DSDS mode. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onActiveDataSubscriptionIdChanged(int subId); + } + + /** + * Interface for modem radio power state listener. + * + * @hide + */ + @SystemApi + public interface RadioPowerStateListener { + /** + * Callback invoked when modem radio power state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param state the modem radio power state + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onRadioPowerStateChanged(@Annotation.RadioPowerState int state); + } + + /** + * Interface for carrier network listener. + */ + public interface CarrierNetworkListener { + /** + * Callback invoked when telephony has received notice from a carrier + * app that a network action that could result in connectivity loss + * has been requested by an app using + * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)} + * <p> + * This is optional and is only used to allow the system to provide alternative UI while + * telephony is performing an action that may result in intentional, temporary network + * lack of connectivity. + * <p> + * Note, this callback is pinned to the registered subscription and will be invoked when + * the notifying carrier app has carrier privilege rule on the registered + * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges} + * + * @param active If the carrier network change is or shortly will be active, + * {@code true} indicate that showing alternative UI, {@code false} otherwise. + */ + public void onCarrierNetworkChange(boolean active); + } + + /** + * Interface for registration failures listener. + */ + public interface RegistrationFailedListener { + /** + * Report that Registration or a Location/Routing/Tracking Area update has failed. + * + * <p>Indicate whenever a registration procedure, including a location, routing, or tracking + * area update fails. This includes procedures that do not necessarily result in a change of + * the modem's registration status. If the modem's registration status changes, that is + * reflected in the onNetworkStateChanged() and subsequent + * get{Voice/Data}RegistrationState(). + * + * <p>Because registration failures are ephemeral, this callback is not sticky. + * Registrants will not receive the most recent past value when registering. + * + * @param cellIdentity the CellIdentity, which must include the globally unique + * identifier + * for the cell (for example, all components of the CGI or ECGI). + * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those + * broadcast by the + * cell that was chosen for the failed registration attempt. + * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure. + * @param causeCode the primary failure cause code of the procedure. + * For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95 + * For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147 + * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9 + * For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2 + * Integer.MAX_VALUE if this value is unused. + * @param additionalCauseCode the cause code of any secondary/combined procedure + * if appropriate. For UMTS, if a combined attach succeeds for + * PS only, then the GMM cause code shall be included as an + * additionalCauseCode. For LTE (ESM), cause codes are in + * TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused. + */ + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, + @NonNull String chosenPlmn, @NetworkRegistrationInfo.Domain int domain, int causeCode, + int additionalCauseCode); + } + + /** + * Interface for the current allowed network type list listener. This list involves values of + * allowed network type for each of reasons. + * + * @hide + */ + @SystemApi + public interface AllowedNetworkTypesListener { + /** + * Callback invoked when the current allowed network type list has changed on the + * registered subscription. + * Note, the registered subscription is associated with {@link TelephonyManager} object + * on which + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)} + * was called. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * given subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param allowedNetworkTypesList Map associating all allowed network type reasons + * ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER}, + * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER}, + * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}, and + * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}) with reason's allowed + * network type values. + * For example: + * map{{TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER, long type value}, + * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER, long type value}, + * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER, long type value}, + * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}} + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + void onAllowedNetworkTypesChanged(@NonNull Map<Integer, Long> allowedNetworkTypesList); + } + + /** + * Interface for call attributes listener. + * + * @hide + */ + @SystemApi + public interface CallAttributesListener { + /** + * Callback invoked when the call attributes changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers TelephonyCallback by + * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param callAttributes the call attributes + */ + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + void onCallAttributesChanged(@NonNull CallAttributes callAttributes); + } + + /** + * Interface for barring information listener. + */ + public interface BarringInfoListener { + /** + * Report updated barring information for the current camped/registered cell. + * + * <p>Barring info is provided for all services applicable to the current camped/registered + * cell, for the registered PLMN and current access class/access category. + * + * @param barringInfo for all services on the current cell. + * @see android.telephony.BarringInfo + */ + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public void onBarringInfoChanged(@NonNull BarringInfo barringInfo); + } + + /** + * Interface for current physical channel configuration listener. + * + * @hide + */ + @SystemApi + public interface PhysicalChannelConfigListener { + /** + * Callback invoked when the current physical channel configuration has changed + * + * @param configs List of the current {@link PhysicalChannelConfig}s + */ + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs); + } + + /** + * Interface for data enabled listener. + * + * @hide + */ + @SystemApi + public interface DataEnabledListener { + /** + * Callback invoked when the data enabled changes. + * + * @param enabled {@code true} if data is enabled, otherwise disabled. + * @param reason Reason for data enabled/disabled. + * See {@link TelephonyManager.DataEnabledReason}. + */ + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onDataEnabledChanged(boolean enabled, + @TelephonyManager.DataEnabledReason int reason); + } + + + /** + * The callback methods need to be called on the handler thread where + * this object was created. If the binder did that for us it'd be nice. + * <p> + * Using a static class and weak reference here to avoid memory leak caused by the + * IPhoneState.Stub callback retaining references to the outside TelephonyCallback: + * even caller has been destroyed and "un-registered" the TelephonyCallback, it is still not + * eligible for GC given the references coming from: + * Native Stack --> TelephonyCallback --> Context (Activity). + * memory of caller's context will be collected after GC from service side get triggered + */ + private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub { + private WeakReference<TelephonyCallback> mTelephonyCallbackWeakRef; + private Executor mExecutor; + + IPhoneStateListenerStub(TelephonyCallback telephonyCallback, Executor executor) { + mTelephonyCallbackWeakRef = new WeakReference<TelephonyCallback>(telephonyCallback); + mExecutor = executor; + } + + public void onServiceStateChanged(ServiceState serviceState) { + ServiceStateListener listener = (ServiceStateListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onServiceStateChanged(serviceState))); + } + + public void onSignalStrengthChanged(int asu) { + // default implementation empty + } + + public void onMessageWaitingIndicatorChanged(boolean mwi) { + MessageWaitingIndicatorListener listener = + (MessageWaitingIndicatorListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onMessageWaitingIndicatorChanged(mwi))); + } + + public void onCallForwardingIndicatorChanged(boolean cfi) { + CallForwardingIndicatorListener listener = + (CallForwardingIndicatorListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onCallForwardingIndicatorChanged(cfi))); + } + + public void onCellLocationChanged(CellIdentity cellIdentity) { + // There is no system/public API to create an CellIdentity in system server, + // so the server pass a null to indicate an empty initial location. + CellLocation location = + cellIdentity == null ? CellLocation.getEmpty() : cellIdentity.asCellLocation(); + CellLocationListener listener = (CellLocationListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onCellLocationChanged(location))); + } + + public void onCallStateChanged(int state, String incomingNumber) { + CallStateListener listener = (CallStateListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onCallStateChanged(state, + incomingNumber))); + } + + public void onDataConnectionStateChanged(int state, int networkType) { + DataConnectionStateListener listener = + (DataConnectionStateListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + if (state == TelephonyManager.DATA_DISCONNECTING + && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) { + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> + listener.onDataConnectionStateChanged( + TelephonyManager.DATA_CONNECTED, networkType))); + } else { + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> + listener.onDataConnectionStateChanged(state, networkType))); + } + } + + public void onDataActivity(int direction) { + DataActivityListener listener = (DataActivityListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onDataActivity(direction))); + } + + public void onSignalStrengthsChanged(SignalStrength signalStrength) { + SignalStrengthsListener listener = + (SignalStrengthsListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onSignalStrengthsChanged( + signalStrength))); + } + + public void onCellInfoChanged(List<CellInfo> cellInfo) { + CellInfoListener listener = (CellInfoListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onCellInfoChanged(cellInfo))); + } + + public void onPreciseCallStateChanged(PreciseCallState callState) { + PreciseCallStateListener listener = + (PreciseCallStateListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onPreciseCallStateChanged(callState))); + } + + public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) { + CallDisconnectCauseListener listener = + (CallDisconnectCauseListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onCallDisconnectCauseChanged( + disconnectCause, preciseDisconnectCause))); + } + + public void onPreciseDataConnectionStateChanged( + PreciseDataConnectionState dataConnectionState) { + PreciseDataConnectionStateListener listener = + (PreciseDataConnectionStateListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> listener.onPreciseDataConnectionStateChanged( + dataConnectionState))); + } + + public void onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo dcRtInfo) { + // default implementation empty + } + + public void onSrvccStateChanged(int state) { + SrvccStateListener listener = (SrvccStateListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onSrvccStateChanged(state))); + } + + public void onVoiceActivationStateChanged(int activationState) { + VoiceActivationStateListener listener = + (VoiceActivationStateListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> listener.onVoiceActivationStateChanged(activationState))); + } + + public void onDataActivationStateChanged(int activationState) { + DataActivationStateListener listener = + (DataActivationStateListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> listener.onDataActivationStateChanged(activationState))); + } + + public void onUserMobileDataStateChanged(boolean enabled) { + UserMobileDataStateListener listener = + (UserMobileDataStateListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> listener.onUserMobileDataStateChanged(enabled))); + } + + public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) { + DisplayInfoListener listener = (DisplayInfoListener)mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> listener.onDisplayInfoChanged(telephonyDisplayInfo))); + } + + public void onOemHookRawEvent(byte[] rawData) { + // default implementation empty + } + + public void onCarrierNetworkChange(boolean active) { + CarrierNetworkListener listener = + (CarrierNetworkListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onCarrierNetworkChange(active))); + } + + public void onEmergencyNumberListChanged(Map emergencyNumberList) { + EmergencyNumberListListener listener = + (EmergencyNumberListListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> listener.onEmergencyNumberListChanged(emergencyNumberList))); + } + + public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber, + int subscriptionId) { + OutgoingEmergencyCallListener listener = + (OutgoingEmergencyCallListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> listener.onOutgoingEmergencyCall(placedEmergencyNumber, + subscriptionId))); + } + + public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber, + int subscriptionId) { + OutgoingEmergencySmsListener listener = + (OutgoingEmergencySmsListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> listener.onOutgoingEmergencySms(sentEmergencyNumber, + subscriptionId))); + } + + public void onPhoneCapabilityChanged(PhoneCapability capability) { + PhoneCapabilityListener listener = + (PhoneCapabilityListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onPhoneCapabilityChanged(capability))); + } + + public void onRadioPowerStateChanged(@Annotation.RadioPowerState int state) { + RadioPowerStateListener listener = + (RadioPowerStateListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onRadioPowerStateChanged(state))); + } + + public void onCallAttributesChanged(CallAttributes callAttributes) { + CallAttributesListener listener = + (CallAttributesListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onCallAttributesChanged( + callAttributes))); + } + + public void onActiveDataSubIdChanged(int subId) { + ActiveDataSubscriptionIdListener listener = + (ActiveDataSubscriptionIdListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onActiveDataSubscriptionIdChanged( + subId))); + } + + public void onImsCallDisconnectCauseChanged(ImsReasonInfo disconnectCause) { + ImsCallDisconnectCauseListener listener = + (ImsCallDisconnectCauseListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> listener.onImsCallDisconnectCauseChanged(disconnectCause))); + } + + public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, + @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) { + RegistrationFailedListener listener = + (RegistrationFailedListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onRegistrationFailed( + cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode))); + // default implementation empty + } + + public void onBarringInfoChanged(BarringInfo barringInfo) { + BarringInfoListener listener = (BarringInfoListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onBarringInfoChanged(barringInfo))); + } + + public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) { + PhysicalChannelConfigListener listener = + (PhysicalChannelConfigListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged( + configs))); + } + + public void onDataEnabledChanged(boolean enabled, + @TelephonyManager.DataEnabledReason int reason) { + DataEnabledListener listener = + (DataEnabledListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onDataEnabledChanged( + enabled, reason))); + } + + public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) { + AllowedNetworkTypesListener listener = + (AllowedNetworkTypesListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList))); + } + } +} diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 3c355d4b6f45..459c6e94e4ac 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -209,7 +209,7 @@ public class TelephonyRegistryManager { } /** - * To check the SDK version for {@link #listenWithEventList}. + * To check the SDK version for {@link #listenFromListener}. */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P) @@ -221,24 +221,49 @@ public class TelephonyRegistryManager { * @param pkg Package name * @param featureId Feature ID * @param listener Listener providing callback - * @param events List events + * @param events Events * @param notifyNow Whether to notify instantly */ - public void listenWithEventList(int subId, @NonNull String pkg, @NonNull String featureId, - @NonNull PhoneStateListener listener, @NonNull int[] events, boolean notifyNow) { + public void listenFromListener(int subId, @NonNull String pkg, @NonNull String featureId, + @NonNull PhoneStateListener listener, @NonNull int events, boolean notifyNow) { + if (listener == null) { + throw new IllegalStateException("telephony service is null."); + } + try { + int[] eventsList = getEventsFromBitmask(events).stream().mapToInt(i -> i).toArray(); // subId from PhoneStateListener is deprecated Q on forward, use the subId from // TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q. if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) { // Since mSubId in PhoneStateListener is deprecated from Q on forward, this is // the only place to set mSubId and its for "informational" only. - listener.mSubId = (events.length == 0) + listener.mSubId = (eventsList.length == 0) ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId; } else if (listener.mSubId != null) { subId = listener.mSubId; } sRegistry.listenWithEventList( - subId, pkg, featureId, listener.callback, events, notifyNow); + subId, pkg, featureId, listener.callback, eventsList, notifyNow); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Listen for incoming subscriptions + * @param subId Subscription ID + * @param pkg Package name + * @param featureId Feature ID + * @param telephonyCallback Listener providing callback + * @param events List events + * @param notifyNow Whether to notify instantly + */ + private void listenFromCallback(int subId, @NonNull String pkg, @NonNull String featureId, + @NonNull TelephonyCallback telephonyCallback, @NonNull int[] events, + boolean notifyNow) { + try { + sRegistry.listenWithEventList( + subId, pkg, featureId, telephonyCallback.callback, events, notifyNow); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -815,136 +840,137 @@ public class TelephonyRegistryManager { } } - public @NonNull Set<Integer> getEventsFromListener(@NonNull PhoneStateListener listener) { + public @NonNull Set<Integer> getEventsFromCallback( + @NonNull TelephonyCallback telephonyCallback) { Set<Integer> eventList = new ArraySet<>(); - if (listener instanceof PhoneStateListener.ServiceStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.ServiceStateListener) { + eventList.add(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED); } - if (listener instanceof PhoneStateListener.MessageWaitingIndicatorChangedListener) { - eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.MessageWaitingIndicatorListener) { + eventList.add(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED); } - if (listener instanceof PhoneStateListener.CallForwardingIndicatorChangedListener) { - eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.CallForwardingIndicatorListener) { + eventList.add(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED); } - if (listener instanceof PhoneStateListener.CellLocationChangedListener) { - eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.CellLocationListener) { + eventList.add(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED); } - if (listener instanceof PhoneStateListener.CallStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.CallStateListener) { + eventList.add(TelephonyCallback.EVENT_CALL_STATE_CHANGED); } - if (listener instanceof PhoneStateListener.DataConnectionStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.DataConnectionStateListener) { + eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED); } - if (listener instanceof PhoneStateListener.DataActivityListener) { - eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.DataActivityListener) { + eventList.add(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED); } - if (listener instanceof PhoneStateListener.SignalStrengthsChangedListener) { - eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.SignalStrengthsListener) { + eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED); } - if (listener instanceof PhoneStateListener.AlwaysReportedSignalStrengthChangedListener) { - eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.AlwaysReportedSignalStrengthListener) { + eventList.add(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED); } - if (listener instanceof PhoneStateListener.CellInfoChangedListener) { - eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.CellInfoListener) { + eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED); } - if (listener instanceof PhoneStateListener.PreciseCallStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.PreciseCallStateListener) { + eventList.add(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED); } - if (listener instanceof PhoneStateListener.CallDisconnectCauseChangedListener) { - eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.CallDisconnectCauseListener) { + eventList.add(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); } - if (listener instanceof PhoneStateListener.ImsCallDisconnectCauseChangedListener) { - eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.ImsCallDisconnectCauseListener) { + eventList.add(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); } - if (listener instanceof PhoneStateListener.PreciseDataConnectionStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.PreciseDataConnectionStateListener) { + eventList.add(TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); } - if (listener instanceof PhoneStateListener.SrvccStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.SrvccStateListener) { + eventList.add(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED); } - if (listener instanceof PhoneStateListener.VoiceActivationStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.VoiceActivationStateListener) { + eventList.add(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED); } - if (listener instanceof PhoneStateListener.DataActivationStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.DataActivationStateListener) { + eventList.add(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED); } - if (listener instanceof PhoneStateListener.UserMobileDataStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.UserMobileDataStateListener) { + eventList.add(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED); } - if (listener instanceof PhoneStateListener.DisplayInfoChangedListener) { - eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.DisplayInfoListener) { + eventList.add(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED); } - if (listener instanceof PhoneStateListener.EmergencyNumberListChangedListener) { - eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.EmergencyNumberListListener) { + eventList.add(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED); } - if (listener instanceof PhoneStateListener.OutgoingEmergencyCallListener) { - eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL); + if (telephonyCallback instanceof TelephonyCallback.OutgoingEmergencyCallListener) { + eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL); } - if (listener instanceof PhoneStateListener.OutgoingEmergencySmsListener) { - eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); + if (telephonyCallback instanceof TelephonyCallback.OutgoingEmergencySmsListener) { + eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS); } - if (listener instanceof PhoneStateListener.PhoneCapabilityChangedListener) { - eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.PhoneCapabilityListener) { + eventList.add(TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED); } - if (listener instanceof PhoneStateListener.ActiveDataSubscriptionIdChangedListener) { - eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.ActiveDataSubscriptionIdListener) { + eventList.add(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); } - if (listener instanceof PhoneStateListener.RadioPowerStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.RadioPowerStateListener) { + eventList.add(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED); } - if (listener instanceof PhoneStateListener.CarrierNetworkChangeListener) { - eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.CarrierNetworkListener) { + eventList.add(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED); } - if (listener instanceof PhoneStateListener.RegistrationFailedListener) { - eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); + if (telephonyCallback instanceof TelephonyCallback.RegistrationFailedListener) { + eventList.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE); } - if (listener instanceof PhoneStateListener.CallAttributesChangedListener) { - eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.CallAttributesListener) { + eventList.add(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED); } - if (listener instanceof PhoneStateListener.BarringInfoChangedListener) { - eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.BarringInfoListener) { + eventList.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED); } - if (listener instanceof PhoneStateListener.PhysicalChannelConfigChangedListener) { - eventList.add(PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.PhysicalChannelConfigListener) { + eventList.add(TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); } - if (listener instanceof PhoneStateListener.DataEnabledChangedListener) { - eventList.add(PhoneStateListener.EVENT_DATA_ENABLED_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.DataEnabledListener) { + eventList.add(TelephonyCallback.EVENT_DATA_ENABLED_CHANGED); } - if (listener instanceof PhoneStateListener.AllowedNetworkTypesChangedListener) { - eventList.add(PhoneStateListener.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED); + if (telephonyCallback instanceof TelephonyCallback.AllowedNetworkTypesListener) { + eventList.add(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED); } return eventList; @@ -955,200 +981,183 @@ public class TelephonyRegistryManager { Set<Integer> eventList = new ArraySet<>(); if ((eventMask & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { - eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED); + eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { - eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED); + eventList.add(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { - eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED); + eventList.add(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { - eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED); + eventList.add(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_CALL_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_CALL_STATE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { - eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED); + eventList.add(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { - eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED); + eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { - eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED); + eventList.add(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) { - eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED); + eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) { - eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED); + eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) { - eventList.add(PhoneStateListener.EVENT_OEM_HOOK_RAW); + eventList.add(TelephonyCallback.EVENT_OEM_HOOK_RAW); } if ((eventMask & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) { - eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) { - eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED); + eventList.add(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { - eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED); + eventList.add(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) { - eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED); + eventList.add(TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) { - eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); + eventList.add(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) { - eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) { - eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED); + eventList.add(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { - eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); + eventList.add(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) { - eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); + eventList.add(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) { - eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); + eventList.add(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); } if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL) != 0) { - eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL); + eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL); } if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS) != 0) { - eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); + eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS); } if ((eventMask & PhoneStateListener.LISTEN_REGISTRATION_FAILURE) != 0) { - eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); + eventList.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE); } if ((eventMask & PhoneStateListener.LISTEN_BARRING_INFO) != 0) { - eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + eventList.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED); } return eventList; } /** - * Registers a listener object to receive notification of changes - * in specified telephony states. + * Registers a callback object to receive notification of changes in specified telephony states. * <p> - * To register a listener, pass a {@link PhoneStateListener} which implements + * To register a callback, pass a {@link TelephonyCallback} which implements * interfaces of events. For example, - * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements - * {@link PhoneStateListener.ServiceStateChangedListener}. + * FakeServiceStateCallback extends {@link TelephonyCallback} implements + * {@link TelephonyCallback.ServiceStateListener}. * * At registration, and when a specified telephony state changes, the telephony manager invokes - * the appropriate callback method on the listener object and passes the current (updated) + * the appropriate callback method on the callback object and passes the current (updated) * values. * <p> * * If this TelephonyManager object has been created with * {@link TelephonyManager#createForSubscriptionId}, applies to the given subId. * Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}. - * To listen events for multiple subIds, pass a separate listener object to + * To register events for multiple subIds, pass a separate callback object to * each TelephonyManager object created with {@link TelephonyManager#createForSubscriptionId}. * * Note: if you call this method while in the middle of a binder transaction, you <b>must</b> * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A * {@link SecurityException} will be thrown otherwise. * - * This API should be used sparingly -- large numbers of listeners will cause system - * instability. If a process has registered too many listeners without unregistering them, it - * may encounter an {@link IllegalStateException} when trying to register more listeners. + * This API should be used sparingly -- large numbers of callbacks will cause system + * instability. If a process has registered too many callbacks without unregistering them, it + * may encounter an {@link IllegalStateException} when trying to register more callbacks. * - * @param listener The {@link PhoneStateListener} object to register. + * @param callback The {@link TelephonyCallback} object to register. */ - public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, int subId, - String pkgName, String attributionTag, @NonNull PhoneStateListener listener, + public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor, + int subId, String pkgName, String attributionTag, @NonNull TelephonyCallback callback, boolean notifyNow) { - listener.setExecutor(executor); - registerPhoneStateListener(subId, pkgName, attributionTag, listener, - getEventsFromListener(listener), notifyNow); - } - - public void registerPhoneStateListenerWithEvents(int subId, String pkgName, - String attributionTag, @NonNull PhoneStateListener listener, int events, - boolean notifyNow) { - registerPhoneStateListener( - subId, pkgName, attributionTag, listener, getEventsFromBitmask(events), notifyNow); - } - - private void registerPhoneStateListener(int subId, - String pkgName, String attributionTag, @NonNull PhoneStateListener listener, - @NonNull Set<Integer> events, boolean notifyNow) { - if (listener == null) { + if (callback == null) { throw new IllegalStateException("telephony service is null."); } - - listenWithEventList(subId, pkgName, attributionTag, listener, - events.stream().mapToInt(i -> i).toArray(), notifyNow); + callback.init(executor); + listenFromCallback(subId, pkgName, attributionTag, callback, + getEventsFromCallback(callback).stream().mapToInt(i -> i).toArray(), notifyNow); } /** - * Unregister an existing {@link PhoneStateListener}. + * Unregister an existing {@link TelephonyCallback}. * - * @param listener The {@link PhoneStateListener} object to unregister. + * @param callback The {@link TelephonyCallback} object to unregister. */ - public void unregisterPhoneStateListener(int subId, String pkgName, String attributionTag, - @NonNull PhoneStateListener listener, - boolean notifyNow) { - listenWithEventList(subId, pkgName, attributionTag, listener, new int[0], notifyNow); + public void unregisterTelephonyCallback(int subId, String pkgName, String attributionTag, + @NonNull TelephonyCallback callback, boolean notifyNow) { + listenFromCallback(subId, pkgName, attributionTag, callback, new int[0], notifyNow); } } diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 2b577d04b18d..3a3302475069 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -68,7 +68,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true"); DEFAULT_FLAGS.put("settings_tether_all_in_one", "false"); - DEFAULT_FLAGS.put("settings_silky_home", "false"); + DEFAULT_FLAGS.put("settings_silky_home", "true"); DEFAULT_FLAGS.put("settings_contextual_home", "false"); DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false"); DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "false"); diff --git a/core/java/android/util/SizeF.java b/core/java/android/util/SizeF.java index 2edc4a7ff588..c77a02434941 100644 --- a/core/java/android/util/SizeF.java +++ b/core/java/android/util/SizeF.java @@ -16,8 +16,12 @@ package android.util; -import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkArgumentFinite; +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; /** * Immutable class for describing width and height dimensions in some arbitrary @@ -26,7 +30,7 @@ import static com.android.internal.util.Preconditions.checkArgumentFinite; * Width and height are finite values stored as a floating point representation. * </p> */ -public final class SizeF { +public final class SizeF implements Parcelable { /** * Create a new immutable SizeF instance. * @@ -161,4 +165,43 @@ public final class SizeF { private final float mWidth; private final float mHeight; + + /** + * Parcelable interface methods + */ + @Override + public int describeContents() { + return 0; + } + + /** + * Write this size to the specified parcel. To restore a size from a parcel, use the + * {@link #CREATOR}. + * @param out The parcel to write the point's coordinates into + */ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeFloat(mWidth); + out.writeFloat(mHeight); + } + + public static final @NonNull Creator<SizeF> CREATOR = new Creator<SizeF>() { + /** + * Return a new size from the data in the specified parcel. + */ + @Override + public @NonNull SizeF createFromParcel(@NonNull Parcel in) { + float width = in.readFloat(); + float height = in.readFloat(); + return new SizeF(width, height); + } + + /** + * Return an array of sizes of the specified size. + */ + @Override + public @NonNull SizeF[] newArray(int size) { + return new SizeF[size]; + } + }; } diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 09452828057e..e5a137cd13f3 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -86,6 +86,12 @@ public final class AccessibilityInteractionController { // accessibility from hanging private static final long REQUEST_PREPARER_TIMEOUT_MS = 500; + // Callbacks should have the same configuration of the flags below to allow satisfying a pending + // node request on prefetch + private static final int FLAGS_AFFECTING_REPORTED_DATA = + AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS + | AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS; + private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList = new ArrayList<AccessibilityNodeInfo>(); @@ -114,6 +120,9 @@ public final class AccessibilityInteractionController { private AddNodeInfosForViewId mAddNodeInfosForViewId; @GuardedBy("mLock") + private ArrayList<Message> mPendingFindNodeByIdMessages; + + @GuardedBy("mLock") private int mNumActiveRequestPreparers; @GuardedBy("mLock") private List<MessageHolder> mMessagesWaitingForRequestPreparer; @@ -128,6 +137,7 @@ public final class AccessibilityInteractionController { mViewRootImpl = viewRootImpl; mPrefetcher = new AccessibilityNodePrefetcher(); mA11yManager = mViewRootImpl.mContext.getSystemService(AccessibilityManager.class); + mPendingFindNodeByIdMessages = new ArrayList<>(); } private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid, @@ -177,7 +187,11 @@ public final class AccessibilityInteractionController { args.arg4 = arguments; message.obj = args; - scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS); + synchronized (mLock) { + mPendingFindNodeByIdMessages.add(message); + scheduleMessage(message, interrogatingPid, interrogatingTid, + CONSIDER_REQUEST_PREPARERS); + } } /** @@ -315,6 +329,9 @@ public final class AccessibilityInteractionController { } private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) { + synchronized (mLock) { + mPendingFindNodeByIdMessages.remove(message); + } final int flags = message.arg1; SomeArgs args = (SomeArgs) message.obj; @@ -329,22 +346,58 @@ public final class AccessibilityInteractionController { args.recycle(); - List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; - infos.clear(); + View rootView = null; + AccessibilityNodeInfo rootNode = null; try { if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { return; } mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags; - final View root = findViewByAccessibilityId(accessibilityViewId); - if (root != null && isShown(root)) { - mPrefetcher.prefetchAccessibilityNodeInfos( - root, virtualDescendantId, flags, infos, arguments); + rootView = findViewByAccessibilityId(accessibilityViewId); + if (rootView != null && isShown(rootView)) { + rootNode = populateAccessibilityNodeInfoForView( + rootView, arguments, virtualDescendantId); } } finally { - updateInfosForViewportAndReturnFindNodeResult( - infos, callback, interactionId, spec, interactiveRegion); + updateInfoForViewportAndReturnFindNodeResult( + rootNode == null ? null : AccessibilityNodeInfo.obtain(rootNode), + callback, interactionId, spec, interactiveRegion); } + ArrayList<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; + infos.clear(); + mPrefetcher.prefetchAccessibilityNodeInfos( + rootView, rootNode == null ? null : AccessibilityNodeInfo.obtain(rootNode), + virtualDescendantId, flags, infos); + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; + updateInfosForViewPort(infos, spec, interactiveRegion); + returnPrefetchResult(interactionId, infos, callback); + returnPendingFindAccessibilityNodeInfosInPrefetch(rootNode, infos, flags); + } + + private AccessibilityNodeInfo populateAccessibilityNodeInfoForView( + View view, Bundle arguments, int virtualViewId) { + AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); + // Determine if we'll be populating extra data + final String extraDataRequested = (arguments == null) ? null + : arguments.getString(EXTRA_DATA_REQUESTED_KEY); + AccessibilityNodeInfo root = null; + if (provider == null) { + root = view.createAccessibilityNodeInfo(); + if (root != null) { + if (extraDataRequested != null) { + view.addExtraDataToAccessibilityNodeInfo(root, extraDataRequested, arguments); + } + } + } else { + root = provider.createAccessibilityNodeInfo(virtualViewId); + if (root != null) { + if (extraDataRequested != null) { + provider.addExtraDataToAccessibilityNodeInfo( + virtualViewId, root, extraDataRequested, arguments); + } + } + } + return root; } public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId, @@ -403,6 +456,7 @@ public final class AccessibilityInteractionController { mAddNodeInfosForViewId.reset(); } } finally { + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfosForViewportAndReturnFindNodeResult( infos, callback, interactionId, spec, interactiveRegion); } @@ -485,6 +539,7 @@ public final class AccessibilityInteractionController { } } } finally { + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfosForViewportAndReturnFindNodeResult( infos, callback, interactionId, spec, interactiveRegion); } @@ -576,6 +631,7 @@ public final class AccessibilityInteractionController { } } } finally { + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfoForViewportAndReturnFindNodeResult( focused, callback, interactionId, spec, interactiveRegion); } @@ -630,6 +686,7 @@ public final class AccessibilityInteractionController { } } } finally { + mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; updateInfoForViewportAndReturnFindNodeResult( next, callback, interactionId, spec, interactiveRegion); } @@ -786,33 +843,6 @@ public final class AccessibilityInteractionController { } } - private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos, - MagnificationSpec spec) { - if (infos == null) { - return; - } - final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale; - if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) { - final int infoCount = infos.size(); - for (int i = 0; i < infoCount; i++) { - AccessibilityNodeInfo info = infos.get(i); - applyAppScaleAndMagnificationSpecIfNeeded(info, spec); - } - } - } - - private void adjustIsVisibleToUserIfNeeded(List<AccessibilityNodeInfo> infos, - Region interactiveRegion) { - if (interactiveRegion == null || infos == null) { - return; - } - final int infoCount = infos.size(); - for (int i = 0; i < infoCount; i++) { - AccessibilityNodeInfo info = infos.get(i); - adjustIsVisibleToUserIfNeeded(info, interactiveRegion); - } - } - private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info, Region interactiveRegion) { if (interactiveRegion == null || info == null) { @@ -833,17 +863,6 @@ public final class AccessibilityInteractionController { return false; } - private void adjustBoundsInScreenIfNeeded(List<AccessibilityNodeInfo> infos) { - if (infos == null || shouldBypassAdjustBoundsInScreen()) { - return; - } - final int infoCount = infos.size(); - for (int i = 0; i < infoCount; i++) { - final AccessibilityNodeInfo info = infos.get(i); - adjustBoundsInScreenIfNeeded(info); - } - } - private void adjustBoundsInScreenIfNeeded(AccessibilityNodeInfo info) { if (info == null || shouldBypassAdjustBoundsInScreen()) { return; @@ -891,17 +910,6 @@ public final class AccessibilityInteractionController { return screenMatrix == null || screenMatrix.isIdentity(); } - private void associateLeashedParentIfNeeded(List<AccessibilityNodeInfo> infos) { - if (infos == null || shouldBypassAssociateLeashedParent()) { - return; - } - final int infoCount = infos.size(); - for (int i = 0; i < infoCount; i++) { - final AccessibilityNodeInfo info = infos.get(i); - associateLeashedParentIfNeeded(info); - } - } - private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) { if (info == null || shouldBypassAssociateLeashedParent()) { return; @@ -975,18 +983,46 @@ public final class AccessibilityInteractionController { return (appScale != 1.0f || (spec != null && !spec.isNop())); } + private void updateInfosForViewPort(List<AccessibilityNodeInfo> infos, MagnificationSpec spec, + Region interactiveRegion) { + for (int i = 0; i < infos.size(); i++) { + updateInfoForViewPort(infos.get(i), spec, interactiveRegion); + } + } + + private void updateInfoForViewPort(AccessibilityNodeInfo info, MagnificationSpec spec, + Region interactiveRegion) { + associateLeashedParentIfNeeded(info); + applyScreenMatrixIfNeeded(info); + adjustBoundsInScreenIfNeeded(info); + // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node, + // then impact the visibility result, we need to adjust visibility before apply scale. + adjustIsVisibleToUserIfNeeded(info, interactiveRegion); + applyAppScaleAndMagnificationSpecIfNeeded(info, spec); + } + private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, Region interactiveRegion) { + if (infos != null) { + updateInfosForViewPort(infos, spec, interactiveRegion); + } + returnFindNodesResult(infos, callback, interactionId); + } + + private void returnFindNodeResult(AccessibilityNodeInfo info, + IAccessibilityInteractionConnectionCallback callback, + int interactionId) { + try { + callback.setFindAccessibilityNodeInfoResult(info, interactionId); + } catch (RemoteException re) { + /* ignore - the other side will time out */ + } + } + + private void returnFindNodesResult(List<AccessibilityNodeInfo> infos, + IAccessibilityInteractionConnectionCallback callback, int interactionId) { try { - mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; - associateLeashedParentIfNeeded(infos); - applyScreenMatrixIfNeeded(infos); - adjustBoundsInScreenIfNeeded(infos); - // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node, - // then impact the visibility result, we need to adjust visibility before apply scale. - adjustIsVisibleToUserIfNeeded(infos, interactiveRegion); - applyAppScaleAndMagnificationSpecIfNeeded(infos, spec); callback.setFindAccessibilityNodeInfosResult(infos, interactionId); if (infos != null) { infos.clear(); @@ -996,22 +1032,80 @@ public final class AccessibilityInteractionController { } } + private void returnPendingFindAccessibilityNodeInfosInPrefetch(AccessibilityNodeInfo rootNode, + List<AccessibilityNodeInfo> infos, int flags) { + + AccessibilityNodeInfo satisfiedPendingRequestPrefetchedNode = null; + IAccessibilityInteractionConnectionCallback satisfiedPendingRequestCallback = null; + int satisfiedPendingRequestInteractionId = AccessibilityInteractionClient.NO_ID; + + synchronized (mLock) { + for (int i = 0; i < mPendingFindNodeByIdMessages.size(); i++) { + final Message pendingMessage = mPendingFindNodeByIdMessages.get(i); + final int pendingFlags = pendingMessage.arg1; + if ((pendingFlags & FLAGS_AFFECTING_REPORTED_DATA) + != (flags & FLAGS_AFFECTING_REPORTED_DATA)) { + continue; + } + SomeArgs args = (SomeArgs) pendingMessage.obj; + final int accessibilityViewId = args.argi1; + final int virtualDescendantId = args.argi2; + + satisfiedPendingRequestPrefetchedNode = nodeWithIdFromList(rootNode, + infos, AccessibilityNodeInfo.makeNodeId( + accessibilityViewId, virtualDescendantId)); + + if (satisfiedPendingRequestPrefetchedNode != null) { + satisfiedPendingRequestCallback = + (IAccessibilityInteractionConnectionCallback) args.arg1; + satisfiedPendingRequestInteractionId = args.argi3; + mHandler.removeMessages( + PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID, + pendingMessage.obj); + args.recycle(); + break; + } + } + mPendingFindNodeByIdMessages.clear(); + } + + if (satisfiedPendingRequestPrefetchedNode != null) { + returnFindNodeResult( + AccessibilityNodeInfo.obtain(satisfiedPendingRequestPrefetchedNode), + satisfiedPendingRequestCallback, satisfiedPendingRequestInteractionId); + } + } + + private AccessibilityNodeInfo nodeWithIdFromList(AccessibilityNodeInfo rootNode, + List<AccessibilityNodeInfo> infos, long nodeId) { + if (rootNode != null && rootNode.getSourceNodeId() == nodeId) { + return rootNode; + } + for (int j = 0; j < infos.size(); j++) { + AccessibilityNodeInfo info = infos.get(j); + if (info.getSourceNodeId() == nodeId) { + return info; + } + } + return null; + } + + private void returnPrefetchResult(int interactionId, List<AccessibilityNodeInfo> infos, + IAccessibilityInteractionConnectionCallback callback) { + if (infos.size() > 0) { + try { + callback.setPrefetchAccessibilityNodeInfoResult(infos, interactionId); + } catch (RemoteException re) { + /* ignore - other side isn't too bothered if this doesn't arrive */ + } + } + } + private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info, IAccessibilityInteractionConnectionCallback callback, int interactionId, MagnificationSpec spec, Region interactiveRegion) { - try { - mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0; - associateLeashedParentIfNeeded(info); - applyScreenMatrixIfNeeded(info); - adjustBoundsInScreenIfNeeded(info); - // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node, - // then impact the visibility result, we need to adjust visibility before apply scale. - adjustIsVisibleToUserIfNeeded(info, interactiveRegion); - applyAppScaleAndMagnificationSpecIfNeeded(info, spec); - callback.setFindAccessibilityNodeInfoResult(info, interactionId); - } catch (RemoteException re) { - /* ignore - the other side will time out */ - } + updateInfoForViewPort(info, spec, interactiveRegion); + returnFindNodeResult(info, callback, interactionId); } private boolean handleClickableSpanActionUiThread( @@ -1054,56 +1148,45 @@ public final class AccessibilityInteractionController { private final ArrayList<View> mTempViewList = new ArrayList<View>(); - public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags, - List<AccessibilityNodeInfo> outInfos, Bundle arguments) { + public void prefetchAccessibilityNodeInfos(View view, AccessibilityNodeInfo root, + int virtualViewId, int fetchFlags, List<AccessibilityNodeInfo> outInfos) { + if (root == null) { + return; + } AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider(); - // Determine if we'll be populating extra data - final String extraDataRequested = (arguments == null) ? null - : arguments.getString(EXTRA_DATA_REQUESTED_KEY); if (provider == null) { - AccessibilityNodeInfo root = view.createAccessibilityNodeInfo(); - if (root != null) { - if (extraDataRequested != null) { - view.addExtraDataToAccessibilityNodeInfo( - root, extraDataRequested, arguments); - } - outInfos.add(root); - if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { - prefetchPredecessorsOfRealNode(view, outInfos); - } - if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) { - prefetchSiblingsOfRealNode(view, outInfos); - } - if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) { - prefetchDescendantsOfRealNode(view, outInfos); - } + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { + prefetchPredecessorsOfRealNode(view, outInfos); + } + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) { + prefetchSiblingsOfRealNode(view, outInfos); + } + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) { + prefetchDescendantsOfRealNode(view, outInfos); } } else { - final AccessibilityNodeInfo root = - provider.createAccessibilityNodeInfo(virtualViewId); - if (root != null) { - if (extraDataRequested != null) { - provider.addExtraDataToAccessibilityNodeInfo( - virtualViewId, root, extraDataRequested, arguments); - } - outInfos.add(root); - if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { - prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos); - } - if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) { - prefetchSiblingsOfVirtualNode(root, view, provider, outInfos); - } - if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) { - prefetchDescendantsOfVirtualNode(root, provider, outInfos); - } + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) { + prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos); + } + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) { + prefetchSiblingsOfVirtualNode(root, view, provider, outInfos); + } + if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) { + prefetchDescendantsOfVirtualNode(root, provider, outInfos); } } if (ENFORCE_NODE_TREE_CONSISTENT) { - enforceNodeTreeConsistent(outInfos); + enforceNodeTreeConsistent(root, outInfos); } } - private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) { + private boolean shouldStopPrefetching(List prefetchededInfos) { + return mHandler.hasUserInteractiveMessagesWaiting() + || prefetchededInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE; + } + + private void enforceNodeTreeConsistent( + AccessibilityNodeInfo root, List<AccessibilityNodeInfo> nodes) { LongSparseArray<AccessibilityNodeInfo> nodeMap = new LongSparseArray<AccessibilityNodeInfo>(); final int nodeCount = nodes.size(); @@ -1114,7 +1197,6 @@ public final class AccessibilityInteractionController { // If the nodes are a tree it does not matter from // which node we start to search for the root. - AccessibilityNodeInfo root = nodeMap.valueAt(0); AccessibilityNodeInfo parent = root; while (parent != null) { root = parent; @@ -1181,9 +1263,11 @@ public final class AccessibilityInteractionController { private void prefetchPredecessorsOfRealNode(View view, List<AccessibilityNodeInfo> outInfos) { + if (shouldStopPrefetching(outInfos)) { + return; + } ViewParent parent = view.getParentForAccessibility(); - while (parent instanceof View - && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { + while (parent instanceof View && !shouldStopPrefetching(outInfos)) { View parentView = (View) parent; AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo(); if (info != null) { @@ -1195,6 +1279,9 @@ public final class AccessibilityInteractionController { private void prefetchSiblingsOfRealNode(View current, List<AccessibilityNodeInfo> outInfos) { + if (shouldStopPrefetching(outInfos)) { + return; + } ViewParent parent = current.getParentForAccessibility(); if (parent instanceof ViewGroup) { ViewGroup parentGroup = (ViewGroup) parent; @@ -1204,7 +1291,7 @@ public final class AccessibilityInteractionController { parentGroup.addChildrenForAccessibility(children); final int childCount = children.size(); for (int i = 0; i < childCount; i++) { - if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { + if (shouldStopPrefetching(outInfos)) { return; } View child = children.get(i); @@ -1232,7 +1319,7 @@ public final class AccessibilityInteractionController { private void prefetchDescendantsOfRealNode(View root, List<AccessibilityNodeInfo> outInfos) { - if (!(root instanceof ViewGroup)) { + if (shouldStopPrefetching(outInfos) || !(root instanceof ViewGroup)) { return; } HashMap<View, AccessibilityNodeInfo> addedChildren = @@ -1243,7 +1330,7 @@ public final class AccessibilityInteractionController { root.addChildrenForAccessibility(children); final int childCount = children.size(); for (int i = 0; i < childCount; i++) { - if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { + if (shouldStopPrefetching(outInfos)) { return; } View child = children.get(i); @@ -1268,7 +1355,7 @@ public final class AccessibilityInteractionController { } finally { children.clear(); } - if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { + if (!shouldStopPrefetching(outInfos)) { for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) { View addedChild = entry.getKey(); AccessibilityNodeInfo virtualRoot = entry.getValue(); @@ -1290,7 +1377,7 @@ public final class AccessibilityInteractionController { long parentNodeId = root.getParentNodeId(); int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId); while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { - if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { + if (shouldStopPrefetching(outInfos)) { return; } final int virtualDescendantId = @@ -1335,7 +1422,7 @@ public final class AccessibilityInteractionController { if (parent != null) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { - if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { + if (shouldStopPrefetching(outInfos)) { return; } final long childNodeId = parent.getChildId(i); @@ -1360,7 +1447,7 @@ public final class AccessibilityInteractionController { final int initialOutInfosSize = outInfos.size(); final int childCount = root.getChildCount(); for (int i = 0; i < childCount; i++) { - if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { + if (shouldStopPrefetching(outInfos)) { return; } final long childNodeId = root.getChildId(i); @@ -1370,7 +1457,7 @@ public final class AccessibilityInteractionController { outInfos.add(child); } } - if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { + if (!shouldStopPrefetching(outInfos)) { final int addedChildCount = outInfos.size() - initialOutInfosSize; for (int i = 0; i < addedChildCount; i++) { AccessibilityNodeInfo child = outInfos.get(initialOutInfosSize + i); @@ -1479,6 +1566,10 @@ public final class AccessibilityInteractionController { boolean hasAccessibilityCallback(Message message) { return message.what < FIRST_NO_ACCESSIBILITY_CALLBACK_MSG ? true : false; } + + boolean hasUserInteractiveMessagesWaiting() { + return hasMessagesOrCallbacks(); + } } private final class AddNodeInfosForViewId implements Predicate<View> { diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 59299f6b15eb..5c65c659e576 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -744,7 +744,10 @@ public final class Choreographer { } try { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame"); + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, + "Choreographer#doFrame " + vsyncEventData.id); + } AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); mFrameInfo.markInputHandlingStart(); diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java index ff84aa57a628..b1b670f5e0c9 100644 --- a/core/java/android/view/RemoteAnimationTarget.java +++ b/core/java/android/view/RemoteAnimationTarget.java @@ -31,7 +31,6 @@ import static android.view.RemoteAnimationTargetProto.START_BOUNDS; import static android.view.RemoteAnimationTargetProto.START_LEASH; import static android.view.RemoteAnimationTargetProto.TASK_ID; import static android.view.RemoteAnimationTargetProto.WINDOW_CONFIGURATION; -import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import android.annotation.IntDef; import android.app.PictureInPictureParams; @@ -196,30 +195,12 @@ public class RemoteAnimationTarget implements Parcelable { */ public PictureInPictureParams pictureInPictureParams; - /** - * The {@link android.view.WindowManager.LayoutParams.WindowType} of this window. It's only used - * for non-app window. - */ - public final @WindowManager.LayoutParams.WindowType int windowType; - public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent, Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position, Rect localBounds, Rect screenSpaceBounds, WindowConfiguration windowConfig, boolean isNotInRecents, SurfaceControl startLeash, Rect startBounds, PictureInPictureParams pictureInPictureParams) { - this(taskId, mode, leash, isTranslucent, clipRect, contentInsets, prefixOrderIndex, - position, localBounds, screenSpaceBounds, windowConfig, isNotInRecents, startLeash, - startBounds, pictureInPictureParams, INVALID_WINDOW_TYPE); - } - - public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent, - Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position, - Rect localBounds, Rect screenSpaceBounds, - WindowConfiguration windowConfig, boolean isNotInRecents, - SurfaceControl startLeash, Rect startBounds, - PictureInPictureParams pictureInPictureParams, - @WindowManager.LayoutParams.WindowType int windowType) { this.mode = mode; this.taskId = taskId; this.leash = leash; @@ -236,7 +217,6 @@ public class RemoteAnimationTarget implements Parcelable { this.startLeash = startLeash; this.startBounds = startBounds == null ? null : new Rect(startBounds); this.pictureInPictureParams = pictureInPictureParams; - this.windowType = windowType; } public RemoteAnimationTarget(Parcel in) { @@ -256,7 +236,6 @@ public class RemoteAnimationTarget implements Parcelable { startLeash = in.readTypedObject(SurfaceControl.CREATOR); startBounds = in.readTypedObject(Rect.CREATOR); pictureInPictureParams = in.readTypedObject(PictureInPictureParams.CREATOR); - windowType = in.readInt(); } @Override @@ -282,7 +261,6 @@ public class RemoteAnimationTarget implements Parcelable { dest.writeTypedObject(startLeash, 0 /* flags */); dest.writeTypedObject(startBounds, 0 /* flags */); dest.writeTypedObject(pictureInPictureParams, 0 /* flags */); - dest.writeInt(windowType); } public void dump(PrintWriter pw, String prefix) { @@ -296,7 +274,6 @@ public class RemoteAnimationTarget implements Parcelable { pw.print(" sourceContainerBounds="); sourceContainerBounds.printShortString(pw); pw.print(" screenSpaceBounds="); screenSpaceBounds.printShortString(pw); pw.print(" localBounds="); localBounds.printShortString(pw); - pw.print(" windowType="); pw.print(windowType); pw.println(); pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration); pw.print(prefix); pw.print("leash="); pw.println(leash); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 1819da4a86a0..7e9a850178fc 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -613,6 +613,10 @@ public interface WindowManager extends ViewManager { * For example, for activities in multi-window mode, the metrics returned are based on the * current bounds that the user has selected for the {@link android.app.Activity Activity}'s * task. + * <p> + * In most scenarios, {@link #getCurrentWindowMetrics()} rather than + * {@link #getMaximumWindowMetrics()} is the correct API to use, since it ensures values reflect + * window size when the app is not fullscreen. * * @see #getMaximumWindowMetrics() * @see WindowMetrics @@ -624,26 +628,27 @@ public interface WindowManager extends ViewManager { /** * Returns the largest {@link WindowMetrics} an app may expect in the current system state. * <p> - * The metrics describe the size of the largest potential area the window might occupy with - * {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets} - * such a window would have. - * <p> * The value of this is based on the largest <b>potential</b> windowing state of the system. * * For example, for activities in multi-window mode, the metrics returned are based on the * what the bounds would be if the user expanded the {@link android.app.Activity Activity}'s * task to cover the entire screen. - * + * <p> + * The metrics describe the size of the largest potential area the window might occupy with + * {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets} + * such a window would have. + * <p> * Note that this might still be smaller than the size of the physical display if certain areas * of the display are not available to windows created in this {@link Context}. - * <p> + * * For example, given that there's a device which have a multi-task mode to limit activities * to a half screen. In this case, {@link #getMaximumWindowMetrics()} reports the bounds of - * the half screen which the activity is located, while {@link Display#getRealSize(Point)} still - * reports the bounds of the whole physical display. - * - * Despite this, {@link #getMaximumWindowMetrics()} and {@link Display#getRealSize(Point)} - * reports the same bounds in general. + * the half screen which the activity is located. + * <p> + * <b>Generally {@link #getCurrentWindowMetrics()} is the correct API to use</b> for choosing + * UI layouts. {@link #getMaximumWindowMetrics()} are only appropriate when the application + * needs to know the largest possible size it can occupy if the user expands/maximizes it on the + * screen. * * @see #getCurrentWindowMetrics() * @see WindowMetrics diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 7cc2db1c0dab..72d403e1f867 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -149,8 +149,15 @@ public class WindowlessWindowManager implements IWindowSession { if (((attrs.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) { try { - mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags, + if(mRealWm instanceof IWindowSession.Stub) { + mRealWm.grantInputChannel(displayId, + new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"), + window, mHostInputToken, attrs.flags, attrs.privateFlags, attrs.type, + outInputChannel); + } else { + mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags, attrs.privateFlags, attrs.type, outInputChannel); + } } catch (RemoteException e) { Log.e(TAG, "Failed to grant input to surface: ", e); } @@ -293,8 +300,14 @@ public class WindowlessWindowManager implements IWindowSession { if ((attrChanges & WindowManager.LayoutParams.FLAGS_CHANGED) != 0 && state.mInputChannelToken != null) { try { - mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc, - attrs.flags, attrs.privateFlags, state.mInputRegion); + if(mRealWm instanceof IWindowSession.Stub) { + mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, + new SurfaceControl(sc, "WindowlessWindowManager.relayout"), + attrs.flags, attrs.privateFlags, state.mInputRegion); + } else { + mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc, + attrs.flags, attrs.privateFlags, state.mInputRegion); + } } catch (RemoteException e) { Log.e(TAG, "Failed to update surface input channel: ", e); } diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index f63749be6df2..8d1271d7311c 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -23,7 +23,9 @@ import android.compat.annotation.UnsupportedAppUsage; import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; @@ -113,6 +115,8 @@ public final class AccessibilityInteractionClient private final Object mInstanceLock = new Object(); + private Handler mMainHandler; + private volatile int mInteractionId = -1; private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult; @@ -123,6 +127,11 @@ public final class AccessibilityInteractionClient private Message mSameThreadMessage; + private int mInteractionIdWaitingForPrefetchResult; + private int mConnectionIdWaitingForPrefetchResult; + private String[] mPackageNamesForNextPrefetchResult; + private Runnable mPrefetchResultRunnable; + /** * @return The client for the current thread. */ @@ -197,6 +206,10 @@ public final class AccessibilityInteractionClient private AccessibilityInteractionClient() { /* reducing constructor visibility */ + Looper mainLooper = Looper.getMainLooper(); + if (mainLooper != null) { + mMainHandler = new Handler(mainLooper); + } } /** @@ -451,16 +464,16 @@ public final class AccessibilityInteractionClient Binder.restoreCallingIdentity(identityToken); } if (packageNames != null) { - List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( - interactionId); - finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, - bypassCache, packageNames); - if (infos != null && !infos.isEmpty()) { - for (int i = 1; i < infos.size(); i++) { - infos.get(i).recycle(); - } - return infos.get(0); + AccessibilityNodeInfo info = + getFindAccessibilityNodeInfoResultAndClear(interactionId); + if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_MASK) != 0 + && info != null) { + setInteractionWaitingForPrefetchResult(interactionId, connectionId, + packageNames); } + finalizeAndCacheAccessibilityNodeInfo(info, connectionId, + bypassCache, packageNames); + return info; } } else { if (DEBUG) { @@ -474,6 +487,15 @@ public final class AccessibilityInteractionClient return null; } + private void setInteractionWaitingForPrefetchResult(int interactionId, int connectionId, + String[] packageNames) { + synchronized (mInstanceLock) { + mInteractionIdWaitingForPrefetchResult = interactionId; + mConnectionIdWaitingForPrefetchResult = connectionId; + mPackageNamesForNextPrefetchResult = packageNames; + } + } + private static String idToString(int accessibilityWindowId, long accessibilityNodeId) { return accessibilityWindowId + "/" + AccessibilityNodeInfo.idToString(accessibilityNodeId); @@ -829,6 +851,59 @@ public final class AccessibilityInteractionClient } /** + * {@inheritDoc} + */ + @Override + public void setPrefetchAccessibilityNodeInfoResult(@NonNull List<AccessibilityNodeInfo> infos, + int interactionId) { + List<AccessibilityNodeInfo> infosCopy = null; + int mConnectionIdWaitingForPrefetchResultCopy = -1; + String[] mPackageNamesForNextPrefetchResultCopy = null; + + synchronized (mInstanceLock) { + if (!infos.isEmpty() && mInteractionIdWaitingForPrefetchResult == interactionId) { + if (mMainHandler != null) { + if (mPrefetchResultRunnable != null) { + mMainHandler.removeCallbacks(mPrefetchResultRunnable); + mPrefetchResultRunnable = null; + } + /** + * TODO(b/180957109): AccessibilityCache is prone to deadlocks + * We post caching the prefetched nodes in the main thread. Using the binder + * thread results in "Long monitor contention with owner main" logs where + * service response times may exceed 5 seconds. This is due to the cache calling + * out to the system when refreshing nodes with the lock held. + */ + mPrefetchResultRunnable = () -> finalizeAndCacheAccessibilityNodeInfos( + infos, mConnectionIdWaitingForPrefetchResult, false, + mPackageNamesForNextPrefetchResult); + mMainHandler.post(mPrefetchResultRunnable); + + } else { + for (AccessibilityNodeInfo info : infos) { + infosCopy.add(new AccessibilityNodeInfo(info)); + } + mConnectionIdWaitingForPrefetchResultCopy = + mConnectionIdWaitingForPrefetchResult; + mPackageNamesForNextPrefetchResultCopy = + new String[mPackageNamesForNextPrefetchResult.length]; + for (int i = 0; i < mPackageNamesForNextPrefetchResult.length; i++) { + mPackageNamesForNextPrefetchResultCopy[i] = + mPackageNamesForNextPrefetchResult[i]; + } + } + } + + } + + if (infosCopy != null) { + finalizeAndCacheAccessibilityNodeInfos( + infosCopy, mConnectionIdWaitingForPrefetchResultCopy, false, + mPackageNamesForNextPrefetchResultCopy); + } + } + + /** * Gets the result of a request to perform an accessibility action. * * @param interactionId The interaction id to match the result with the request. diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl index 049bb31adbb1..231e75a19a06 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl @@ -47,6 +47,15 @@ oneway interface IAccessibilityInteractionConnectionCallback { int interactionId); /** + * Sets the result of a prefetch request that returns {@link AccessibilityNodeInfo}s. + * + * @param root The {@link AccessibilityNodeInfo} for which the prefetching is based off of. + * @param infos The result {@link AccessibilityNodeInfo}s. + */ + void setPrefetchAccessibilityNodeInfoResult( + in List<AccessibilityNodeInfo> infos, int interactionId); + + /** * Sets the result of a request to perform an accessibility action. * * @param Whether the action was performed. diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 2cf50bbc6793..dfb2263a97a7 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -51,7 +51,6 @@ import android.content.res.loader.ResourcesProvider; import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Outline; -import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -76,6 +75,7 @@ import android.util.DisplayMetrics; import android.util.IntArray; import android.util.Log; import android.util.Pair; +import android.util.SizeF; import android.util.SparseIntArray; import android.util.TypedValue; import android.util.TypedValue.ComplexDimensionUnit; @@ -353,7 +353,7 @@ public class RemoteViews implements Parcelable, Filter { * Only to be used on children views used in a {@link RemoteViews} with * {@link RemoteViews#hasSizedRemoteViews()}. */ - private PointF mIdealSize = null; + private SizeF mIdealSize = null; @ApplyFlags private int mApplyFlags = 0; @@ -3042,11 +3042,11 @@ public class RemoteViews implements Parcelable, Filter { return mSizedRemoteViews != null; } - private @Nullable PointF getIdealSize() { + private @Nullable SizeF getIdealSize() { return mIdealSize; } - private void setIdealSize(@Nullable PointF size) { + private void setIdealSize(@Nullable SizeF size) { mIdealSize = size; } @@ -3094,13 +3094,18 @@ public class RemoteViews implements Parcelable, Filter { * Create a new RemoteViews object that will inflate the layout with the closest size * specification. * - * The default remote views in that case is always the smallest one provided. + * The default remote views in that case is always the one with the smallest area. + * + * If the {@link RemoteViews} host provides the size of the view, the layout with the largest + * area that fits entirely in the provided size will be used (i.e. the width and height of + * the layout must be less than the size of the view, with a 1dp margin to account for + * rounding). If no layout fits in the view, the layout with the smallest area will be used. * * @param remoteViews Mapping of size to layout. * @throws IllegalArgumentException if the map is empty, there are more than * MAX_INIT_VIEW_COUNT layouts or the remote views are not all from the same application. */ - public RemoteViews(@NonNull Map<PointF, RemoteViews> remoteViews) { + public RemoteViews(@NonNull Map<SizeF, RemoteViews> remoteViews) { if (remoteViews.isEmpty()) { throw new IllegalArgumentException("The set of RemoteViews cannot be empty"); } @@ -3135,8 +3140,8 @@ public class RemoteViews implements Parcelable, Filter { RemoteViews smallestView = null; while (remoteViews.hasNext()) { RemoteViews view = remoteViews.next(); - PointF size = view.getIdealSize(); - float newViewArea = size.x * size.y; + SizeF size = view.getIdealSize(); + float newViewArea = size.getWidth() * size.getHeight(); if (smallestView != null && !view.hasSameAppInfo(smallestView.mApplication)) { throw new IllegalArgumentException( "All RemoteViews must share the same package and user"); @@ -3239,7 +3244,7 @@ public class RemoteViews implements Parcelable, Filter { if (mode == MODE_NORMAL) { mApplication = parcel.readInt() == 0 ? info : ApplicationInfo.CREATOR.createFromParcel(parcel); - mIdealSize = parcel.readInt() == 0 ? null : PointF.CREATOR.createFromParcel(parcel); + mIdealSize = parcel.readInt() == 0 ? null : SizeF.CREATOR.createFromParcel(parcel); mLayoutId = parcel.readInt(); mLightBackgroundLayoutId = parcel.readInt(); @@ -4625,9 +4630,9 @@ public class RemoteViews implements Parcelable, Filter { * * This is particularly useful when we only care about the ordering of the distances. */ - private static float squareDistance(PointF p1, PointF p2) { - float dx = p1.x - p2.x; - float dy = p1.y - p2.y; + private static float squareDistance(SizeF p1, SizeF p2) { + float dx = p1.getWidth() - p2.getWidth(); + float dy = p1.getHeight() - p2.getHeight(); return dx * dx + dy * dy; } @@ -4637,31 +4642,17 @@ public class RemoteViews implements Parcelable, Filter { * A layout fits on a widget if the widget size is known (i.e. not null) and both dimensions * are smaller than the ones of the widget, adding some padding to account for rounding errors. */ - private static boolean fitsIn(PointF sizeLayout, @Nullable PointF sizeWidget) { - return sizeWidget != null && (Math.ceil(sizeWidget.x) + 1 > sizeLayout.x) - && (Math.ceil(sizeWidget.y) + 1 > sizeLayout.y); + private static boolean fitsIn(SizeF sizeLayout, @Nullable SizeF sizeWidget) { + return sizeWidget != null && (Math.ceil(sizeWidget.getWidth()) + 1 > sizeLayout.getWidth()) + && (Math.ceil(sizeWidget.getHeight()) + 1 > sizeLayout.getHeight()); } - /** - * Returns the most appropriate {@link RemoteViews} given the context and, if not null, the - * size of the widget. - * - * If {@link RemoteViews#hasSizedRemoteViews()} returns true, the most appropriate view is - * the one that fits in the widget (according to {@link RemoteViews#fitsIn}) and has the - * diagonal the most similar to the widget. If no layout fits or the size of the widget is - * not specified, the one with the smallest area will be chosen. - */ - private RemoteViews getRemoteViewsToApply(@NonNull Context context, - @Nullable PointF widgetSize) { - if (!hasSizedRemoteViews()) { - // If there isn't multiple remote views, fall back on the previous methods. - return getRemoteViewsToApply(context); - } + private RemoteViews findBestFitLayout(@NonNull SizeF widgetSize) { // Find the better remote view RemoteViews bestFit = null; float bestSqDist = Float.MAX_VALUE; for (RemoteViews layout : mSizedRemoteViews) { - PointF layoutSize = layout.getIdealSize(); + SizeF layoutSize = layout.getIdealSize(); if (fitsIn(layoutSize, widgetSize)) { if (bestFit == null) { bestFit = layout; @@ -4682,6 +4673,46 @@ public class RemoteViews implements Parcelable, Filter { return bestFit; } + /** + * Returns the most appropriate {@link RemoteViews} given the context and, if not null, the + * size of the widget. + * + * If {@link RemoteViews#hasSizedRemoteViews()} returns true, the most appropriate view is + * the one that fits in the widget (according to {@link RemoteViews#fitsIn}) and has the + * diagonal the most similar to the widget. If no layout fits or the size of the widget is + * not specified, the one with the smallest area will be chosen. + * + * @hide + */ + public RemoteViews getRemoteViewsToApply(@NonNull Context context, + @Nullable SizeF widgetSize) { + if (!hasSizedRemoteViews()) { + // If there isn't multiple remote views, fall back on the previous methods. + return getRemoteViewsToApply(context); + } + return findBestFitLayout(widgetSize); + } + + /** + * Checks whether the change of size will lead to using a different {@link RemoteViews}. + * + * @hide + */ + @Nullable + public RemoteViews getRemoteViewsToApplyIfDifferent(@Nullable SizeF oldSize, + @NonNull SizeF newSize) { + if (!hasSizedRemoteViews()) { + return null; + } + RemoteViews oldBestFit = oldSize == null ? findSmallestRemoteView() : findBestFitLayout( + oldSize); + RemoteViews newBestFit = findBestFitLayout(newSize); + if (oldBestFit != newBestFit) { + return newBestFit; + } + return null; + } + /** * Inflates the view hierarchy represented by this object and applies @@ -4705,7 +4736,7 @@ public class RemoteViews implements Parcelable, Filter { /** @hide */ public View apply(@NonNull Context context, @NonNull ViewGroup parent, - @Nullable InteractionHandler handler, @Nullable PointF size) { + @Nullable InteractionHandler handler, @Nullable SizeF size) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); View result = inflateView(context, rvToApply, parent); @@ -4722,7 +4753,7 @@ public class RemoteViews implements Parcelable, Filter { /** @hide */ public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent, @Nullable InteractionHandler handler, @StyleRes int applyThemeResId, - @Nullable PointF size) { + @Nullable SizeF size) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); View result = inflateView(context, rvToApply, parent, applyThemeResId, null); @@ -4732,7 +4763,7 @@ public class RemoteViews implements Parcelable, Filter { /** @hide */ public View apply(Context context, ViewGroup parent, InteractionHandler handler, - @NonNull PointF size, @Nullable ColorResources colorResources) { + @NonNull SizeF size, @Nullable ColorResources colorResources) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); View result = inflateView(context, rvToApply, parent, 0, colorResources); @@ -4828,21 +4859,21 @@ public class RemoteViews implements Parcelable, Filter { /** @hide */ public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener, InteractionHandler handler, - PointF size) { + SizeF size) { return getAsyncApplyTask(context, parent, listener, handler, size, null /* themeColors */) .startTaskOnExecutor(executor); } /** @hide */ public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, - OnViewAppliedListener listener, InteractionHandler handler, PointF size, + OnViewAppliedListener listener, InteractionHandler handler, SizeF size, ColorResources colorResources) { return getAsyncApplyTask(context, parent, listener, handler, size, colorResources) .startTaskOnExecutor(executor); } private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent, - OnViewAppliedListener listener, InteractionHandler handler, PointF size, + OnViewAppliedListener listener, InteractionHandler handler, SizeF size, ColorResources colorResources) { return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener, handler, colorResources, null /* result */); @@ -4968,7 +4999,7 @@ public class RemoteViews implements Parcelable, Filter { } /** @hide */ - public void reapply(Context context, View v, InteractionHandler handler, PointF size, + public void reapply(Context context, View v, InteractionHandler handler, SizeF size, ColorResources colorResources) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); @@ -5012,7 +5043,7 @@ public class RemoteViews implements Parcelable, Filter { /** @hide */ public CancellationSignal reapplyAsync(Context context, View v, Executor executor, - OnViewAppliedListener listener, InteractionHandler handler, PointF size, + OnViewAppliedListener listener, InteractionHandler handler, SizeF size, ColorResources colorResources) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java index 4421f06460a0..141f47b130d1 100644 --- a/core/java/android/window/TransitionFilter.java +++ b/core/java/android/window/TransitionFilter.java @@ -139,8 +139,8 @@ public final class TransitionFilter implements Parcelable { boolean matches(@NonNull TransitionInfo info) { for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getParent() != null) { - // Only look at the top animating windows. + if (!TransitionInfo.isIndependent(change, info)) { + // Only look at independent animating windows. continue; } if (mActivityType != ACTIVITY_TYPE_UNDEFINED) { diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index d1d49b6d4722..499ce25f8bb9 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -246,6 +246,33 @@ public final class TransitionInfo implements Parcelable { return sb.toString(); } + /** + * Indication that `change` is independent of parents (ie. it has a different type of + * transition vs. "going along for the ride") + */ + public static boolean isIndependent(TransitionInfo.Change change, TransitionInfo info) { + // If the change has no parent (it is root), then it is independent + if (change.getParent() == null) return true; + + // non-visibility changes will just be folded into the parent change, so they aren't + // independent either. + if (change.getMode() == TRANSIT_CHANGE) return false; + + TransitionInfo.Change parentChg = info.getChange(change.getParent()); + while (parentChg != null) { + // If the parent is a visibility change, it will include the results of all child + // changes into itself, so none of its children can be independent. + if (parentChg.getMode() != TRANSIT_CHANGE) return false; + + // If there are no more parents left, then all the parents, so far, have not been + // visibility changes which means this change is indpendent. + if (parentChg.getParent() == null) return true; + + parentChg = info.getChange(parentChg.getParent()); + } + return false; + } + /** Represents the change a WindowContainer undergoes during a transition */ public static final class Change implements Parcelable { private final WindowContainerToken mContainer; diff --git a/core/java/com/android/internal/graphics/palette/WuQuantizer.java b/core/java/com/android/internal/graphics/palette/WuQuantizer.java index 01e45f613986..66206bf8297a 100644 --- a/core/java/com/android/internal/graphics/palette/WuQuantizer.java +++ b/core/java/com/android/internal/graphics/palette/WuQuantizer.java @@ -16,7 +16,6 @@ package com.android.internal.graphics.palette; - import java.util.ArrayList; import java.util.List; @@ -120,7 +119,11 @@ public class WuQuantizer implements Quantizer { } } - for (k = 0; k < mMaxColors; ++k) { + // If extraction is run on a set of pixels whose count is less than the + // number of max colors, then colors.length < max colors, and accesses + // to colors[index] inside the for loop throw an ArrayOutOfBoundsException. + int numColorsToCreate = (int) Math.min(mMaxColors, colors.length); + for (k = 0; k < numColorsToCreate; ++k) { weight = getVolume(cube[k], mWt); if (weight > 0) { red = (int) (getVolume(cube[k], mMr) / weight); diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS index 0aa54f556b92..3f01ebb1afe7 100644 --- a/core/java/com/android/internal/os/OWNERS +++ b/core/java/com/android/internal/os/OWNERS @@ -6,6 +6,7 @@ per-file *Cpu* = file:CPU_OWNERS per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS per-file BatteryStats* = file:/BATTERY_STATS_OWNERS per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS +per-file *ChargeCalculator* = file:/BATTERY_STATS_OWNERS per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index ec41a47a8798..74a37cadc54c 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -628,6 +628,7 @@ message ActivityManagerServiceDumpProcessesProto { optional string tag = 3; optional int32 type = 4; optional int32 reason_code = 5; + optional int32 calling_uid = 6; } repeated PendingTempWhitelist pending_temp_whitelist = 26; diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 100983bcef0d..efc8fe9aafa2 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8424,6 +8424,9 @@ <!-- Fully qualified class name of an activity that allows the user to modify the settings for this service. --> <attr name="settingsActivity" /> + <!-- Fully qualified class name of an activity that allows the user to view any passwords + saved by this service. --> + <attr name="passwordsActivity" format="string" /> <!-- Specifies whether the AutofillService supports inline suggestions--> <attr name="supportsInlineSuggestions" format="boolean" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 633e2161b5ee..f38264c41211 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4668,11 +4668,11 @@ <!-- WindowsManager JetPack display features --> <string name="config_display_features" translatable="false" /> - <!-- Aspect ratio of task level letterboxing. Values <= 1.0 will be ignored. - Note: Activity min/max aspect ratio restrictions will still be respected by the - activity-level letterboxing (size-compat mode). Therefore this override can control the - maximum screen area that can be occupied by the app in the letterbox mode. --> - <item name="config_taskLetterboxAspectRatio" format="float" type="dimen">0.0</item> + <!-- Aspect ratio of letterboxing for fixed orientation. Values <= 1.0 will be ignored. + Note: Activity min/max aspect ratio restrictions will still be respected. + Therefore this override can control the maximum screen area that can be occupied by + the app in the letterbox mode. --> + <item name="config_fixedOrientationLetterboxAspectRatio" format="float" type="dimen">0.0</item> <!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and corners of the activity won't be rounded. --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 9bc92e14de53..7702ee437c43 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3085,6 +3085,7 @@ <public name="hand_secondTint"/> <public name="hand_secondTintMode"/> <public name="dataExtractionRules"/> + <public name="passwordsActivity"/> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ff9d26fb2363..2fff7b5f6298 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4181,7 +4181,7 @@ <java-symbol type="dimen" name="controls_thumbnail_image_max_height" /> <java-symbol type="dimen" name="controls_thumbnail_image_max_width" /> - <java-symbol type="dimen" name="config_taskLetterboxAspectRatio" /> + <java-symbol type="dimen" name="config_fixedOrientationLetterboxAspectRatio" /> <java-symbol type="integer" name="config_letterboxActivityCornersRadius" /> <java-symbol type="integer" name="config_letterboxBackgroundType" /> <java-symbol type="color" name="config_letterboxBackgroundColor" /> diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java index 0fe44630f308..9e8837386128 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java @@ -21,6 +21,8 @@ import static com.google.common.truth.Truth.assertThat; import android.os.Bundle; +import com.google.common.collect.ImmutableList; + import org.junit.Test; import java.util.List; @@ -67,9 +69,9 @@ public class SearchSpecTest { SearchSpec searchSpec = new SearchSpec.Builder() .setTermMatch(SearchSpec.TERM_MATCH_PREFIX) - .addProjection("TypeA", "field1", "field2.subfield2") - .addProjection("TypeB", "field7") - .addProjection("TypeC") + .addProjection("TypeA", ImmutableList.of("field1", "field2.subfield2")) + .addProjection("TypeB", ImmutableList.of("field7")) + .addProjection("TypeC", ImmutableList.of()) .build(); Map<String, List<String>> typePropertyPathMap = searchSpec.getProjections(); diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java index bf6b07f71839..677f4bd9e46d 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java @@ -42,13 +42,13 @@ public class SetSchemaRequestTest { } @Test - public void testInvalidSchemaReferences_fromSystemUiVisibility() { + public void testInvalidSchemaReferences_fromDisplayedBySystem() { IllegalArgumentException expected = expectThrows( IllegalArgumentException.class, () -> new SetSchemaRequest.Builder() - .setSchemaTypeVisibilityForSystemUi("InvalidSchema", false) + .setSchemaTypeDisplayedBySystem("InvalidSchema", false) .build()); assertThat(expected).hasMessageThat().contains("referenced, but were not added"); } @@ -71,30 +71,30 @@ public class SetSchemaRequestTest { } @Test - public void testSchemaTypeVisibilityForSystemUi_visible() { + public void testSetSchemaTypeDisplayedBySystem_displayed() { AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); - // By default, the schema is visible. + // By default, the schema is displayed. SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build(); - assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty(); + assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty(); request = new SetSchemaRequest.Builder() .addSchemas(schema) - .setSchemaTypeVisibilityForSystemUi("Schema", true) + .setSchemaTypeDisplayedBySystem("Schema", true) .build(); - assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty(); + assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty(); } @Test - public void testSchemaTypeVisibilityForSystemUi_notVisible() { + public void testSetSchemaTypeDisplayedBySystem_notDisplayed() { AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); SetSchemaRequest request = new SetSchemaRequest.Builder() .addSchemas(schema) - .setSchemaTypeVisibilityForSystemUi("Schema", false) + .setSchemaTypeDisplayedBySystem("Schema", false) .build(); - assertThat(request.getSchemasNotVisibleToSystemUi()).containsExactly("Schema"); + assertThat(request.getSchemasNotDisplayedBySystem()).containsExactly("Schema"); } @Test diff --git a/core/tests/coretests/src/android/os/BytesMatcherTest.java b/core/tests/coretests/src/android/os/BytesMatcherTest.java new file mode 100644 index 000000000000..67c1b3c947fb --- /dev/null +++ b/core/tests/coretests/src/android/os/BytesMatcherTest.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 static com.android.internal.util.HexDump.hexStringToByteArray; + +import android.bluetooth.BluetoothUuid; +import android.net.MacAddress; + +import androidx.test.filters.SmallTest; + +import junit.framework.TestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +@SmallTest +public class BytesMatcherTest extends TestCase { + @Test + public void testEmpty() throws Exception { + BytesMatcher matcher = BytesMatcher.decode(""); + assertFalse(matcher.test(hexStringToByteArray("cafe"))); + assertFalse(matcher.test(hexStringToByteArray(""))); + } + + @Test + public void testExact() throws Exception { + BytesMatcher matcher = BytesMatcher.decode("+cafe"); + assertTrue(matcher.test(hexStringToByteArray("cafe"))); + assertFalse(matcher.test(hexStringToByteArray("beef"))); + assertFalse(matcher.test(hexStringToByteArray("ca"))); + assertFalse(matcher.test(hexStringToByteArray("cafe00"))); + } + + @Test + public void testMask() throws Exception { + BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00"); + assertTrue(matcher.test(hexStringToByteArray("cafe"))); + assertTrue(matcher.test(hexStringToByteArray("ca88"))); + assertFalse(matcher.test(hexStringToByteArray("beef"))); + assertFalse(matcher.test(hexStringToByteArray("ca"))); + assertFalse(matcher.test(hexStringToByteArray("cafe00"))); + } + + @Test + public void testMacAddress() throws Exception { + BytesMatcher matcher = BytesMatcher.decode("+cafe00112233/ffffff000000"); + assertTrue(matcher.testMacAddress( + MacAddress.fromString("ca:fe:00:00:00:00"))); + assertFalse(matcher.testMacAddress( + MacAddress.fromString("f0:0d:00:00:00:00"))); + } + + @Test + public void testBluetoothUuid() throws Exception { + BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00"); + assertTrue(matcher.testBluetoothUuid( + BluetoothUuid.parseUuidFrom(hexStringToByteArray("cafe")))); + assertFalse(matcher.testBluetoothUuid( + BluetoothUuid.parseUuidFrom(hexStringToByteArray("beef")))); + } + + /** + * Verify that single matcher can be configured to match Bluetooth UUIDs of + * varying lengths. + */ + @Test + public void testBluetoothUuid_Mixed() throws Exception { + BytesMatcher matcher = BytesMatcher.decode("+aaaa/ff00,+bbbbbbbb/ffff0000"); + assertTrue(matcher.testBluetoothUuid( + BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaa")))); + assertFalse(matcher.testBluetoothUuid( + BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbb")))); + assertTrue(matcher.testBluetoothUuid( + BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbbbbbb")))); + assertFalse(matcher.testBluetoothUuid( + BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaaaaaa")))); + } + + @Test + public void testSerialize() throws Exception { + BytesMatcher matcher = new BytesMatcher(); + matcher.addRejectRule(hexStringToByteArray("cafe00112233"), + hexStringToByteArray("ffffff000000")); + matcher.addRejectRule(hexStringToByteArray("beef00112233"), + null); + matcher.addAcceptRule(hexStringToByteArray("000000000000"), + hexStringToByteArray("000000000000")); + + assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff"))); + assertFalse(matcher.test(hexStringToByteArray("beef00112233"))); + assertTrue(matcher.test(hexStringToByteArray("beef00ffffff"))); + + // Bounce through serialization pass and confirm it still works + matcher = BytesMatcher.decode(BytesMatcher.encode(matcher)); + + assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff"))); + assertFalse(matcher.test(hexStringToByteArray("beef00112233"))); + assertTrue(matcher.test(hexStringToByteArray("beef00ffffff"))); + } + + @Test + public void testOrdering_RejectFirst() throws Exception { + BytesMatcher matcher = BytesMatcher.decode("-ff/0f,+ff/f0"); + assertFalse(matcher.test(hexStringToByteArray("ff"))); + assertTrue(matcher.test(hexStringToByteArray("f0"))); + assertFalse(matcher.test(hexStringToByteArray("0f"))); + } + + @Test + public void testOrdering_AcceptFirst() throws Exception { + BytesMatcher matcher = BytesMatcher.decode("+ff/f0,-ff/0f"); + assertTrue(matcher.test(hexStringToByteArray("ff"))); + assertTrue(matcher.test(hexStringToByteArray("f0"))); + assertFalse(matcher.test(hexStringToByteArray("0f"))); + } +} diff --git a/core/tests/coretests/src/android/security/CredentialManagementAppTest.java b/core/tests/coretests/src/android/security/CredentialManagementAppTest.java index 366aabd183e3..fa824b140caa 100644 --- a/core/tests/coretests/src/android/security/CredentialManagementAppTest.java +++ b/core/tests/coretests/src/android/security/CredentialManagementAppTest.java @@ -37,8 +37,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Iterator; -import java.util.Map; @SmallTest @RunWith(AndroidJUnit4.class) @@ -149,37 +147,6 @@ public final class CredentialManagementAppTest { private void assertCredentialManagementAppsEqual(CredentialManagementApp actual, CredentialManagementApp expected) { assertThat(actual.getPackageName(), is(expected.getPackageName())); - assertAuthenticationPoliciesEqual(actual.getAuthenticationPolicy(), - expected.getAuthenticationPolicy()); - } - - private void assertAuthenticationPoliciesEqual(AppUriAuthenticationPolicy actual, - AppUriAuthenticationPolicy expected) { - Iterator<Map.Entry<String, Map<Uri, String>>> actualIter = - actual.getAppAndUriMappings().entrySet().iterator(); - Iterator<Map.Entry<String, Map<Uri, String>>> expectedIter = - expected.getAppAndUriMappings().entrySet().iterator(); - - assertThat(actual.getAppAndUriMappings().size(), - is(expected.getAppAndUriMappings().size())); - while (actualIter.hasNext()) { - Map.Entry<String, Map<Uri, String>> actualAppToUri = actualIter.next(); - Map.Entry<String, Map<Uri, String>> expectedAppToUri = expectedIter.next(); - assertThat(actualAppToUri.getKey(), is(expectedAppToUri.getKey())); - assertUrisToAliasesEqual(actualAppToUri.getValue(), expectedAppToUri.getValue()); - } - } - - private void assertUrisToAliasesEqual(Map<Uri, String> actual, Map<Uri, String> expected) { - Iterator<Map.Entry<Uri, String>> actualIter = actual.entrySet().iterator(); - Iterator<Map.Entry<Uri, String>> expectedIter = expected.entrySet().iterator(); - - assertThat(actual.size(), is(expected.size())); - while (actualIter.hasNext()) { - Map.Entry<Uri, String> actualUriToAlias = actualIter.next(); - Map.Entry<Uri, String> expectedUriToAlias = expectedIter.next(); - assertThat(actualUriToAlias.getKey(), is(expectedUriToAlias.getKey())); - assertThat(actualUriToAlias.getValue(), is(expectedUriToAlias.getValue())); - } + assertThat(actual.getAuthenticationPolicy(), is(expected.getAuthenticationPolicy())); } } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java index ab24f89015c7..7e1e7f4bdd7f 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java @@ -33,9 +33,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import java.util.Arrays; -import java.util.List; - /** * Tests for AccessibilityInteractionClient */ @@ -65,7 +62,7 @@ public class AccessibilityInteractionClientTest { final long accessibilityNodeId = 0x4321L; AccessibilityNodeInfo nodeFromConnection = AccessibilityNodeInfo.obtain(); nodeFromConnection.setSourceNodeId(accessibilityNodeId, windowId); - mMockConnection.mInfosToReturn = Arrays.asList(nodeFromConnection); + mMockConnection.mInfoToReturn = nodeFromConnection; AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); AccessibilityNodeInfo node = client.findAccessibilityNodeInfoByAccessibilityId( MOCK_CONNECTION_ID, windowId, accessibilityNodeId, true, 0, null); @@ -75,7 +72,7 @@ public class AccessibilityInteractionClientTest { } private static class MockConnection extends AccessibilityServiceConnectionImpl { - List<AccessibilityNodeInfo> mInfosToReturn; + AccessibilityNodeInfo mInfoToReturn; @Override public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, @@ -83,7 +80,7 @@ public class AccessibilityInteractionClientTest { IAccessibilityInteractionConnectionCallback callback, int flags, long threadId, Bundle arguments) { try { - callback.setFindAccessibilityNodeInfosResult(mInfosToReturn, interactionId); + callback.setFindAccessibilityNodeInfoResult(mInfoToReturn, interactionId); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index 2f41b9084f40..89644e2320c1 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -146,6 +146,13 @@ prebuilt_etc { } prebuilt_etc { + name: "allowed_privapp_com.android.car.rotary", + sub_dir: "permissions", + src: "com.android.car.rotary.xml", + filename_from_src: true, +} + +prebuilt_etc { name: "allowed_privapp_com.android.car.ui.paintbooth", sub_dir: "permissions", src: "com.android.car.ui.paintbooth.xml", diff --git a/data/etc/car/com.android.car.rotary.xml b/data/etc/car/com.android.car.rotary.xml new file mode 100644 index 000000000000..eddef1acbbc7 --- /dev/null +++ b/data/etc/car/com.android.car.rotary.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<permissions> + <privapp-permissions package="com.android.car.rotary"> + <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> +</permissions> + diff --git a/keystore/java/android/security/AppUriAuthenticationPolicy.java b/keystore/java/android/security/AppUriAuthenticationPolicy.java index 0244ce97c0d4..df79912128fe 100644 --- a/keystore/java/android/security/AppUriAuthenticationPolicy.java +++ b/keystore/java/android/security/AppUriAuthenticationPolicy.java @@ -238,4 +238,21 @@ public final class AppUriAuthenticationPolicy implements Parcelable { return aliases; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AppUriAuthenticationPolicy)) { + return false; + } + AppUriAuthenticationPolicy other = (AppUriAuthenticationPolicy) obj; + return Objects.equals(mAppToUris, other.mAppToUris); + } + + @Override + public int hashCode() { + return mAppToUris.hashCode(); + } + } diff --git a/keystore/java/android/security/UrisToAliases.java b/keystore/java/android/security/UrisToAliases.java index 65d433abe166..9a8b659f3db4 100644 --- a/keystore/java/android/security/UrisToAliases.java +++ b/keystore/java/android/security/UrisToAliases.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * The mapping from URI to alias, which determines the alias to use when the user visits a URI. @@ -135,4 +136,21 @@ public final class UrisToAliases implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeMap(mUrisToAliases); } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof UrisToAliases)) { + return false; + } + UrisToAliases other = (UrisToAliases) obj; + return Objects.equals(mUrisToAliases, other.mUrisToAliases); + } + + @Override + public int hashCode() { + return mUrisToAliases.hashCode(); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java index 05526018d73f..f4c0f9384705 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java @@ -52,6 +52,9 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { private final SyncTransactionQueue mSyncQueue; private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>(); + // TODO(shell-transitions): Remove when switched to shell-transitions. + private final SparseArray<Point> mPositionByTaskId = new SparseArray<>(); + RunningTaskInfo mPrimary; RunningTaskInfo mSecondary; SurfaceControl mPrimarySurface; @@ -167,6 +170,7 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { @Override public void onTaskVanished(RunningTaskInfo taskInfo) { synchronized (this) { + mPositionByTaskId.remove(taskInfo.taskId); if (taskInfo.hasParentTask()) { mLeashByTaskId.remove(taskInfo.taskId); return; @@ -200,16 +204,24 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { } synchronized (this) { if (taskInfo.hasParentTask()) { + // changed messages are noisy since it reports on every ensureVisibility. This + // conflicts with legacy app-transitions which "swaps" the position to a + // leash. For now, only update when position actually changes to avoid + // poorly-timed duplicate calls. + if (taskInfo.positionInParent.equals(mPositionByTaskId.get(taskInfo.taskId))) { + return; + } handleChildTaskChanged(taskInfo); - return; + } else { + handleTaskInfoChanged(taskInfo); } - - handleTaskInfoChanged(taskInfo); + mPositionByTaskId.put(taskInfo.taskId, new Point(taskInfo.positionInParent)); } } private void handleChildTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { mLeashByTaskId.put(taskInfo.taskId, leash); + mPositionByTaskId.put(taskInfo.taskId, new Point(taskInfo.positionInParent)); if (Transitions.ENABLE_SHELL_TRANSITIONS) return; updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 843e27f1b651..7ed7fd0096bd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -172,6 +172,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, }; private ActivityManager.RunningTaskInfo mTaskInfo; + // To handle the edge case that onTaskInfoChanged callback is received during the entering + // PiP transition, where we do not want to intercept the transition but still want to apply the + // changed RunningTaskInfo when it finishes. + private ActivityManager.RunningTaskInfo mDeferredTaskInfo; private WindowContainerToken mToken; private SurfaceControl mLeash; private State mState = State.UNDEFINED; @@ -520,12 +524,18 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipTransitionController.sendOnPipTransitionStarted(direction); } - private void sendOnPipTransitionFinished( + @VisibleForTesting + void sendOnPipTransitionFinished( @PipAnimationController.TransitionDirection int direction) { if (direction == TRANSITION_DIRECTION_TO_PIP) { mState = State.ENTERED_PIP; } mPipTransitionController.sendOnPipTransitionFinished(direction); + // Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent. + if (direction == TRANSITION_DIRECTION_TO_PIP && mDeferredTaskInfo != null) { + onTaskInfoChanged(mDeferredTaskInfo); + mDeferredTaskInfo = null; + } } private void sendOnPipTransitionCancelled( @@ -567,6 +577,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken"); + if (mState != State.ENTERED_PIP) { + Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState); + mDeferredTaskInfo = info; + return; + } mPipBoundsState.setLastPipComponentName(info.topActivity); mPipBoundsState.setOverrideMinSize( mPipBoundsAlgorithm.getMinimalSize(info.topActivityInfo)); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 2182ee5590e1..629ff0db6b4a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -97,8 +97,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); - // Don't animate anything with an animating parent - if (change.getParent() != null) continue; + // Don't animate anything that isn't independent. + if (!TransitionInfo.isIndependent(change, info)) continue; Animation a = loadAnimation(info.getType(), change); if (a != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 89eee67bf5af..677db10d07a7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -214,8 +214,8 @@ public class Transitions { final SurfaceControl leash = change.getLeash(); final int mode = info.getChanges().get(i).getMode(); - // Don't move anything with an animating parent - if (change.getParent() != null) { + // Don't move anything that isn't independent within its parents + if (!TransitionInfo.isIndependent(change, info)) { if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) { t.show(leash); t.setMatrix(leash, 1, 0, 0, 1); @@ -225,9 +225,13 @@ public class Transitions { continue; } - t.reparent(leash, info.getRootLeash()); - t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x, - change.getStartAbsBounds().top - info.getRootOffset().y); + boolean hasParent = change.getParent() != null; + + if (!hasParent) { + t.reparent(leash, info.getRootLeash()); + t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x, + change.getStartAbsBounds().top - info.getRootOffset().y); + } // Put all the OPEN/SHOW on top if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { t.show(leash); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 79ec624a1557..195b701a1c26 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -16,6 +16,8 @@ package com.android.wm.shell.pip; +import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; + import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -162,11 +164,29 @@ public class PipTaskOrganizerTest extends ShellTestCase { } @Test - public void onTaskInfoChanged_updatesAspectRatioIfChanged() { + public void onTaskInfoChanged_notInPip_deferUpdatesAspectRatio() { + final Rational startAspectRatio = new Rational(2, 1); + final Rational newAspectRatio = new Rational(1, 2); + mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, + createPipParams(startAspectRatio)), null /* leash */); + + // It is in entering transition, should defer onTaskInfoChanged callback in this case. + mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1, + createPipParams(newAspectRatio))); + assertEquals(startAspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f); + + // Once the entering transition finishes, the new aspect ratio applies in a deferred manner + mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); + assertEquals(newAspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f); + } + + @Test + public void onTaskInfoChanged_inPip_updatesAspectRatioIfChanged() { final Rational startAspectRatio = new Rational(2, 1); final Rational newAspectRatio = new Rational(1, 2); mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(startAspectRatio)), null /* leash */); + mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1, createPipParams(newAspectRatio))); @@ -175,9 +195,10 @@ public class PipTaskOrganizerTest extends ShellTestCase { } @Test - public void onTaskInfoChanged_updatesLastPipComponentName() { + public void onTaskInfoChanged_inPip_updatesLastPipComponentName() { mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)), null /* leash */); + mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2, createPipParams(null))); @@ -186,9 +207,10 @@ public class PipTaskOrganizerTest extends ShellTestCase { } @Test - public void onTaskInfoChanged_updatesOverrideMinSize() { + public void onTaskInfoChanged_inPip_updatesOverrideMinSize() { mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)), null /* leash */); + mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); final Size minSize = new Size(400, 320); mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2, diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index c9146b2fc2d1..3408ffda3f9d 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -17,7 +17,7 @@ #include "DrawFrameTask.h" #include <utils/Log.h> -#include <utils/Trace.h> +#include <utils/TraceUtils.h> #include "../DeferredLayerUpdater.h" #include "../DisplayList.h" @@ -82,7 +82,8 @@ void DrawFrameTask::postAndWait() { } void DrawFrameTask::run() { - ATRACE_NAME("DrawFrame"); + const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)]; + ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId); bool canUnblockUiThread; bool canDrawThisFrame; diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index f5b204a4a908..5656dffe1d4a 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -16,6 +16,7 @@ package android.media; +import android.annotation.CallbackExecutor; import android.annotation.IntRange; import android.annotation.NonNull; import android.graphics.GraphicBuffer; @@ -24,6 +25,7 @@ import android.graphics.ImageFormat.Format; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.hardware.HardwareBuffer.Usage; +import android.hardware.camera2.MultiResolutionImageReader; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -36,7 +38,9 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.NioUtils; import java.util.List; +import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -134,7 +138,8 @@ public class ImageReader implements AutoCloseable { // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not // work, and is inscrutable anyway return new ImageReader(width, height, format, maxImages, - format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN); + format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN, + /*parent*/ null); } /** @@ -250,18 +255,37 @@ public class ImageReader implements AutoCloseable { // throw new IllegalArgumentException("The given format=" + Integer.toHexString(format) // + " & usage=" + Long.toHexString(usage) + " is not supported"); // } - return new ImageReader(width, height, format, maxImages, usage); + return new ImageReader(width, height, format, maxImages, usage, /*parent*/ null); } + /** + * @hide + */ + public static @NonNull ImageReader newInstance( + @IntRange(from = 1) int width, + @IntRange(from = 1) int height, + @Format int format, + @IntRange(from = 1) int maxImages, + @NonNull MultiResolutionImageReader parent) { + // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not + // work, and is inscrutable anyway + return new ImageReader(width, height, format, maxImages, + format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN, + parent); + } + + /** * @hide */ - protected ImageReader(int width, int height, int format, int maxImages, long usage) { + protected ImageReader(int width, int height, int format, int maxImages, long usage, + MultiResolutionImageReader parent) { mWidth = width; mHeight = height; mFormat = format; mUsage = usage; mMaxImages = maxImages; + mParent = parent; if (width < 1 || height < 1) { throw new IllegalArgumentException( @@ -433,6 +457,9 @@ public class ImageReader implements AutoCloseable { if (image != null) { image.close(); } + if (mParent != null) { + mParent.flushOther(this); + } } } @@ -584,12 +611,38 @@ public class ImageReader implements AutoCloseable { } if (mListenerHandler == null || mListenerHandler.getLooper() != looper) { mListenerHandler = new ListenerHandler(looper); + mListenerExecutor = new HandlerExecutor(mListenerHandler); } - mListener = listener; } else { - mListener = null; mListenerHandler = null; + mListenerExecutor = null; } + mListener = listener; + } + } + + /** + * Register a listener to be invoked when a new image becomes available + * from the ImageReader. + * + * @param listener + * The listener that will be run. + * @param executor + * The executor which will be used to invoke the listener. + * @throws IllegalArgumentException + * If no handler specified and the calling thread has no looper. + * + * @hide + */ + public void setOnImageAvailableListenerWithExecutor(@NonNull OnImageAvailableListener listener, + @NonNull Executor executor) { + if (executor == null) { + throw new IllegalArgumentException("executor must not be null"); + } + + synchronized (mListenerLock) { + mListenerExecutor = executor; + mListener = listener; } } @@ -763,12 +816,27 @@ public class ImageReader implements AutoCloseable { return; } - final Handler handler; + final Executor executor; + final OnImageAvailableListener listener; synchronized (ir.mListenerLock) { - handler = ir.mListenerHandler; + executor = ir.mListenerExecutor; + listener = ir.mListener; } - if (handler != null) { - handler.sendEmptyMessage(0); + final boolean isReaderValid; + synchronized (ir.mCloseLock) { + isReaderValid = ir.mIsReaderValid; + } + + // It's dangerous to fire onImageAvailable() callback when the ImageReader + // is being closed, as application could acquire next image in the + // onImageAvailable() callback. + if (executor != null && listener != null && isReaderValid) { + executor.execute(new Runnable() { + @Override + public void run() { + listener.onImageAvailable(ir); + } + }); } } @@ -785,11 +853,16 @@ public class ImageReader implements AutoCloseable { private final Object mCloseLock = new Object(); private boolean mIsReaderValid = false; private OnImageAvailableListener mListener; + private Executor mListenerExecutor; private ListenerHandler mListenerHandler; // Keep track of the successfully acquired Images. This need to be thread safe as the images // could be closed by different threads (e.g., application thread and GC thread). private List<Image> mAcquiredImages = new CopyOnWriteArrayList<>(); + // Applicable if this isn't a standalone ImageReader, but belongs to a + // MultiResolutionImageReader. + private final MultiResolutionImageReader mParent; + /** * This field is used by native code, do not access or modify. */ @@ -802,23 +875,22 @@ public class ImageReader implements AutoCloseable { public ListenerHandler(Looper looper) { super(looper, null, true /*async*/); } + } - @Override - public void handleMessage(Message msg) { - OnImageAvailableListener listener; - synchronized (mListenerLock) { - listener = mListener; - } + /** + * An adapter {@link Executor} that posts all executed tasks onto the + * given {@link Handler}. + **/ + private final class HandlerExecutor implements Executor { + private final Handler mHandler; + + public HandlerExecutor(@NonNull Handler handler) { + mHandler = Objects.requireNonNull(handler); + } - // It's dangerous to fire onImageAvailable() callback when the ImageReader is being - // closed, as application could acquire next image in the onImageAvailable() callback. - boolean isReaderValid = false; - synchronized (mCloseLock) { - isReaderValid = mIsReaderValid; - } - if (listener != null && isReaderValid) { - listener.onImageAvailable(ImageReader.this); - } + @Override + public void execute(Runnable command) { + mHandler.post(command); } } diff --git a/media/java/android/media/metrics/Event.java b/media/java/android/media/metrics/Event.java index 5646dcdb6c9c..96b61d2eaf4d 100644 --- a/media/java/android/media/metrics/Event.java +++ b/media/java/android/media/metrics/Event.java @@ -17,22 +17,30 @@ package android.media.metrics; import android.annotation.IntRange; +import android.os.Bundle; /** * Abstract class for metrics events. */ public abstract class Event { - private final long mTimeSinceCreatedMillis; + final long mTimeSinceCreatedMillis; + Bundle mExtras; // hide default constructor /* package */ Event() { mTimeSinceCreatedMillis = MediaMetricsManager.INVALID_TIMESTAMP; } + // TODO: remove protected Event(long timeSinceCreatedMillis) { mTimeSinceCreatedMillis = timeSinceCreatedMillis; } + /* package */ Event(long timeSinceCreatedMillis, Bundle extras) { + mTimeSinceCreatedMillis = timeSinceCreatedMillis; + mExtras = extras; + } + /** * Gets time since the corresponding instance is created in millisecond. * @return the timestamp since the instance is created, or -1 if unknown. @@ -41,4 +49,9 @@ public abstract class Event { public long getTimeSinceCreatedMillis() { return mTimeSinceCreatedMillis; } + + /** @hide */ + public Bundle getExtras() { + return mExtras; + } } diff --git a/media/java/android/media/metrics/IMediaMetricsManager.aidl b/media/java/android/media/metrics/IMediaMetricsManager.aidl index 2cb2ab5c42b5..f2c0d44a00b1 100644 --- a/media/java/android/media/metrics/IMediaMetricsManager.aidl +++ b/media/java/android/media/metrics/IMediaMetricsManager.aidl @@ -28,7 +28,8 @@ import android.media.metrics.TrackChangeEvent; */ interface IMediaMetricsManager { void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId); - String getSessionId(int userId); + String getPlaybackSessionId(int userId); + String getRecordingSessionId(int userId); void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId); void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId); void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId); diff --git a/media/java/android/media/metrics/LogSessionId.java b/media/java/android/media/metrics/LogSessionId.java new file mode 100644 index 000000000000..7ddb259f7f28 --- /dev/null +++ b/media/java/android/media/metrics/LogSessionId.java @@ -0,0 +1,34 @@ +/* + * 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.media.metrics; + +/** + * An instances of this class represents the ID of a log session. + * @hide + */ +public class LogSessionId { + private final String mSessionId; + + /* package */ LogSessionId(String id) { + mSessionId = id; + } + + /** @hide */ + public String getStringId() { + return mSessionId; + } +} diff --git a/media/java/android/media/metrics/MediaMetricsManager.java b/media/java/android/media/metrics/MediaMetricsManager.java index de780f672b28..9710e88cf24b 100644 --- a/media/java/android/media/metrics/MediaMetricsManager.java +++ b/media/java/android/media/metrics/MediaMetricsManager.java @@ -94,7 +94,7 @@ public class MediaMetricsManager { @NonNull public PlaybackSession createPlaybackSession() { try { - String id = mService.getSessionId(mUserId); + String id = mService.getPlaybackSessionId(mUserId); PlaybackSession session = new PlaybackSession(id, this); return session; } catch (RemoteException e) { @@ -103,6 +103,21 @@ public class MediaMetricsManager { } /** + * Creates a recording session. + * @hide + */ + @NonNull + public RecordingSession createRecordingSession() { + try { + String id = mService.getRecordingSessionId(mUserId); + RecordingSession session = new RecordingSession(id, this); + return session; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Reports error event. * @hide */ diff --git a/media/java/android/media/metrics/NetworkEvent.java b/media/java/android/media/metrics/NetworkEvent.java index 029edeb93374..098885cc9bdd 100644 --- a/media/java/android/media/metrics/NetworkEvent.java +++ b/media/java/android/media/metrics/NetworkEvent.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -33,6 +34,9 @@ import java.util.Objects; public final class NetworkEvent extends Event implements Parcelable { /** Network type is not specified. Default type. */ public static final int NETWORK_TYPE_NONE = 0; + // TODO: replace NONE with UNKNOWN + /** @hide */ + public static final int NETWORK_TYPE_UNKNOWN = 0; /** Other network type */ public static final int NETWORK_TYPE_OTHER = 1; /** Wi-Fi network */ @@ -49,6 +53,9 @@ public final class NetworkEvent extends Event implements Parcelable { public static final int NETWORK_TYPE_5G_NSA = 7; /** 5G SA network */ public static final int NETWORK_TYPE_5G_SA = 8; + /** Not network connected */ + /** @hide */ + public static final int NETWORK_TYPE_OFFLINE = 9; private final int mNetworkType; private final long mTimeSinceCreatedMillis; @@ -56,6 +63,7 @@ public final class NetworkEvent extends Event implements Parcelable { /** @hide */ @IntDef(prefix = "NETWORK_TYPE_", value = { NETWORK_TYPE_NONE, + NETWORK_TYPE_UNKNOWN, NETWORK_TYPE_OTHER, NETWORK_TYPE_WIFI, NETWORK_TYPE_ETHERNET, @@ -63,7 +71,8 @@ public final class NetworkEvent extends Event implements Parcelable { NETWORK_TYPE_3G, NETWORK_TYPE_4G, NETWORK_TYPE_5G_NSA, - NETWORK_TYPE_5G_SA + NETWORK_TYPE_5G_SA, + NETWORK_TYPE_OFFLINE }) @Retention(RetentionPolicy.SOURCE) public @interface NetworkType {} @@ -92,6 +101,8 @@ public final class NetworkEvent extends Event implements Parcelable { return "NETWORK_TYPE_5G_NSA"; case NETWORK_TYPE_5G_SA: return "NETWORK_TYPE_5G_SA"; + case NETWORK_TYPE_OFFLINE: + return "NETWORK_TYPE_OFFLINE"; default: return Integer.toHexString(value); } @@ -102,9 +113,10 @@ public final class NetworkEvent extends Event implements Parcelable { * * @hide */ - public NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis) { + public NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis, Bundle extras) { this.mNetworkType = type; this.mTimeSinceCreatedMillis = timeSinceCreatedMillis; + this.mExtras = extras.deepCopy(); } /** @@ -149,8 +161,12 @@ public final class NetworkEvent extends Event implements Parcelable { @Override public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + byte flg = 0; + if (mExtras != null) flg |= 0x1; + dest.writeByte(flg); dest.writeInt(mNetworkType); dest.writeLong(mTimeSinceCreatedMillis); + if (mExtras != null) dest.writeBundle(mExtras); } @Override @@ -160,11 +176,14 @@ public final class NetworkEvent extends Event implements Parcelable { /** @hide */ /* package-private */ NetworkEvent(@NonNull android.os.Parcel in) { + byte flg = in.readByte(); int type = in.readInt(); long timeSinceCreatedMillis = in.readLong(); + Bundle extras = (flg & 0x2) == 0 ? null : in.readBundle(); this.mNetworkType = type; this.mTimeSinceCreatedMillis = timeSinceCreatedMillis; + this.mExtras = extras; } /** @@ -189,6 +208,7 @@ public final class NetworkEvent extends Event implements Parcelable { public static final class Builder { private int mNetworkType = NETWORK_TYPE_NONE; private long mTimeSinceCreatedMillis = -1; + private Bundle mExtras; /** * Creates a new Builder. @@ -214,9 +234,19 @@ public final class NetworkEvent extends Event implements Parcelable { return this; } + /** + * Set extras for compatibility. + * <p>Should be used by support library only. + * @hide + */ + public @NonNull Builder setExtras(@NonNull Bundle extras) { + mExtras = extras; + return this; + } + /** Builds the instance. */ public @NonNull NetworkEvent build() { - NetworkEvent o = new NetworkEvent(mNetworkType, mTimeSinceCreatedMillis); + NetworkEvent o = new NetworkEvent(mNetworkType, mTimeSinceCreatedMillis, mExtras); return o; } } diff --git a/media/java/android/media/metrics/PlaybackErrorEvent.java b/media/java/android/media/metrics/PlaybackErrorEvent.java index 5a0820d16cb9..b23b4d2728b4 100644 --- a/media/java/android/media/metrics/PlaybackErrorEvent.java +++ b/media/java/android/media/metrics/PlaybackErrorEvent.java @@ -21,6 +21,7 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -63,11 +64,13 @@ public final class PlaybackErrorEvent extends Event implements Parcelable { @Nullable String exceptionStack, int errorCode, int subErrorCode, - long timeSinceCreatedMillis) { + long timeSinceCreatedMillis, + Bundle extras) { this.mExceptionStack = exceptionStack; this.mErrorCode = errorCode; this.mSubErrorCode = subErrorCode; this.mTimeSinceCreatedMillis = timeSinceCreatedMillis; + this.mExtras = extras.deepCopy(); } /** @hide */ @@ -135,11 +138,13 @@ public final class PlaybackErrorEvent extends Event implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { byte flg = 0; if (mExceptionStack != null) flg |= 0x1; + if (mExtras != null) flg |= 0x2; dest.writeByte(flg); if (mExceptionStack != null) dest.writeString(mExceptionStack); dest.writeInt(mErrorCode); dest.writeInt(mSubErrorCode); dest.writeLong(mTimeSinceCreatedMillis); + if (mExtras != null) dest.writeBundle(mExtras); } @Override @@ -154,11 +159,13 @@ public final class PlaybackErrorEvent extends Event implements Parcelable { int errorCode = in.readInt(); int subErrorCode = in.readInt(); long timeSinceCreatedMillis = in.readLong(); + Bundle extras = (flg & 0x2) == 0 ? null : in.readBundle(); this.mExceptionStack = exceptionStack; this.mErrorCode = errorCode; this.mSubErrorCode = subErrorCode; this.mTimeSinceCreatedMillis = timeSinceCreatedMillis; + this.mExtras = extras; } @@ -183,6 +190,7 @@ public final class PlaybackErrorEvent extends Event implements Parcelable { private int mErrorCode; private int mSubErrorCode; private long mTimeSinceCreatedMillis = -1; + private Bundle mExtras; /** * Creates a new Builder. @@ -226,6 +234,16 @@ public final class PlaybackErrorEvent extends Event implements Parcelable { return this; } + /** + * Set extras for compatibility. + * <p>Should be used by support library only. + * @hide + */ + public @NonNull Builder setExtras(@NonNull Bundle extras) { + mExtras = extras; + return this; + } + /** Builds the instance. */ public @NonNull PlaybackErrorEvent build() { @@ -241,7 +259,8 @@ public final class PlaybackErrorEvent extends Event implements Parcelable { stack, mErrorCode, mSubErrorCode, - mTimeSinceCreatedMillis); + mTimeSinceCreatedMillis, + mExtras); return o; } } diff --git a/media/java/android/media/metrics/PlaybackMetrics.java b/media/java/android/media/metrics/PlaybackMetrics.java index 4aa61662ba52..7e7f44a97b9c 100644 --- a/media/java/android/media/metrics/PlaybackMetrics.java +++ b/media/java/android/media/metrics/PlaybackMetrics.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -58,6 +59,10 @@ public final class PlaybackMetrics implements Parcelable { /** SS (HTTP Smooth Streaming) stream type. */ public static final int STREAM_TYPE_SS = 5; + /** Unknown playback type. */ + // TODO: change the PLAYBACK_TYPE_ values + /** @hide */ + public static final int PLAYBACK_TYPE_UNKNOWN = 0; /** VOD (Video on Demand) playback type. */ public static final int PLAYBACK_TYPE_VOD = 0; /** Live playback type. */ @@ -80,6 +85,10 @@ public final class PlaybackMetrics implements Parcelable { /** Clear key DRM type. */ public static final int DRM_TYPE_CLEARKEY = 6; + /** Unknown content type. */ + // TODO: change the CONTENT_TYPE_ values + /** @hide */ + public static final int CONTENT_TYPE_UNKNOWN = 0; /** Main contents. */ public static final int CONTENT_TYPE_MAIN = 0; /** Advertisement contents. */ @@ -112,6 +121,7 @@ public final class PlaybackMetrics implements Parcelable { /** @hide */ @IntDef(prefix = "PLAYBACK_TYPE_", value = { + PLAYBACK_TYPE_UNKNOWN, PLAYBACK_TYPE_VOD, PLAYBACK_TYPE_LIVE, PLAYBACK_TYPE_OTHER @@ -134,6 +144,7 @@ public final class PlaybackMetrics implements Parcelable { /** @hide */ @IntDef(prefix = "CONTENT_TYPE_", value = { + CONTENT_TYPE_UNKNOWN, CONTENT_TYPE_MAIN, CONTENT_TYPE_AD, CONTENT_TYPE_OTHER @@ -158,6 +169,8 @@ public final class PlaybackMetrics implements Parcelable { private final long mNetworkBytesRead; private final long mLocalBytesRead; private final long mNetworkTransferDurationMillis; + private final byte[] mDrmSessionId; + private final Bundle mExtras; /** * Creates a new PlaybackMetrics. @@ -179,7 +192,9 @@ public final class PlaybackMetrics implements Parcelable { int audioUnderrunCount, long networkBytesRead, long localBytesRead, - long networkTransferDurationMillis) { + long networkTransferDurationMillis, + byte[] drmSessionId, + Bundle extras) { this.mMediaDurationMillis = mediaDurationMillis; this.mStreamSource = streamSource; this.mStreamType = streamType; @@ -196,6 +211,8 @@ public final class PlaybackMetrics implements Parcelable { this.mNetworkBytesRead = networkBytesRead; this.mLocalBytesRead = localBytesRead; this.mNetworkTransferDurationMillis = networkTransferDurationMillis; + this.mDrmSessionId = drmSessionId; + this.mExtras = extras.deepCopy(); } /** @@ -321,6 +338,12 @@ public final class PlaybackMetrics implements Parcelable { return mNetworkTransferDurationMillis; } + /** @hide */ + @NonNull + public byte[] getDrmSessionId() { + return mDrmSessionId; + } + @Override public String toString() { return "PlaybackMetrics { " @@ -339,6 +362,7 @@ public final class PlaybackMetrics implements Parcelable { + "networkBytesRead = " + mNetworkBytesRead + ", " + "localBytesRead = " + mLocalBytesRead + ", " + "networkTransferDurationMillis = " + mNetworkTransferDurationMillis + + "drmSessionId = " + Arrays.toString(mDrmSessionId) + " }"; } @@ -361,7 +385,8 @@ public final class PlaybackMetrics implements Parcelable { && mAudioUnderrunCount == that.mAudioUnderrunCount && mNetworkBytesRead == that.mNetworkBytesRead && mLocalBytesRead == that.mLocalBytesRead - && mNetworkTransferDurationMillis == that.mNetworkTransferDurationMillis; + && mNetworkTransferDurationMillis == that.mNetworkTransferDurationMillis + && Arrays.equals(mDrmSessionId, that.mDrmSessionId); } @Override @@ -369,7 +394,7 @@ public final class PlaybackMetrics implements Parcelable { return Objects.hash(mMediaDurationMillis, mStreamSource, mStreamType, mPlaybackType, mDrmType, mContentType, mPlayerName, mPlayerVersion, mExperimentIds, mVideoFramesPlayed, mVideoFramesDropped, mAudioUnderrunCount, mNetworkBytesRead, - mLocalBytesRead, mNetworkTransferDurationMillis); + mLocalBytesRead, mNetworkTransferDurationMillis, mDrmSessionId); } @Override @@ -377,6 +402,7 @@ public final class PlaybackMetrics implements Parcelable { long flg = 0; if (mPlayerName != null) flg |= 0x80; if (mPlayerVersion != null) flg |= 0x100; + if (mExtras != null) flg |= 0x200; dest.writeLong(flg); dest.writeLong(mMediaDurationMillis); dest.writeInt(mStreamSource); @@ -386,6 +412,7 @@ public final class PlaybackMetrics implements Parcelable { dest.writeInt(mContentType); if (mPlayerName != null) dest.writeString(mPlayerName); if (mPlayerVersion != null) dest.writeString(mPlayerVersion); + if (mExtras != null) dest.writeBundle(mExtras); dest.writeLongArray(mExperimentIds); dest.writeInt(mVideoFramesPlayed); dest.writeInt(mVideoFramesDropped); @@ -393,6 +420,8 @@ public final class PlaybackMetrics implements Parcelable { dest.writeLong(mNetworkBytesRead); dest.writeLong(mLocalBytesRead); dest.writeLong(mNetworkTransferDurationMillis); + dest.writeInt(mDrmSessionId.length); + dest.writeByteArray(mDrmSessionId); } @Override @@ -411,6 +440,7 @@ public final class PlaybackMetrics implements Parcelable { int contentType = in.readInt(); String playerName = (flg & 0x80) == 0 ? null : in.readString(); String playerVersion = (flg & 0x100) == 0 ? null : in.readString(); + Bundle extras = (flg & 0x200) == 0 ? null : in.readBundle(); long[] experimentIds = in.createLongArray(); int videoFramesPlayed = in.readInt(); int videoFramesDropped = in.readInt(); @@ -418,6 +448,9 @@ public final class PlaybackMetrics implements Parcelable { long networkBytesRead = in.readLong(); long localBytesRead = in.readLong(); long networkTransferDurationMillis = in.readLong(); + int drmSessionIdLen = in.readInt(); + byte[] drmSessionId = new byte[drmSessionIdLen]; + in.readByteArray(drmSessionId); this.mMediaDurationMillis = mediaDurationMillis; this.mStreamSource = streamSource; @@ -435,6 +468,8 @@ public final class PlaybackMetrics implements Parcelable { this.mNetworkBytesRead = networkBytesRead; this.mLocalBytesRead = localBytesRead; this.mNetworkTransferDurationMillis = networkTransferDurationMillis; + this.mDrmSessionId = drmSessionId; + this.mExtras = extras; } public static final @NonNull Parcelable.Creator<PlaybackMetrics> CREATOR = @@ -470,6 +505,8 @@ public final class PlaybackMetrics implements Parcelable { private long mNetworkBytesRead = -1; private long mLocalBytesRead = -1; private long mNetworkTransferDurationMillis = -1; + private byte[] mDrmSessionId = new byte[0]; + private Bundle mExtras; /** * Creates a new Builder. @@ -608,6 +645,24 @@ public final class PlaybackMetrics implements Parcelable { return this; } + /** + * @hide + */ + public @NonNull Builder setDrmSessionId(@NonNull byte[] drmSessionId) { + mDrmSessionId = drmSessionId; + return this; + } + + /** + * Set extras for compatibility. + * <p>Should be used by support library only. + * @hide + */ + public @NonNull Builder setExtras(@NonNull Bundle extras) { + mExtras = extras; + return this; + } + /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull PlaybackMetrics build() { @@ -626,7 +681,9 @@ public final class PlaybackMetrics implements Parcelable { mAudioUnderrunCount, mNetworkBytesRead, mLocalBytesRead, - mNetworkTransferDurationMillis); + mNetworkTransferDurationMillis, + mDrmSessionId, + mExtras); return o; } diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java index 4ee8a45fe196..272fd9bd5576 100644 --- a/media/java/android/media/metrics/PlaybackSession.java +++ b/media/java/android/media/metrics/PlaybackSession.java @@ -29,6 +29,7 @@ import java.util.Objects; public final class PlaybackSession implements AutoCloseable { private final @NonNull String mId; private final @NonNull MediaMetricsManager mManager; + private final @NonNull LogSessionId mLogSessionId; private boolean mClosed = false; /** @@ -41,6 +42,7 @@ public final class PlaybackSession implements AutoCloseable { mManager = manager; AnnotationValidations.validate(NonNull.class, null, mId); AnnotationValidations.validate(NonNull.class, null, mManager); + mLogSessionId = new LogSessionId(mId); } /** @@ -79,9 +81,16 @@ public final class PlaybackSession implements AutoCloseable { } public @NonNull String getId() { + // TODO: remove this method and use getSessionId(); return mId; } + /** @hide */ + public @NonNull LogSessionId getSessionId() { + // TODO: remove getId() and use this method; + return mLogSessionId; + } + @Override public boolean equals(@Nullable Object o) { if (this == o) return true; diff --git a/media/java/android/media/metrics/PlaybackStateEvent.java b/media/java/android/media/metrics/PlaybackStateEvent.java index 8ca5b75dec98..dea8c1db71de 100644 --- a/media/java/android/media/metrics/PlaybackStateEvent.java +++ b/media/java/android/media/metrics/PlaybackStateEvent.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -136,9 +137,11 @@ public final class PlaybackStateEvent extends Event implements Parcelable { */ public PlaybackStateEvent( int state, - long timeSinceCreatedMillis) { + long timeSinceCreatedMillis, + Bundle extras) { this.mTimeSinceCreatedMillis = timeSinceCreatedMillis; this.mState = state; + this.mExtras = extras.deepCopy(); } /** @@ -174,8 +177,12 @@ public final class PlaybackStateEvent extends Event implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { + byte flg = 0; + if (mExtras != null) flg |= 0x1; + dest.writeByte(flg); dest.writeInt(mState); dest.writeLong(mTimeSinceCreatedMillis); + if (mExtras != null) dest.writeBundle(mExtras); } @Override @@ -185,11 +192,14 @@ public final class PlaybackStateEvent extends Event implements Parcelable { /** @hide */ /* package-private */ PlaybackStateEvent(@NonNull Parcel in) { + byte flg = in.readByte(); int state = in.readInt(); long timeSinceCreatedMillis = in.readLong(); + Bundle extras = (flg & 0x1) == 0 ? null : in.readBundle(); this.mState = state; this.mTimeSinceCreatedMillis = timeSinceCreatedMillis; + this.mExtras = extras; } public static final @NonNull Parcelable.Creator<PlaybackStateEvent> CREATOR = @@ -211,6 +221,7 @@ public final class PlaybackStateEvent extends Event implements Parcelable { public static final class Builder { private int mState = STATE_NOT_STARTED; private long mTimeSinceCreatedMillis = -1; + private Bundle mExtras; /** * Creates a new Builder. @@ -236,11 +247,22 @@ public final class PlaybackStateEvent extends Event implements Parcelable { return this; } + /** + * Set extras for compatibility. + * <p>Should be used by support library only. + * @hide + */ + public @NonNull Builder setExtras(@NonNull Bundle extras) { + mExtras = extras; + return this; + } + /** Builds the instance. */ public @NonNull PlaybackStateEvent build() { PlaybackStateEvent o = new PlaybackStateEvent( mState, - mTimeSinceCreatedMillis); + mTimeSinceCreatedMillis, + mExtras); return o; } } diff --git a/media/java/android/media/metrics/RecordingSession.java b/media/java/android/media/metrics/RecordingSession.java new file mode 100644 index 000000000000..541d129604ad --- /dev/null +++ b/media/java/android/media/metrics/RecordingSession.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.metrics; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.internal.util.AnnotationValidations; + +import java.util.Objects; + +/** + * An instances of this class represents a session of media recording. + * @hide + */ +public final class RecordingSession implements AutoCloseable { + private final @NonNull String mId; + private final @NonNull MediaMetricsManager mManager; + private final @NonNull LogSessionId mLogSessionId; + private boolean mClosed = false; + + /** @hide */ + public RecordingSession(@NonNull String id, @NonNull MediaMetricsManager manager) { + mId = id; + mManager = manager; + AnnotationValidations.validate(NonNull.class, null, mId); + AnnotationValidations.validate(NonNull.class, null, mManager); + mLogSessionId = new LogSessionId(mId); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RecordingSession that = (RecordingSession) o; + return Objects.equals(mId, that.mId); + } + + @Override + public int hashCode() { + return Objects.hash(mId); + } + + @Override + public void close() { + mClosed = true; + } +} diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java index ef25357457c5..aa519782ec80 100644 --- a/media/java/android/media/metrics/TrackChangeEvent.java +++ b/media/java/android/media/metrics/TrackChangeEvent.java @@ -16,10 +16,12 @@ package android.media.metrics; +import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -65,9 +67,10 @@ public final class TrackChangeEvent extends Event implements Parcelable { private final @Nullable String mLanguage; private final @Nullable String mLanguageRegion; private final int mChannelCount; - private final int mSampleRate; + private final int mAudioSampleRate; private final int mWidth; private final int mHeight; + private final float mVideoFrameRate; @@ -99,6 +102,7 @@ public final class TrackChangeEvent extends Event implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface TrackType {} + // TODO: remove this constructor. Use the private one below. public TrackChangeEvent( int state, int reason, @@ -125,9 +129,45 @@ public final class TrackChangeEvent extends Event implements Parcelable { this.mLanguage = language; this.mLanguageRegion = languageRegion; this.mChannelCount = channelCount; - this.mSampleRate = sampleRate; + this.mAudioSampleRate = sampleRate; this.mWidth = width; this.mHeight = height; + this.mVideoFrameRate = -1; + } + + private TrackChangeEvent( + int state, + int reason, + @Nullable String containerMimeType, + @Nullable String sampleMimeType, + @Nullable String codecName, + int bitrate, + long timeSinceCreatedMillis, + int type, + @Nullable String language, + @Nullable String languageRegion, + int channelCount, + int sampleRate, + int width, + int height, + float videoFrameRate, + @Nullable Bundle extras) { + this.mState = state; + this.mReason = reason; + this.mContainerMimeType = containerMimeType; + this.mSampleMimeType = sampleMimeType; + this.mCodecName = codecName; + this.mBitrate = bitrate; + this.mTimeSinceCreatedMillis = timeSinceCreatedMillis; + this.mType = type; + this.mLanguage = language; + this.mLanguageRegion = languageRegion; + this.mChannelCount = channelCount; + this.mAudioSampleRate = sampleRate; + this.mWidth = width; + this.mHeight = height; + this.mVideoFrameRate = videoFrameRate; + this.mExtras = extras.deepCopy(); } /** @@ -223,7 +263,7 @@ public final class TrackChangeEvent extends Event implements Parcelable { */ @IntRange(from = -1, to = Integer.MAX_VALUE) public int getSampleRate() { - return mSampleRate; + return mAudioSampleRate; } /** @@ -244,6 +284,16 @@ public final class TrackChangeEvent extends Event implements Parcelable { return mHeight; } + /** + * Gets video frame rate. + * @return the video frame rate, or -1 if unknown. + * @hide + */ + @FloatRange(from = -1, to = Float.MAX_VALUE) + public float getVideoFrameRate() { + return mVideoFrameRate; + } + @Override public void writeToParcel(@NonNull Parcel dest, int flags) { int flg = 0; @@ -252,6 +302,7 @@ public final class TrackChangeEvent extends Event implements Parcelable { if (mCodecName != null) flg |= 0x10; if (mLanguage != null) flg |= 0x100; if (mLanguageRegion != null) flg |= 0x200; + if (mExtras != null) flg |= 0x400; dest.writeInt(flg); dest.writeInt(mState); dest.writeInt(mReason); @@ -264,9 +315,11 @@ public final class TrackChangeEvent extends Event implements Parcelable { if (mLanguage != null) dest.writeString(mLanguage); if (mLanguageRegion != null) dest.writeString(mLanguageRegion); dest.writeInt(mChannelCount); - dest.writeInt(mSampleRate); + dest.writeInt(mAudioSampleRate); dest.writeInt(mWidth); dest.writeInt(mHeight); + dest.writeFloat(mVideoFrameRate); + if (mExtras != null) dest.writeBundle(mExtras); } @Override @@ -291,6 +344,8 @@ public final class TrackChangeEvent extends Event implements Parcelable { int sampleRate = in.readInt(); int width = in.readInt(); int height = in.readInt(); + float videoFrameRate = in.readFloat(); + Bundle extras = (flg & 0x400) == 0 ? null : in.readBundle(); this.mState = state; this.mReason = reason; @@ -303,9 +358,11 @@ public final class TrackChangeEvent extends Event implements Parcelable { this.mLanguage = language; this.mLanguageRegion = languageRegion; this.mChannelCount = channelCount; - this.mSampleRate = sampleRate; + this.mAudioSampleRate = sampleRate; this.mWidth = width; this.mHeight = height; + this.mVideoFrameRate = videoFrameRate; + this.mExtras = extras; } public static final @NonNull Parcelable.Creator<TrackChangeEvent> CREATOR = @@ -335,9 +392,10 @@ public final class TrackChangeEvent extends Event implements Parcelable { + "language = " + mLanguage + ", " + "languageRegion = " + mLanguageRegion + ", " + "channelCount = " + mChannelCount + ", " - + "sampleRate = " + mSampleRate + ", " + + "sampleRate = " + mAudioSampleRate + ", " + "width = " + mWidth + ", " - + "height = " + mHeight + + "height = " + mHeight + ", " + + "videoFrameRate = " + mVideoFrameRate + " }"; } @@ -357,16 +415,17 @@ public final class TrackChangeEvent extends Event implements Parcelable { && Objects.equals(mLanguage, that.mLanguage) && Objects.equals(mLanguageRegion, that.mLanguageRegion) && mChannelCount == that.mChannelCount - && mSampleRate == that.mSampleRate + && mAudioSampleRate == that.mAudioSampleRate && mWidth == that.mWidth - && mHeight == that.mHeight; + && mHeight == that.mHeight + && mVideoFrameRate == that.mVideoFrameRate; } @Override public int hashCode() { return Objects.hash(mState, mReason, mContainerMimeType, mSampleMimeType, mCodecName, mBitrate, mTimeSinceCreatedMillis, mType, mLanguage, mLanguageRegion, - mChannelCount, mSampleRate, mWidth, mHeight); + mChannelCount, mAudioSampleRate, mWidth, mHeight, mVideoFrameRate); } /** @@ -385,9 +444,11 @@ public final class TrackChangeEvent extends Event implements Parcelable { private @Nullable String mLanguage; private @Nullable String mLanguageRegion; private int mChannelCount = -1; - private int mSampleRate = -1; + private int mAudioSampleRate = -1; private int mWidth = -1; private int mHeight = -1; + private float mVideoFrameRate = -1; + private Bundle mExtras; private long mBuilderFieldsSet = 0L; @@ -512,9 +573,10 @@ public final class TrackChangeEvent extends Event implements Parcelable { */ public @NonNull Builder setSampleRate( @IntRange(from = -1, to = Integer.MAX_VALUE) int value) { + // TODO: rename it to setAudioSampleRate checkNotUsed(); mBuilderFieldsSet |= 0x800; - mSampleRate = value; + mAudioSampleRate = value; return this; } @@ -540,6 +602,28 @@ public final class TrackChangeEvent extends Event implements Parcelable { return this; } + /** + * Sets video frame rate. + * @param value the video frame rate. -1 indicates the value is unknown. + * @hide + */ + public @NonNull Builder setVideoFrameRate( + @FloatRange(from = -1, to = Float.MAX_VALUE) float value) { + checkNotUsed(); + mVideoFrameRate = value; + return this; + } + + /** + * Set extras for compatibility. + * <p>Should be used by support library only. + * @hide + */ + public @NonNull Builder setExtras(@NonNull Bundle extras) { + mExtras = extras; + return this; + } + /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull TrackChangeEvent build() { checkNotUsed(); @@ -557,9 +641,11 @@ public final class TrackChangeEvent extends Event implements Parcelable { mLanguage, mLanguageRegion, mChannelCount, - mSampleRate, + mAudioSampleRate, mWidth, - mHeight); + mHeight, + mVideoFrameRate, + mExtras); return o; } diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp index 307d80dc15c0..07866ac34e4c 100644 --- a/media/jni/android_media_MediaCodecList.cpp +++ b/media/jni/android_media_MediaCodecList.cpp @@ -105,6 +105,7 @@ static const JavaMediaCodecListWrapper *getCodecList(JNIEnv *env) { // This should never happen unless something is really wrong jniThrowException( env, "java/lang/RuntimeException", "cannot get MediaCodecList"); + return NULL; } sListWrapper.reset(new JavaMediaCodecListWrapper(mcl)); diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt index a9fd6f248560..d2ed73ef8298 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -6,6 +6,7 @@ package android.net { } public class ConnectivityManager { + method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot(); method @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); diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 39ec2edcea3f..d7c6854496b7 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -1259,6 +1259,25 @@ public class ConnectivityManager { } /** + * Return a list of {@link NetworkStateSnapshot}s, one for each network that is currently + * connected. + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.NETWORK_SETTINGS}) + @NonNull + public List<NetworkStateSnapshot> getAllNetworkStateSnapshot() { + try { + return mService.getAllNetworkStateSnapshot(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns the {@link Network} object currently serving a given type, or * null if the given type is not connected. * diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index 160338d396af..cd49258d1c47 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -31,6 +31,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.NetworkState; +import android.net.NetworkStateSnapshot; import android.net.OemNetworkPreferences; import android.net.ProxyInfo; import android.net.UidRange; @@ -79,6 +80,8 @@ interface IConnectivityManager @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) NetworkState[] getAllNetworkState(); + List<NetworkStateSnapshot> getAllNetworkStateSnapshot(); + boolean isActiveNetworkMetered(); boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress, diff --git a/core/java/android/net/NetworkState.java b/packages/Connectivity/framework/src/android/net/NetworkState.java index 813fde1c15f2..d01026566ca0 100644 --- a/core/java/android/net/NetworkState.java +++ b/packages/Connectivity/framework/src/android/net/NetworkState.java @@ -115,7 +115,8 @@ public class NetworkState implements Parcelable { } @UnsupportedAppUsage - public static final @android.annotation.NonNull Creator<NetworkState> CREATOR = new Creator<NetworkState>() { + @NonNull + public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() { @Override public NetworkState createFromParcel(Parcel in) { return new NetworkState(in); diff --git a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java index 43fffd733e91..739ddada50b4 100644 --- a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java +++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java @@ -30,8 +30,8 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.provider.Settings; -import android.telephony.PhoneStateListener; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.Log; @@ -92,8 +92,8 @@ public class MultinetworkPolicyTracker { } @VisibleForTesting - protected class ActiveDataSubscriptionIdChangedListener extends PhoneStateListener - implements PhoneStateListener.ActiveDataSubscriptionIdChangedListener { + protected class ActiveDataSubscriptionIdListener extends TelephonyCallback + implements TelephonyCallback.ActiveDataSubscriptionIdListener { @Override public void onActiveDataSubscriptionIdChanged(int subId) { mActiveSubId = subId; @@ -121,8 +121,8 @@ public class MultinetworkPolicyTracker { } }; - ctx.getSystemService(TelephonyManager.class).registerPhoneStateListener( - new HandlerExecutor(handler), new ActiveDataSubscriptionIdChangedListener()); + ctx.getSystemService(TelephonyManager.class).registerTelephonyCallback( + new HandlerExecutor(handler), new ActiveDataSubscriptionIdListener()); updateAvoidBadWifi(); updateMeteredMultipathPreference(); diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 96241194402a..efa9f3c16b48 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -64,8 +64,12 @@ <string name="wifi_security_eap" translatable="false">WPA/WPA2/WPA3-Enterprise</string> <!-- Do not translate. Concise terminology for wifi with WPA 802.1x EAP security --> <string name="wifi_security_eap_wpa" translatable="false">WPA-Enterprise</string> + <!-- Do not translate. Concise terminology for wifi with 802.1x EAP security --> + <string name="wifi_security_eap_wpa_wpa2" translatable="false">WPA/WPA2-Enterprise</string> <!-- Do not translate. Concise terminology for wifi with WPA2/WPA3 802.1x EAP security --> <string name="wifi_security_eap_wpa2_wpa3" translatable="false">WPA2/WPA3-Enterprise</string> + <!-- Do not translate. Concise terminology for wifi with WPA3 802.1x EAP security --> + <string name="wifi_security_eap_wpa3" translatable="false">WPA3-Enterprise</string> <!-- Do not translate. Concise terminology for Passpoint network --> <string name="wifi_security_passpoint" translatable="false">Passpoint</string> <!-- Do not translate. Terminology for wifi with WPA3 security --> diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java index 43717aba3abd..dfde3c7a2512 100644 --- a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java @@ -27,7 +27,7 @@ import android.net.wifi.WifiManager.SubsystemRestartTrackingCallback; import android.os.Handler; import android.os.HandlerExecutor; import android.provider.Settings; -import android.telephony.PhoneStateListener; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.Log; @@ -72,7 +72,10 @@ public class ConnectivitySubsystemsRecoveryManager { checkIfAllSubsystemsRestartsAreDone(); } }; - private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + private final MobileTelephonyCallback mTelephonyCallback = new MobileTelephonyCallback(); + + private class MobileTelephonyCallback extends TelephonyCallback implements + TelephonyCallback.RadioPowerStateListener { @Override public void onRadioPowerStateChanged(int state) { if (!mTelephonyRestartInProgress || mCurrentRecoveryCallback == null) { @@ -85,7 +88,7 @@ public class ConnectivitySubsystemsRecoveryManager { checkIfAllSubsystemsRestartsAreDone(); } } - }; + } public ConnectivitySubsystemsRecoveryManager(@NonNull Context context, @NonNull Handler handler) { @@ -201,12 +204,12 @@ public class ConnectivitySubsystemsRecoveryManager { } private void startTrackingTelephonyRestart() { - mTelephonyManager.registerPhoneStateListener(new HandlerExecutor(mHandler), - mPhoneStateListener); + mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(mHandler), + mTelephonyCallback); } private void stopTrackingTelephonyRestart() { - mTelephonyManager.unregisterPhoneStateListener(mPhoneStateListener); + mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback); } private void checkIfAllSubsystemsRestartsAreDone() { diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java index 0cd5e4ded168..1a08366734bc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java @@ -17,11 +17,11 @@ package com.android.settingslib.mobile; import android.os.Handler; import android.os.Looper; -import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; import android.util.Log; @@ -40,9 +40,9 @@ public class MobileStatusTracker { private final SubscriptionInfo mSubscriptionInfo; private final Callback mCallback; private final MobileStatus mMobileStatus; - private final PhoneStateListener mPhoneStateListener; private final SubscriptionDefaults mDefaults; private final Handler mReceiverHandler; + private final MobileTelephonyCallback mTelephonyCallback; /** * MobileStatusTracker constructors @@ -58,7 +58,7 @@ public class MobileStatusTracker { SubscriptionInfo info, SubscriptionDefaults defaults, Callback callback) { mPhone = phone; mReceiverHandler = new Handler(receiverLooper); - mPhoneStateListener = new MobilePhoneStateListener(); + mTelephonyCallback = new MobileTelephonyCallback(); mSubscriptionInfo = info; mDefaults = defaults; mCallback = callback; @@ -68,8 +68,8 @@ public class MobileStatusTracker { /* updateTelephony= */false, new MobileStatus(mMobileStatus))); } - public PhoneStateListener getPhoneStateListener() { - return mPhoneStateListener; + public MobileTelephonyCallback getTelephonyCallback() { + return mTelephonyCallback; } /** @@ -77,9 +77,9 @@ public class MobileStatusTracker { */ public void setListening(boolean listening) { if (listening) { - mPhone.registerPhoneStateListener(mReceiverHandler::post, mPhoneStateListener); + mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback); } else { - mPhone.unregisterPhoneStateListener(mPhoneStateListener); + mPhone.unregisterTelephonyCallback(mTelephonyCallback); } } @@ -106,15 +106,14 @@ public class MobileStatusTracker { || activity == TelephonyManager.DATA_ACTIVITY_OUT; } - private class MobilePhoneStateListener extends PhoneStateListener implements - PhoneStateListener.ServiceStateChangedListener, - PhoneStateListener.SignalStrengthsChangedListener, - PhoneStateListener.CallStateChangedListener, - PhoneStateListener.DataConnectionStateChangedListener, - PhoneStateListener.DataActivityListener, - PhoneStateListener.CarrierNetworkChangeListener, - PhoneStateListener.ActiveDataSubscriptionIdChangedListener, - PhoneStateListener.DisplayInfoChangedListener{ + public class MobileTelephonyCallback extends TelephonyCallback implements + TelephonyCallback.ServiceStateListener, + TelephonyCallback.SignalStrengthsListener, + TelephonyCallback.DataConnectionStateListener, + TelephonyCallback.DataActivityListener, + TelephonyCallback.CarrierNetworkListener, + TelephonyCallback.ActiveDataSubscriptionIdListener, + TelephonyCallback.DisplayInfoListener{ @Override public void onSignalStrengthsChanged(SignalStrength signalStrength) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index a81a05f1147a..303ee3c9fca2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -223,7 +223,8 @@ public class AccessPoint implements Comparable<AccessPoint> { public static final int SECURITY_OWE = 4; public static final int SECURITY_SAE = 5; public static final int SECURITY_EAP_SUITE_B = 6; - public static final int SECURITY_MAX_VAL = 7; // Has to be the last + public static final int SECURITY_EAP_WPA3_ENTERPRISE = 7; + public static final int SECURITY_MAX_VAL = 8; // Has to be the last private static final int PSK_UNKNOWN = 0; private static final int PSK_WPA = 1; diff --git a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml b/packages/SystemUI/res/layout/udfps_animation_view_bp.xml new file mode 100644 index 000000000000..0cfbf2e61dd1 --- /dev/null +++ b/packages/SystemUI/res/layout/udfps_animation_view_bp.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<com.android.systemui.biometrics.UdfpsAnimationViewBp + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/udfps_animation_view" + android:layout_width="match_parent" + android:layout_height="match_parent"> +</com.android.systemui.biometrics.UdfpsAnimationViewBp> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 3bc1c8053db3..b3fca366ea1e 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -183,7 +183,9 @@ <color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 --> <!-- UDFPS colors --> - <color name="udfps_enroll_icon">#000000</color> <!-- 100% black --> + <color name="udfps_enroll_icon">#000000</color> <!-- 100% black --> + <color name="udfps_moving_target_fill">#cc4285f4</color> <!-- 80% blue --> + <color name="udfps_moving_target_stroke">#ff669DF6</color> <!-- 100% blue --> <!-- Logout button --> <color name="logout_button_bg_color">#ccffffff</color> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 5f8df5a13ad1..b5ded012b3bd 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -294,8 +294,10 @@ <string name="screenrecord_share_label">Share</string> <!-- A toast message shown after successfully canceling a screen recording [CHAR LIMIT=NONE] --> <string name="screenrecord_cancel_success">Screen recording canceled</string> - <!-- Notification text shown after saving a screen recording to prompt the user to view it [CHAR LIMIT=100] --> - <string name="screenrecord_save_message">Screen recording saved, tap to view</string> + <!-- Notification text shown after saving a screen recording [CHAR LIMIT=100] --> + <string name="screenrecord_save_title">Screen recording saved</string> + <!-- Subtext for a notification shown after saving a screen recording to prompt the user to view it [CHAR_LIMIT=100] --> + <string name="screenrecord_save_text">Tap to view</string> <!-- A toast message shown when there is an error deleting a screen recording [CHAR LIMIT=NONE] --> <string name="screenrecord_delete_error">Error deleting screen recording</string> <!-- A toast message shown when the screen recording cannot be started due to insufficient permissions [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java index e6477f158c27..400bf15f8c65 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java @@ -113,17 +113,6 @@ public class RemoteAnimationAdapterCompat { // TODO(bc-unlock): Build wrapped object for non-apps target. final RemoteAnimationTargetCompat[] nonAppsCompat = new RemoteAnimationTargetCompat[0]; - final Runnable animationFinishedCallback = new Runnable() { - @Override - public void run() { - try { - finishCallback.onTransitionFinished(null /* wct */); - } catch (RemoteException e) { - Log.e("ActivityOptionsCompat", "Failed to call app controlled animation" - + " finished callback", e); - } - } - }; // TODO(b/177438007): Move this set-up logic into launcher's animation impl. boolean isReturnToHome = false; @@ -143,8 +132,8 @@ public class RemoteAnimationAdapterCompat { final TransitionInfo.Change change = info.getChanges().get(i); final SurfaceControl leash = change.getLeash(); final int mode = info.getChanges().get(i).getMode(); - // Only deal with roots - if (change.getParent() != null) continue; + // Only deal with independent layers + if (!TransitionInfo.isIndependent(change, info)) continue; if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { t.setLayer(leash, info.getChanges().size() * 3 - i); } @@ -156,6 +145,18 @@ public class RemoteAnimationAdapterCompat { } } t.apply(); + + final Runnable animationFinishedCallback = new Runnable() { + @Override + public void run() { + try { + finishCallback.onTransitionFinished(null /* wct */); + } catch (RemoteException e) { + Log.e("ActivityOptionsCompat", "Failed to call app controlled animation" + + " finished callback", e); + } + } + }; // TODO(bc-unlcok): Pass correct transit type. remoteAnimationAdapter.onAnimationStart( TRANSIT_OLD_NONE, diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java index 568fdea19219..87f6b8202ded 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -16,8 +16,6 @@ package com.android.systemui.shared.system; -import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; - import android.app.PictureInPictureParams; import android.app.WindowConfiguration; import android.graphics.Point; @@ -46,9 +44,6 @@ public class RemoteAnimationTargetCompat { public static final int ACTIVITY_TYPE_ASSISTANT = WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; public final int activityType; - public static final int TYPE_NAVIGATION_BAR = WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; - public final int windowType; - public final int taskId; public final SurfaceControlCompat leash; public final boolean isTranslucent; @@ -79,7 +74,6 @@ public class RemoteAnimationTargetCompat { contentInsets = app.contentInsets; activityType = app.windowConfiguration.getActivityType(); pictureInPictureParams = app.pictureInPictureParams; - windowType = app.windowType; mStartLeash = app.startLeash; } @@ -120,7 +114,6 @@ public class RemoteAnimationTargetCompat { activityType = ACTIVITY_TYPE_UNDEFINED; } pictureInPictureParams = null; - windowType = INVALID_WINDOW_TYPE; mStartLeash = null; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java index a02900328ae7..a51b6fd16445 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java @@ -17,6 +17,7 @@ package com.android.systemui.biometrics; import android.content.Context; +import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.view.View; @@ -46,12 +47,17 @@ public abstract class UdfpsAnimation extends Drawable { } public void onSensorRectUpdated(@NonNull RectF sensorRect) { - int margin = (int) (sensorRect.bottom - sensorRect.top) / 5; - mFingerprintDrawable.setBounds( - (int) sensorRect.left + margin, + final int margin = (int) sensorRect.height() / 8; + + final Rect bounds = new Rect((int) sensorRect.left + margin, (int) sensorRect.top + margin, (int) sensorRect.right - margin, (int) sensorRect.bottom - margin); + updateFingerprintIconBounds(bounds); + } + + protected void updateFingerprintIconBounds(@NonNull Rect bounds) { + mFingerprintDrawable.setBounds(bounds); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java index 28b57195c5d4..015a598e972b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java @@ -22,13 +22,14 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; +import android.graphics.PointF; +import android.graphics.Rect; import android.graphics.RectF; -import android.util.Log; +import android.graphics.drawable.Drawable; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.settingslib.Utils; import com.android.systemui.R; /** @@ -40,9 +41,13 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { private static final float SHADOW_RADIUS = 5.f; private static final float PROGRESS_BAR_RADIUS = 140.f; - @Nullable private RectF mSensorRect; + @NonNull private final Drawable mMovingTargetFpIcon; @NonNull private final Paint mSensorPaint; - private final int mNotificationShadeColor; + @NonNull private final Paint mBlueFill; + @NonNull private final Paint mBlueStroke;; + + @Nullable private RectF mSensorRect; + @Nullable private UdfpsEnrollHelper mEnrollHelper; UdfpsAnimationEnroll(@NonNull Context context) { super(context); @@ -53,8 +58,24 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, Color.BLACK); mSensorPaint.setStyle(Paint.Style.FILL); - mNotificationShadeColor = Utils.getColorAttr(context, - android.R.attr.colorBackgroundFloating).getDefaultColor(); + mBlueFill = new Paint(0 /* flags */); + mBlueFill.setAntiAlias(true); + mBlueFill.setColor(context.getColor(R.color.udfps_moving_target_fill)); + mBlueFill.setStyle(Paint.Style.FILL); + + mBlueStroke = new Paint(0 /* flags */); + mBlueStroke.setAntiAlias(true); + mBlueStroke.setColor(context.getColor(R.color.udfps_moving_target_stroke)); + mBlueStroke.setStyle(Paint.Style.STROKE); + mBlueStroke.setStrokeWidth(12); + + mMovingTargetFpIcon = context.getResources().getDrawable(R.drawable.ic_fingerprint, null); + mMovingTargetFpIcon.setTint(Color.WHITE); + mMovingTargetFpIcon.mutate(); + } + + void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { + mEnrollHelper = helper; } @Override @@ -74,6 +95,12 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { } @Override + protected void updateFingerprintIconBounds(@NonNull Rect bounds) { + super.updateFingerprintIconBounds(bounds); + mMovingTargetFpIcon.setBounds(bounds); + } + + @Override public void draw(@NonNull Canvas canvas) { if (isIlluminationShowing()) { return; @@ -87,6 +114,24 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { } } mFingerprintDrawable.draw(canvas); + + // Draw moving target + if (mEnrollHelper.isCenterEnrollmentComplete()) { + mFingerprintDrawable.setAlpha(64); + + canvas.save(); + final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint(); + canvas.translate(point.x, point.y); + if (mSensorRect != null) { + canvas.drawOval(mSensorRect, mBlueFill); + canvas.drawOval(mSensorRect, mBlueStroke); + } + + mMovingTargetFpIcon.draw(canvas); + canvas.restore(); + } else { + mFingerprintDrawable.setAlpha(255); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java index f4dd181eb329..43ecf6778022 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.graphics.Canvas; +import android.graphics.PointF; import android.graphics.RectF; import android.util.AttributeSet; import android.widget.FrameLayout; @@ -62,7 +63,10 @@ public abstract class UdfpsAnimationView extends FrameLayout implements DozeRece @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - getUdfpsAnimation().onDestroy(); + + if (getUdfpsAnimation() != null) { + getUdfpsAnimation().onDestroy(); + } } private int expansionToAlpha(float expansion) { @@ -78,11 +82,19 @@ public abstract class UdfpsAnimationView extends FrameLayout implements DozeRece } void onIlluminationStarting() { + if (getUdfpsAnimation() == null) { + return; + } + getUdfpsAnimation().setIlluminationShowing(true); postInvalidate(); } void onIlluminationStopped() { + if (getUdfpsAnimation() == null) { + return; + } + getUdfpsAnimation().setIlluminationShowing(false); postInvalidate(); } @@ -131,4 +143,14 @@ public abstract class UdfpsAnimationView extends FrameLayout implements DozeRece } return getUdfpsAnimation().getPaddingY(); } + + /** + * @return the amount of translation needed if the view currently requires the user to touch + * somewhere other than the exact center of the sensor. For example, this can happen + * during guided enrollment. + */ + @NonNull + PointF getTouchTranslation() { + return new PointF(0, 0); + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java new file mode 100644 index 000000000000..515b442b61f6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.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.systemui.biometrics; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.annotation.Nullable; + +/** + * Class that coordinates non-HBM animations during BiometricPrompt. + * + * Note that {@link AuthBiometricUdfpsView} also shows UDFPS animations. At some point we should + * de-dupe this if necessary. This will probably happen once the top-level TODO in UdfpsController + * is completed (inflate operation-specific views, instead of inflating generic udfps_view and + * adding operation-specific animations to it). + */ +public class UdfpsAnimationViewBp extends UdfpsAnimationView { + public UdfpsAnimationViewBp(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Nullable + @Override + protected UdfpsAnimation getUdfpsAnimation() { + return null; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java index 19e774937e20..543df33dd5d7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java @@ -19,6 +19,7 @@ package com.android.systemui.biometrics; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.graphics.PointF; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -33,7 +34,7 @@ public class UdfpsAnimationViewEnroll extends UdfpsAnimationView private static final String TAG = "UdfpsAnimationViewEnroll"; - @NonNull private UdfpsAnimation mUdfpsAnimation; + @NonNull private UdfpsAnimationEnroll mUdfpsAnimation; @NonNull private UdfpsProgressBar mProgressBar; @Nullable private UdfpsEnrollHelper mEnrollHelper; @@ -50,6 +51,7 @@ public class UdfpsAnimationViewEnroll extends UdfpsAnimationView public void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { mEnrollHelper = helper; + mUdfpsAnimation.setEnrollHelper(helper); } @Override @@ -81,4 +83,14 @@ public class UdfpsAnimationViewEnroll extends UdfpsAnimationView mProgressBar.setProgress(interpolatedProgress, true); } + + @NonNull + @Override + PointF getTouchTranslation() { + if (!mEnrollHelper.isCenterEnrollmentComplete()) { + return new PointF(0, 0); + } else { + return mEnrollHelper.getNextGuidedEnrollmentPoint(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index e1d7eb3cbb19..852cdcbe20fd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -97,7 +97,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { public void showUdfpsOverlay(int sensorId, int reason) { if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) { - mEnrollHelper = new UdfpsEnrollHelper(reason); + mEnrollHelper = new UdfpsEnrollHelper(mContext, reason); } else { mEnrollHelper = null; } @@ -231,6 +231,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { @Override public void dozeTimeTick() { + if (mView == null) { + return; + } mView.dozeTimeTick(); } @@ -358,23 +361,29 @@ public class UdfpsController implements DozeReceiver, HbmCallback { switch (reason) { case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR: case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: { - final UdfpsAnimationViewEnroll animation = (UdfpsAnimationViewEnroll) + final UdfpsAnimationViewEnroll view = (UdfpsAnimationViewEnroll) inflater.inflate(R.layout.udfps_animation_view_enroll, null, false); - animation.setEnrollHelper(mEnrollHelper); - return animation; + view.setEnrollHelper(mEnrollHelper); + return view; + } + + case IUdfpsOverlayController.REASON_AUTH_BP: { + final UdfpsAnimationViewBp view = (UdfpsAnimationViewBp) + inflater.inflate(R.layout.udfps_animation_view_bp, null, false); + return view; } case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: { - final UdfpsAnimationViewKeyguard animation = (UdfpsAnimationViewKeyguard) + final UdfpsAnimationViewKeyguard view = (UdfpsAnimationViewKeyguard) inflater.inflate(R.layout.udfps_animation_view_keyguard, null, false); - animation.setStatusBarStateController(mStatusBarStateController); - return animation; + view.setStatusBarStateController(mStatusBarStateController); + return view; } case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: { - final UdfpsAnimationViewFpmOther animation = (UdfpsAnimationViewFpmOther) + final UdfpsAnimationViewFpmOther view = (UdfpsAnimationViewFpmOther) inflater.inflate(R.layout.udfps_animation_view_fpm_other, null, false); - return animation; + return view; } default: diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java index 942fa7aae0bc..14eca1b1cb2c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java @@ -18,7 +18,13 @@ package com.android.systemui.biometrics; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; +import android.graphics.PointF; import android.hardware.fingerprint.IUdfpsOverlayController; +import android.util.TypedValue; + +import java.util.ArrayList; +import java.util.List; /** * Helps keep track of enrollment state and animates the progress bar accordingly. @@ -26,20 +32,48 @@ import android.hardware.fingerprint.IUdfpsOverlayController; public class UdfpsEnrollHelper { private static final String TAG = "UdfpsEnrollHelper"; + // Enroll with two center touches before going to guided enrollment + private static final int NUM_CENTER_TOUCHES = 2; + interface Listener { void onEnrollmentProgress(int remaining, int totalSteps); } // IUdfpsOverlayController reason private final int mEnrollReason; + private final List<PointF> mGuidedEnrollmentPoints; private int mTotalSteps = -1; - private int mCurrentProgress = 0; + private int mRemainingSteps = -1; + + // Note that this is actually not equal to "mTotalSteps - mRemainingSteps", because the + // interface makes no promises about monotonically increasing by one each time. + private int mLocationsEnrolled = 0; @Nullable Listener mListener; - public UdfpsEnrollHelper(int reason) { + public UdfpsEnrollHelper(@NonNull Context context, int reason) { mEnrollReason = reason; + mGuidedEnrollmentPoints = new ArrayList<>(); + + // Number of pixels per mm + float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1, + context.getResources().getDisplayMetrics()); + + mGuidedEnrollmentPoints.add(new PointF( 2.00f * px, 0.00f * px)); + mGuidedEnrollmentPoints.add(new PointF( 0.87f * px, -2.70f * px)); + mGuidedEnrollmentPoints.add(new PointF(-1.80f * px, -1.31f * px)); + mGuidedEnrollmentPoints.add(new PointF(-1.80f * px, 1.31f * px)); + mGuidedEnrollmentPoints.add(new PointF( 0.88f * px, 2.70f * px)); + mGuidedEnrollmentPoints.add(new PointF( 3.94f * px, -1.06f * px)); + mGuidedEnrollmentPoints.add(new PointF( 2.90f * px, -4.14f * px)); + mGuidedEnrollmentPoints.add(new PointF(-0.52f * px, -5.95f * px)); + mGuidedEnrollmentPoints.add(new PointF(-3.33f * px, -3.33f * px)); + mGuidedEnrollmentPoints.add(new PointF(-3.99f * px, -0.35f * px)); + mGuidedEnrollmentPoints.add(new PointF(-3.62f * px, 2.54f * px)); + mGuidedEnrollmentPoints.add(new PointF(-1.49f * px, 5.57f * px)); + mGuidedEnrollmentPoints.add(new PointF( 2.29f * px, 4.92f * px)); + mGuidedEnrollmentPoints.add(new PointF( 3.82f * px, 1.78f * px)); } boolean shouldShowProgressBar() { @@ -51,6 +85,12 @@ public class UdfpsEnrollHelper { mTotalSteps = remaining; } + if (remaining != mRemainingSteps) { + mLocationsEnrolled++; + } + + mRemainingSteps = remaining; + if (mListener != null) { mListener.onEnrollmentProgress(remaining, mTotalSteps); } @@ -67,8 +107,21 @@ public class UdfpsEnrollHelper { // bar can be updated. If enrollment has not started yet, the progress bar will be empty // anyway. if (mTotalSteps != -1) { - final int remainingSteps = mTotalSteps - mCurrentProgress; - mListener.onEnrollmentProgress(remainingSteps, mTotalSteps); + mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps); + } + } + + boolean isCenterEnrollmentComplete() { + if (mTotalSteps == -1 || mRemainingSteps == -1) { + return false; } + final int stepsEnrolled = mTotalSteps - mRemainingSteps; + return stepsEnrolled >= NUM_CENTER_TOUCHES; + } + + @NonNull + PointF getNextGuidedEnrollmentPoint() { + final int index = mLocationsEnrolled - NUM_CENTER_TOUCHES; + return mGuidedEnrollmentPoints.get(index % mGuidedEnrollmentPoints.size()); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index cd849e63ba9c..75a362131de1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -27,6 +27,7 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.PointF; import android.graphics.RectF; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.text.TextUtils; @@ -180,8 +181,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin boolean isValidTouch(float x, float y, float pressure) { // The X and Y coordinates of the sensor's center. - final float cx = mSensorRect.centerX(); - final float cy = mSensorRect.centerY(); + final PointF translation = mAnimationView.getTouchTranslation(); + final float cx = mSensorRect.centerX() + translation.x; + final float cy = mSensorRect.centerY() + translation.y; // Radii along the X and Y axes. final float rx = (mSensorRect.right - mSensorRect.left) / 2.0f; final float ry = (mSensorRect.bottom - mSensorRect.top) / 2.0f; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index 70b7b047eebc..4491cc12a3cb 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -180,7 +180,7 @@ public class NavigationBarController implements Callbacks, } } else { for (int i = 0; i < mNavigationBars.size(); i++) { - mNavigationBars.get(i).onConfigurationChanged(newConfig); + mNavigationBars.valueAt(i).onConfigurationChanged(newConfig); } } } @@ -193,7 +193,7 @@ public class NavigationBarController implements Callbacks, if (navBar == null) { continue; } - NavigationBarView view = (NavigationBarView) mNavigationBars.get(i).getView(); + NavigationBarView view = (NavigationBarView) navBar.getView(); if (view != null) { view.updateStates(); } @@ -399,7 +399,7 @@ public class NavigationBarController implements Callbacks, if (i > 0) { pw.println(); } - mNavigationBars.get(i).dump(pw); + mNavigationBars.valueAt(i).dump(pw); } } } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index 2ea8657f88e5..a69ec278be91 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -23,7 +23,6 @@ import android.app.Activity; import android.app.INotificationManager; import android.app.people.IPeopleManager; import android.app.people.PeopleSpaceTile; -import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; @@ -34,12 +33,10 @@ import android.provider.Settings; import android.util.Log; import android.view.ViewGroup; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.R; +import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import java.util.Collections; import java.util.List; import javax.inject.Inject; @@ -54,15 +51,14 @@ public class PeopleSpaceActivity extends Activity { private ViewGroup mPeopleSpaceLayout; private IPeopleManager mPeopleManager; + private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; private INotificationManager mNotificationManager; private PackageManager mPackageManager; private LauncherApps mLauncherApps; private Context mContext; - private AppWidgetManager mAppWidgetManager; private NotificationEntryManager mNotificationEntryManager; private int mAppWidgetId; private boolean mShowSingleConversation; - private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); @Inject public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager) { @@ -81,8 +77,8 @@ public class PeopleSpaceActivity extends Activity { mPackageManager = getPackageManager(); mPeopleManager = IPeopleManager.Stub.asInterface( ServiceManager.getService(Context.PEOPLE_SERVICE)); + mPeopleSpaceWidgetManager = new PeopleSpaceWidgetManager(mContext); mLauncherApps = mContext.getSystemService(LauncherApps.class); - mAppWidgetManager = AppWidgetManager.getInstance(mContext); setTileViewsWithPriorityConversations(); mAppWidgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID); @@ -142,29 +138,13 @@ public class PeopleSpaceActivity extends Activity { + mAppWidgetId); } } - - PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId); - int[] widgetIds = new int[mAppWidgetId]; - // TODO: Populate new widget with existing conversation notification, if there is any. - PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager, - mPeopleManager); - if (mLauncherApps != null) { - try { - if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId()); - mLauncherApps.cacheShortcuts(tile.getPackageName(), - Collections.singletonList(tile.getId()), - tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); - } catch (Exception e) { - Log.w(TAG, "Exception caching shortcut:" + e); - } - } + mPeopleSpaceWidgetManager.addNewWidget(tile, mAppWidgetId); finishActivity(); } /** Finish activity with a successful widget configuration result. */ private void finishActivity() { if (DEBUG) Log.d(TAG, "Widget added!"); - mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED); setActivityResult(RESULT_OK); finish(); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index 41080afb3604..f9a16c1c1970 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -220,7 +220,7 @@ public class PeopleSpaceUtils { } @Nullable - private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager, + public static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager, AppWidgetManager appWidgetManager, Context context, int appWidgetId) { try { @@ -230,7 +230,7 @@ public class PeopleSpaceUtils { String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING); int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID); String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING); - if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) { + if (!validKey(shortcutId, pkg, userId)) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); shortcutId = sp.getString(String.valueOf(appWidgetId), null); if (shortcutId == null) { @@ -268,6 +268,17 @@ public class PeopleSpaceUtils { } } + /** Returns stored widgets for the conversation specified. */ + public static Set<String> getStoredWidgetIds(SharedPreferences sp, String shortcutId, + String packageName, int userId) { + if (shortcutId == null || packageName == null) { + return new HashSet<>(); + } + String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId); + return new HashSet<>(sp.getStringSet(key, new HashSet<>())); + } + + /** Best-effort attempts to migrate existing users to the new storage format. */ // TODO: Remove after sufficient time. Temporary migration storage for existing users. private static void migrateExistingUsersToNewStorage(Context context, String shortcutId, @@ -286,7 +297,11 @@ public class PeopleSpaceUtils { if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName()); setStorageForTile(context, entry.get(), appWidgetId); } else { - Log.e(TAG, "Could not migrate user"); + Log.e(TAG, "Could not migrate user. Delete old storage"); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sp.edit(); + editor.remove(String.valueOf(appWidgetId)); + editor.apply(); } } catch (Exception e) { Log.e(TAG, "Could not query conversations"); @@ -320,17 +335,10 @@ public class PeopleSpaceUtils { } /** Removes stored data when tile is deleted. */ - public static void removeStorageForTile(Context context, int widgetId) { - SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId), - Context.MODE_PRIVATE); - String packageName = widgetSp.getString(PACKAGE_NAME, null); - String shortcutId = widgetSp.getString(SHORTCUT_ID, null); - int userId = widgetSp.getInt(USER_ID, -1); - + public static void removeStorageForTile(Context context, String key, int widgetId) { // Delete widgetId mapping to key. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); - String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId); Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); storedWidgetIds.remove(String.valueOf(widgetId)); editor.putStringSet(key, storedWidgetIds); @@ -338,6 +346,8 @@ public class PeopleSpaceUtils { editor.apply(); // Delete all data specifically mapped to widgetId. + SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId), + Context.MODE_PRIVATE); SharedPreferences.Editor widgetEditor = widgetSp.edit(); widgetEditor.remove(PACKAGE_NAME); widgetEditor.remove(USER_ID); @@ -345,21 +355,6 @@ public class PeopleSpaceUtils { widgetEditor.apply(); } - /** - * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the - * requested update data. - */ - public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId, - String packageName, int userId) { - SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId), - Context.MODE_PRIVATE); - String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING); - int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID); - String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING); - return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId) - && storedUserId == userId; - } - static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context, List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) { if (notificationEntryManager == null) { @@ -396,39 +391,8 @@ public class PeopleSpaceUtils { return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn()); } - /** - * If incoming notification changed tile, store the changes in the tile options. - */ - public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager, - Context context, - StatusBarNotification sbn, - NotificationAction notificationAction, AppWidgetManager appWidgetManager, - int appWidgetId) { - PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context, - appWidgetId); - if (storedTile == null) { - if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to"); - return; - } - if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { - if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId); - storedTile = augmentTileFromNotification(context, storedTile, sbn); - } else { - if (DEBUG) { - Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId); - } - storedTile = storedTile - .toBuilder() - .setNotificationKey(null) - .setNotificationContent(null) - .setNotificationDataUri(null) - .setNotificationCategory(null) - .build(); - } - updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile); - } - - static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile, + /** Augments {@code tile} with the notification content from {@code sbn}. */ + public static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile, StatusBarNotification sbn) { Notification notification = sbn.getNotification(); if (notification == null) { @@ -992,7 +956,7 @@ public class PeopleSpaceUtils { } /** Update app widget options and the current view. */ - private static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager, + public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager, Context context, int appWidgetId, PeopleSpaceTile tile) { updateAppWidgetOptions(appWidgetManager, appWidgetId, tile); RemoteViews views = createRemoteViews(context, tile, appWidgetId); @@ -1065,10 +1029,19 @@ public class PeopleSpaceUtils { * <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li> * </ul> */ + @Nullable public static String getKey(String shortcutId, String packageName, int userId) { + if (!validKey(shortcutId, packageName, userId)) { + return null; + } return shortcutId + "/" + userId + "/" + packageName; } + /** Returns whether the key is valid. */ + public static boolean validKey(String shortcutId, String packageName, int userId) { + return !TextUtils.isEmpty(shortcutId) && !TextUtils.isEmpty(packageName) && userId >= 0; + } + /** Returns the userId associated with a {@link PeopleSpaceTile} */ public static int getUserId(PeopleSpaceTile tile) { return tile.getUserHandle().getIdentifier(); diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 9e5c786b9a63..22ee9e89d0a0 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -16,12 +16,28 @@ package com.android.systemui.people.widget; +import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; +import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; +import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID; +import static com.android.systemui.people.PeopleSpaceUtils.USER_ID; +import static com.android.systemui.people.PeopleSpaceUtils.augmentTileFromNotification; +import static com.android.systemui.people.PeopleSpaceUtils.getPeopleSpaceTile; +import static com.android.systemui.people.PeopleSpaceUtils.getStoredWidgetIds; +import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView; + import android.app.NotificationChannel; +import android.app.Person; +import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; +import android.app.people.PeopleManager; +import android.app.people.PeopleSpaceTile; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.LauncherApps; +import android.content.pm.ShortcutInfo; +import android.net.Uri; import android.os.ServiceManager; import android.os.UserHandle; import android.preference.PreferenceManager; @@ -30,14 +46,18 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.appwidget.IAppWidgetService; -import com.android.systemui.R; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import javax.inject.Inject; @@ -49,52 +69,49 @@ public class PeopleSpaceWidgetManager { private static final String TAG = "PeopleSpaceWidgetMgr"; private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; + private final Object mLock = new Object(); private final Context mContext; - private IAppWidgetService mAppWidgetService; + private LauncherApps mLauncherApps; private AppWidgetManager mAppWidgetManager; - private IPeopleManager mPeopleManager; + private IPeopleManager mIPeopleManager; + private SharedPreferences mSharedPrefs; + private PeopleManager mPeopleManager; + public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); + @GuardedBy("mLock") + public static Map<String, PeopleSpaceWidgetProvider.TileConversationListener> mListeners = + new HashMap<>(); @Inject - public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) { + public PeopleSpaceWidgetManager(Context context) { if (DEBUG) Log.d(TAG, "constructor"); mContext = context; - mAppWidgetService = appWidgetService; mAppWidgetManager = AppWidgetManager.getInstance(context); - mPeopleManager = IPeopleManager.Stub.asInterface( + mIPeopleManager = IPeopleManager.Stub.asInterface( ServiceManager.getService(Context.PEOPLE_SERVICE)); - } - - /** - * Constructor used for testing. - */ - @VisibleForTesting - protected PeopleSpaceWidgetManager(Context context) { - if (DEBUG) Log.d(TAG, "constructor"); - mContext = context; - mAppWidgetService = IAppWidgetService.Stub.asInterface( - ServiceManager.getService(Context.APPWIDGET_SERVICE)); + mLauncherApps = context.getSystemService(LauncherApps.class); + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); + mPeopleManager = mContext.getSystemService(PeopleManager.class); } /** * AppWidgetManager setter used for testing. */ @VisibleForTesting - protected void setAppWidgetManager(IAppWidgetService appWidgetService, - AppWidgetManager appWidgetManager, IPeopleManager peopleManager) { - mAppWidgetService = appWidgetService; + protected void setAppWidgetManager( + AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager, + PeopleManager peopleManager, LauncherApps launcherApps) { mAppWidgetManager = appWidgetManager; + mIPeopleManager = iPeopleManager; mPeopleManager = peopleManager; + mLauncherApps = launcherApps; } /** * Updates People Space widgets. */ - public void updateWidgets() { + public void updateWidgets(int[] widgetIds) { try { if (DEBUG) Log.d(TAG, "updateWidgets called"); - int[] widgetIds = mAppWidgetService.getAppWidgetIds( - new ComponentName(mContext, PeopleSpaceWidgetProvider.class) - ); if (widgetIds.length == 0) { if (DEBUG) Log.d(TAG, "no widgets to update"); return; @@ -103,14 +120,11 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "updating " + widgetIds.length + " widgets"); boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; - if (showSingleConversation) { - PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, - mAppWidgetManager, mPeopleManager); - } else { - mAppWidgetService - .notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds, - R.id.widget_list_view); + synchronized (mLock) { + PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, + mAppWidgetManager, mIPeopleManager); + } } } catch (Exception e) { Log.e(TAG, "Exception: " + e); @@ -121,9 +135,9 @@ public class PeopleSpaceWidgetManager { * Check if any existing People tiles match the incoming notification change, and store the * change in the tile if so. */ - public void updateWidgetWithNotificationChanged(StatusBarNotification sbn, + public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction) { - if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called"); + if (DEBUG) Log.d(TAG, "updateWidgetsWithNotificationChanged called"); boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; if (!showSingleConversation) { @@ -135,26 +149,22 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "Sbn shortcut id is null"); return; } - int[] widgetIds = mAppWidgetService.getAppWidgetIds( + int[] widgetIds = mAppWidgetManager.getAppWidgetIds( new ComponentName(mContext, PeopleSpaceWidgetProvider.class) ); if (widgetIds.length == 0) { Log.d(TAG, "No app widget ids returned"); return; } - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); - int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier(); - String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId); - Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>())); - if (storedWidgetIds.isEmpty()) { - Log.d(TAG, "No stored widget ids"); - return; - } - for (String widgetIdString : storedWidgetIds) { - int widgetId = Integer.parseInt(widgetIdString); - if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey()); - PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext, - sbn, notificationAction, mAppWidgetManager, widgetId); + synchronized (mLock) { + Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, sbnShortcutId, + sbn.getPackageName(), + UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier()); + for (String widgetIdString : storedWidgetIds) { + int widgetId = Integer.parseInt(widgetIdString); + if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey()); + updateStorageAndViewWithNotificationData(sbn, notificationAction, widgetId); + } } } catch (Exception e) { Log.e(TAG, "Exception: " + e); @@ -162,6 +172,91 @@ public class PeopleSpaceWidgetManager { } /** + * Update the tiles associated with the incoming conversation update. + */ + public void updateWidgetsWithConversationChanged(ConversationChannel conversation) { + ShortcutInfo info = conversation.getShortcutInfo(); + synchronized (mLock) { + Set<String> storedWidgetIds = getStoredWidgetIds(mSharedPrefs, info.getId(), + info.getPackage(), + info.getUserId()); + for (String widgetIdString : storedWidgetIds) { + if (DEBUG) { + Log.d(TAG, + "Conversation update for widget " + widgetIdString + " , " + + info.getLabel()); + } + updateStorageAndViewWithConversationData(conversation, + Integer.valueOf(widgetIdString)); + } + } + } + + /** + * Update {@code appWidgetId} with the new data provided by {@code conversation}. + */ + private void updateStorageAndViewWithConversationData(ConversationChannel conversation, + int appWidgetId) { + PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager, + mContext, + appWidgetId); + if (storedTile == null) { + if (DEBUG) Log.d(TAG, "Could not find stored tile to add conversation to"); + return; + } + ShortcutInfo info = conversation.getShortcutInfo(); + Uri uri = null; + if (info.getPersons() != null && info.getPersons().length > 0) { + Person person = info.getPersons()[0]; + uri = person.getUri() == null ? null : Uri.parse(person.getUri()); + } + storedTile = storedTile.toBuilder() + .setUserName(info.getLabel()) + .setUserIcon( + PeopleSpaceTile.convertDrawableToIcon(mLauncherApps.getShortcutIconDrawable( + info, 0)) + ) + .setContactUri(uri) + .setStatuses(conversation.getStatuses()) + .setLastInteractionTimestamp(conversation.getLastEventTimestamp()) + .setIsImportantConversation(conversation.getParentNotificationChannel() != null + && conversation.getParentNotificationChannel().isImportantConversation()) + .build(); + updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile); + } + + /** + * Update {@code appWidgetId} with the new data provided by {@code sbn}. + */ + private void updateStorageAndViewWithNotificationData( + StatusBarNotification sbn, + PeopleSpaceUtils.NotificationAction notificationAction, + int appWidgetId) { + PeopleSpaceTile storedTile = getPeopleSpaceTile(mIPeopleManager, mAppWidgetManager, + mContext, + appWidgetId); + if (storedTile == null) { + if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to"); + return; + } + if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { + if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId); + storedTile = augmentTileFromNotification(mContext, storedTile, sbn); + } else { + if (DEBUG) { + Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId); + } + storedTile = storedTile + .toBuilder() + .setNotificationKey(null) + .setNotificationContent(null) + .setNotificationDataUri(null) + .build(); + } + updateAppWidgetOptionsAndView(mAppWidgetManager, mContext, appWidgetId, storedTile); + } + + /** * Attaches the manager to the pipeline, making it ready to receive events. Should only be * called once. */ @@ -174,8 +269,7 @@ public class PeopleSpaceWidgetManager { @Override public void onNotificationPosted( StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) { - if (DEBUG) Log.d(TAG, "onNotificationPosted"); - updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED); + updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED); } @Override @@ -183,8 +277,7 @@ public class PeopleSpaceWidgetManager { StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap ) { - if (DEBUG) Log.d(TAG, "onNotificationRemoved"); - updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); + updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); } @Override @@ -192,8 +285,7 @@ public class PeopleSpaceWidgetManager { StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap, int reason) { - if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason); - updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); + updateWidgetsWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED); } @Override @@ -204,7 +296,6 @@ public class PeopleSpaceWidgetManager { @Override public void onNotificationsInitialized() { if (DEBUG) Log.d(TAG, "onNotificationsInitialized"); - updateWidgets(); } @Override @@ -213,11 +304,131 @@ public class PeopleSpaceWidgetManager { UserHandle user, NotificationChannel channel, int modificationType) { - if (DEBUG) Log.d(TAG, "onNotificationChannelModified"); if (channel.isConversation()) { - updateWidgets(); + updateWidgets(mAppWidgetManager.getAppWidgetIds( + new ComponentName(mContext, PeopleSpaceWidgetProvider.class) + )); } } }; -}
\ No newline at end of file + /** Adds {@code tile} mapped to {@code appWidgetId}. */ + public void addNewWidget(PeopleSpaceTile tile, int appWidgetId) { + mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED); + synchronized (mLock) { + if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getUserName()); + PeopleSpaceUtils.setStorageForTile(mContext, tile, appWidgetId); + } + try { + if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId()); + mLauncherApps.cacheShortcuts(tile.getPackageName(), + Collections.singletonList(tile.getId()), + tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); + } catch (Exception e) { + Log.w(TAG, "Exception caching shortcut:" + e); + } + PeopleSpaceWidgetProvider provider = new PeopleSpaceWidgetProvider(); + provider.onUpdate(mContext, mAppWidgetManager, new int[]{appWidgetId}); + } + + /** Registers a conversation listener for {@code appWidgetId} if not already registered. */ + public void registerConversationListenerIfNeeded(int widgetId, + PeopleSpaceWidgetProvider.TileConversationListener newListener) { + // Retrieve storage needed for registration. + String packageName; + String shortcutId; + int userId; + String key; + synchronized (mLock) { + SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId), + Context.MODE_PRIVATE); + packageName = widgetSp.getString(PACKAGE_NAME, null); + shortcutId = widgetSp.getString(SHORTCUT_ID, null); + userId = widgetSp.getInt(USER_ID, INVALID_USER_ID); + key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId); + if (key == null) { + if (DEBUG) Log.e(TAG, "Could not register " + widgetId); + return; + } + } + synchronized (mListeners) { + if (mListeners.containsKey(key)) { + if (DEBUG) Log.d(TAG, "Already registered listener"); + return; + } + if (DEBUG) Log.d(TAG, "Register listener for " + widgetId + " with " + key); + mListeners.put(key, newListener); + } + mPeopleManager.registerConversationListener(packageName, + userId, + shortcutId, newListener, + mContext.getMainExecutor()); + } + + /** Deletes all storage, listeners, and caching for {@code appWidgetIds}. */ + public void deleteWidgets(int[] appWidgetIds) { + for (int widgetId : appWidgetIds) { + if (DEBUG) Log.d(TAG, "Widget removed: " + widgetId); + mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED); + // Retrieve storage needed for widget deletion. + String packageName; + String shortcutId; + int userId; + String key; + Set<String> storedWidgetIdsForKey; + synchronized (mLock) { + SharedPreferences widgetSp = mContext.getSharedPreferences(String.valueOf(widgetId), + Context.MODE_PRIVATE); + packageName = widgetSp.getString(PACKAGE_NAME, null); + shortcutId = widgetSp.getString(SHORTCUT_ID, null); + userId = widgetSp.getInt(USER_ID, INVALID_USER_ID); + key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId); + if (key == null) { + if (DEBUG) Log.e(TAG, "Could not delete " + widgetId); + return; + } + storedWidgetIdsForKey = new HashSet<>( + mSharedPrefs.getStringSet(key, new HashSet<>())); + } + synchronized (mLock) { + PeopleSpaceUtils.removeStorageForTile(mContext, key, widgetId); + } + // If another tile with the conversation is still stored, we need to keep the listener. + if (DEBUG) Log.d(TAG, "Stored widget IDs: " + storedWidgetIdsForKey.toString()); + if (storedWidgetIdsForKey.contains(String.valueOf(widgetId)) + && storedWidgetIdsForKey.size() == 1) { + if (DEBUG) Log.d(TAG, "Remove caching and listener"); + unregisterConversationListener(key, widgetId); + uncacheConversationShortcut(shortcutId, packageName, userId); + } + } + } + + /** Unregisters the conversation listener for {@code appWidgetId}. */ + private void unregisterConversationListener(String key, int appWidgetId) { + PeopleSpaceWidgetProvider.TileConversationListener registeredListener; + synchronized (mListeners) { + registeredListener = mListeners.get(key); + if (registeredListener == null) { + if (DEBUG) Log.d(TAG, "Cannot find listener to unregister"); + return; + } + if (DEBUG) Log.d(TAG, "Unregister listener for " + appWidgetId + " with " + key); + mListeners.remove(key); + } + mPeopleManager.unregisterConversationListener(registeredListener); + } + + /** Uncaches the conversation shortcut. */ + private void uncacheConversationShortcut(String shortcutId, String packageName, int userId) { + try { + if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId); + mLauncherApps.uncacheShortcuts(packageName, + Collections.singletonList(shortcutId), + UserHandle.of(userId), + LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); + } catch (Exception e) { + Log.d(TAG, "Exception uncaching 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 90baf56e0137..c0c18471ba32 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java @@ -16,31 +16,17 @@ package com.android.systemui.people.widget; -import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; -import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID; -import static com.android.systemui.people.PeopleSpaceUtils.USER_ID; - -import android.app.PendingIntent; -import android.app.people.IPeopleManager; +import android.annotation.NonNull; +import android.app.people.ConversationChannel; +import android.app.people.PeopleManager; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.LauncherApps; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.provider.Settings; import android.util.Log; -import android.widget.RemoteViews; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.UiEventLoggerImpl; -import com.android.systemui.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.people.PeopleSpaceUtils; -import java.util.Collections; - /** People Space Widget Provider class. */ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { private static final String TAG = "PeopleSpaceWidgetPvd"; @@ -50,7 +36,26 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; public static final String EXTRA_USER_HANDLE = "extra_user_handle"; - public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); + public PeopleSpaceWidgetManager peopleSpaceWidgetManager; + + /** Listener for the shortcut data changes. */ + public class TileConversationListener implements PeopleManager.ConversationListener { + + @Override + public void onConversationUpdate(@NonNull ConversationChannel conversation) { + if (DEBUG) { + Log.d(TAG, + "Received updated conversation: " + + conversation.getShortcutInfo().getLabel()); + } + if (peopleSpaceWidgetManager == null) { + // This shouldn't happen since onUpdate is called at reboot. + Log.e(TAG, "Skipping conversation update: WidgetManager uninitialized"); + return; + } + peopleSpaceWidgetManager.updateWidgetsWithConversationChanged(conversation); + } + } /** Called when widget updates. */ @Override @@ -58,70 +63,32 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { super.onUpdate(context, appWidgetManager, appWidgetIds); if (DEBUG) Log.d(TAG, "onUpdate called"); - boolean showSingleConversation = Settings.Global.getInt(context.getContentResolver(), - Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; - if (showSingleConversation) { - PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds, - appWidgetManager, IPeopleManager.Stub.asInterface( - ServiceManager.getService(Context.PEOPLE_SERVICE))); - return; - } - // Perform this loop procedure for each App Widget that belongs to this provider + ensurePeopleSpaceWidgetManagerInitialized(context); + peopleSpaceWidgetManager.updateWidgets(appWidgetIds); for (int appWidgetId : appWidgetIds) { - RemoteViews views = - new RemoteViews(context.getPackageName(), R.layout.people_space_widget); - - Intent intent = new Intent(context, PeopleSpaceWidgetService.class); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); - views.setRemoteAdapter(R.id.widget_list_view, intent); - - Intent activityIntent = new Intent(context, LaunchConversationActivity.class); - activityIntent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_ACTIVITY_NO_HISTORY - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - PendingIntent pendingIntent = PendingIntent.getActivity( - context, - appWidgetId, - activityIntent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); - views.setPendingIntentTemplate(R.id.widget_list_view, pendingIntent); + PeopleSpaceWidgetProvider.TileConversationListener + newListener = new PeopleSpaceWidgetProvider.TileConversationListener(); + peopleSpaceWidgetManager.registerConversationListenerIfNeeded(appWidgetId, + newListener); + } + return; + } - // Tell the AppWidgetManager to perform an update on the current app widget - appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_list_view); - appWidgetManager.updateAppWidget(appWidgetId, views); + private void ensurePeopleSpaceWidgetManagerInitialized(Context context) { + if (peopleSpaceWidgetManager == null) { + peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context); } } @Override public void onDeleted(Context context, int[] appWidgetIds) { super.onDeleted(context, appWidgetIds); - LauncherApps launcherApps = context.getSystemService(LauncherApps.class); - - for (int widgetId : appWidgetIds) { - if (DEBUG) Log.d(TAG, "Widget removed"); - mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED); - if (launcherApps != null) { - SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId), - Context.MODE_PRIVATE); - String packageName = widgetSp.getString(PACKAGE_NAME, null); - String shortcutId = widgetSp.getString(SHORTCUT_ID, null); - int userId = widgetSp.getInt(USER_ID, -1); + ensurePeopleSpaceWidgetManagerInitialized(context); + peopleSpaceWidgetManager.deleteWidgets(appWidgetIds); + } - if (packageName != null && shortcutId != null && userId != -1) { - try { - if (DEBUG) Log.d(TAG, "Uncaching shortcut for PeopleTile: " + shortcutId); - launcherApps.uncacheShortcuts(packageName, - Collections.singletonList(shortcutId), - UserHandle.of(userId), - LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); - } catch (Exception e) { - Log.d(TAG, "Exception uncaching shortcut:" + e); - } - } - } - PeopleSpaceUtils.removeStorageForTile(context, widgetId); - } + @VisibleForTesting + public void setPeopleSpaceWidgetManager(PeopleSpaceWidgetManager manager) { + peopleSpaceWidgetManager = manager; } } diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 197582104f8e..bd46ffec759b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -351,7 +351,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_screenrecord) - .setContentTitle(getResources().getString(R.string.screenrecord_save_message)) + .setContentTitle(getResources().getString(R.string.screenrecord_save_title)) + .setContentText(getResources().getString(R.string.screenrecord_save_text)) .setContentIntent(PendingIntent.getActivity( this, REQUEST_CODE, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java index 7248bcef621c..151840afcef1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java @@ -44,9 +44,10 @@ public class NotificationBigPictureTemplateViewWrapper extends NotificationTempl private void updateImageTag(StatusBarNotification notification) { final Bundle extras = notification.getNotification().extras; - Icon overRiddenIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG); - if (overRiddenIcon != null) { - mPicture.setTag(ImageTransformState.ICON_TAG, overRiddenIcon); + Icon overriddenIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG); + if (overriddenIcon != null) { + mRightIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon); + mLeftIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java index e9934c0053b0..e0b58125aabd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java @@ -51,7 +51,8 @@ import com.android.systemui.statusbar.notification.row.HybridNotificationView; public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper { private final int mFullHeaderTranslation; - protected ImageView mPicture; + protected ImageView mRightIcon; + protected ImageView mLeftIcon; private ProgressBar mProgressBar; private TextView mTitle; private TextView mText; @@ -140,9 +141,14 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp } private void resolveTemplateViews(StatusBarNotification notification) { - mPicture = mView.findViewById(com.android.internal.R.id.right_icon); - if (mPicture != null) { - mPicture.setTag(ImageTransformState.ICON_TAG, + mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon); + if (mRightIcon != null) { + mRightIcon.setTag(ImageTransformState.ICON_TAG, + notification.getNotification().getLargeIcon()); + } + mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon); + if (mLeftIcon != null) { + mLeftIcon.setTag(ImageTransformState.ICON_TAG, notification.getNotification().getLargeIcon()); } mTitle = mView.findViewById(com.android.internal.R.id.title); @@ -240,9 +246,9 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp resolveTemplateViews(row.getEntry().getSbn()); super.onContentUpdated(row); // With the modern templates, a large icon visually overlaps the header, so we can't - // simply hide the header -- just show the + // hide the header, we must show it. mCanHideHeader = mNotificationHeader != null - && (mPicture == null || mPicture.getVisibility() != VISIBLE); + && (mRightIcon == null || mRightIcon.getVisibility() != VISIBLE); if (row.getHeaderVisibleAmount() != DEFAULT_HEADER_VISIBLE_AMOUNT) { setHeaderVisibleAmount(row.getHeaderVisibleAmount()); } @@ -260,14 +266,15 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT, mText); } - if (mPicture != null) { + if (mRightIcon != null) { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE, - mPicture); + mRightIcon); } if (mProgressBar != null) { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS, mProgressBar); } + addViewsTransformingToSimilar(mLeftIcon); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index bf36435b78c9..0807f8aa5607 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -4398,7 +4398,10 @@ public class StatusBar extends SystemUI implements DemoMode, */ public static Bundle getActivityOptions(int displayId, @Nullable RemoteAnimationAdapter animationAdapter) { - return getDefaultActivityOptions(animationAdapter).toBundle(); + ActivityOptions options = getDefaultActivityOptions(animationAdapter); + options.setLaunchDisplayId(displayId); + options.setCallerDisplayId(displayId); + return options.toBundle(); } /** 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 800d8593035d..f60fa099feaa 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 @@ -19,14 +19,21 @@ package com.android.systemui.people.widget; import static android.app.Notification.CATEGORY_MISSED_CALL; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY; +import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY; +import static android.app.people.ConversationStatus.ACTIVITY_GAME; +import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE; +import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; +import static com.android.systemui.people.PeopleSpaceUtils.USER_ID; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -38,12 +45,15 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.Person; import android.app.people.ConversationChannel; +import android.app.people.ConversationStatus; import android.app.people.IPeopleManager; +import android.app.people.PeopleManager; import android.app.people.PeopleSpaceTile; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; import android.graphics.drawable.Icon; import android.net.Uri; @@ -56,7 +66,6 @@ import android.testing.AndroidTestingRunner; import androidx.preference.PreferenceManager; import androidx.test.filters.SmallTest; -import com.android.internal.appwidget.IAppWidgetService; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.people.PeopleSpaceUtils; @@ -76,7 +85,9 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; @SmallTest @@ -99,6 +110,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private static final String NOTIFICATION_CONTENT = "message text"; private static final Uri URI = Uri.parse("fake_uri"); private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android); + private static final String KEY = PeopleSpaceUtils.getKey(SHORTCUT_ID, TEST_PACKAGE_A, 0); private static final Person PERSON = new Person.Builder() .setName("name") .setKey("abc") @@ -121,12 +133,15 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Mock private NotificationListener mListenerService; - @Mock - private IAppWidgetService mIAppWidgetService; + @Mock private AppWidgetManager mAppWidgetManager; @Mock private IPeopleManager mIPeopleManager; + @Mock + private PeopleManager mPeopleManager; + @Mock + private LauncherApps mLauncherApps; @Captor private ArgumentCaptor<NotificationHandler> mListenerCaptor; @@ -136,13 +151,19 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private final NoManSimulator mNoMan = new NoManSimulator(); private final FakeSystemClock mClock = new FakeSystemClock(); + private PeopleSpaceWidgetProvider mProvider; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mLauncherApps = mock(LauncherApps.class); mManager = new PeopleSpaceWidgetManager(mContext); - mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager); + mManager.setAppWidgetManager(mAppWidgetManager, mIPeopleManager, mPeopleManager, + mLauncherApps); mManager.attach(mListenerService); + mProvider = new PeopleSpaceWidgetProvider(); + mProvider.setPeopleSpaceWidgetManager(mManager); verify(mListenerService).addNotificationHandler(mListenerCaptor.capture()); NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue()); @@ -166,7 +187,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception { int[] widgetIdsArray = {}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbn = createNotification( OTHER_SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false); @@ -182,7 +203,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception { int[] widgetIdsArray = {}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); Notification notificationWithoutShortcut = new Notification.Builder(mContext) .setContentTitle("TEST_TITLE") @@ -206,7 +227,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception { int[] widgetIdsArray = {}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithoutPackageName = new SbnBuilder() .setNotification(createMessagingStyleNotification( @@ -224,7 +245,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception { int[] widgetIdsArray = {1}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotificationChannel channel = new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); @@ -240,7 +261,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testUpdateAppWidgetIfConversationChannelModified() throws Exception { int[] widgetIdsArray = {1}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotificationChannel channel = new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); @@ -257,7 +278,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbn = createNotification( OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); @@ -277,7 +298,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0); int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder() .setNotification(createMessagingStyleNotification( @@ -298,7 +319,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbn = createNotification( OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); @@ -318,7 +339,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder() .setNotification(createMessagingStyleNotification( @@ -339,9 +360,74 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test + public void testDoNotUpdateStatusPostedIfDifferentShortcutId() throws Exception { + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + ConversationStatus status1 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID, + ACTIVITY_GAME).setDescription("Playing a game!").build(); + ConversationStatus status2 = new ConversationStatus.Builder(OTHER_SHORTCUT_ID, + ACTIVITY_BIRTHDAY).build(); + ConversationChannel conversationChannel = getConversationWithShortcutId(OTHER_SHORTCUT_ID, + Arrays.asList(status1, status2)); + mManager.updateWidgetsWithConversationChanged(conversationChannel); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, never()) + .updateAppWidgetOptions(anyInt(), any()); + verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), + any()); + } + + @Test + public void testUpdateStatusPostedIfExistingTile() throws Exception { + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID, + ACTIVITY_GAME).setDescription("Playing a game!").build(); + ConversationChannel conversationChannel = getConversationWithShortcutId(SHORTCUT_ID, + Arrays.asList(status)); + mManager.updateWidgetsWithConversationChanged(conversationChannel); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + Bundle bundle = mBundleArgumentCaptor.getValue(); + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + assertThat(tile.getStatuses()).containsExactly(status); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testUpdateStatusPostedOnTwoExistingTiles() throws Exception { + addSecondWidgetForPersonTile(); + + ConversationStatus status = new ConversationStatus.Builder(SHORTCUT_ID, + ACTIVITY_ANNIVERSARY).build(); + ConversationChannel conversationChannel = getConversationWithShortcutId(SHORTCUT_ID, + Arrays.asList(status)); + mManager.updateWidgetsWithConversationChanged(conversationChannel); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT), + any()); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test public void testUpdateNotificationPostedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(createNotification( @@ -362,17 +448,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception { - Bundle options = new Bundle(); - options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); - when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT))) - .thenReturn(options); - // Set the same Person associated on another People Tile widget ID. - setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); - setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT); - - int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, - SECOND_WIDGET_ID_WITH_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + addSecondWidgetForPersonTile(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(createNotification( @@ -395,19 +471,9 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson() throws Exception { - Bundle options = new Bundle(); - options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); - when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT))) - .thenReturn(options); - // Set the same Person associated on another People Tile widget ID. - setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); - setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT); + addSecondWidgetForPersonTile(); - int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, - SECOND_WIDGET_ID_WITH_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - - PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT); + PeopleSpaceUtils.removeStorageForTile(mContext, KEY, SECOND_WIDGET_ID_WITH_SHORTCUT); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(createNotification( SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false)) @@ -430,7 +496,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { public void testUpdateMissedCallNotificationWithoutContentPostedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() @@ -456,7 +522,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { public void testUpdateMissedCallNotificationWithContentPostedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() @@ -480,7 +546,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { @Test public void testUpdateNotificationRemovedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; - when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbn = createNotification( SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); @@ -502,14 +568,82 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { any()); } + @Test + public void testDeleteAllWidgetsForConversationsUncachesShortcutAndRemovesListeners() { + addSecondWidgetForPersonTile(); + mProvider.onUpdate(mContext, mAppWidgetManager, + new int[]{WIDGET_ID_WITH_SHORTCUT, SECOND_WIDGET_ID_WITH_SHORTCUT}); + + // Delete only one widget for the conversation. + mManager.deleteWidgets(new int[]{WIDGET_ID_WITH_SHORTCUT}); + + // Check deleted storage. + SharedPreferences widgetSp = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID_WITH_SHORTCUT), + Context.MODE_PRIVATE); + assertThat(widgetSp.getString(PACKAGE_NAME, null)).isNull(); + assertThat(widgetSp.getString(SHORTCUT_ID, null)).isNull(); + assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + assertThat(sp.getStringSet(KEY, new HashSet<>())).containsExactly( + String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT)); + // Check listener & shortcut caching remain for other widget. + verify(mPeopleManager, never()).unregisterConversationListener(any()); + verify(mLauncherApps, never()).uncacheShortcuts(eq(TEST_PACKAGE_A), + eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)), + eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS)); + + // Delete all widgets for the conversation. + mProvider.onDeleted(mContext, new int[]{SECOND_WIDGET_ID_WITH_SHORTCUT}); + + // Check deleted storage. + SharedPreferences secondWidgetSp = mContext.getSharedPreferences( + String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT), + Context.MODE_PRIVATE); + assertThat(secondWidgetSp.getString(PACKAGE_NAME, null)).isNull(); + assertThat(secondWidgetSp.getString(SHORTCUT_ID, null)).isNull(); + assertThat(secondWidgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID); + assertThat(sp.getStringSet(KEY, new HashSet<>())).isEmpty(); + // Check listener is removed and shortcut is uncached. + verify(mPeopleManager, times(1)).unregisterConversationListener(any()); + verify(mLauncherApps, times(1)).uncacheShortcuts(eq(TEST_PACKAGE_A), + eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)), + eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS)); + } + + /** + * Adds another widget for {@code PERSON_TILE} with widget ID: {@code + * SECOND_WIDGET_ID_WITH_SHORTCUT}. + */ + private void addSecondWidgetForPersonTile() { + Bundle options = new Bundle(); + options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE); + when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT))) + .thenReturn(options); + // Set the same Person associated on another People Tile widget ID. + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT); + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT, + SECOND_WIDGET_ID_WITH_SHORTCUT}; + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + } + /** * Returns a single conversation associated with {@code shortcutId}. */ private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception { + return getConversationWithShortcutId(shortcutId, Arrays.asList()); + } + + /** + * Returns a single conversation associated with {@code shortcutId} and {@code statuses}. + */ + private ConversationChannel getConversationWithShortcutId(String shortcutId, + List<ConversationStatus> statuses) throws Exception { ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel( "name").build(); ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null, - 0L, false); + 0L, false, false, statuses); return convo; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index b1b71ccba8ce..999d2822c928 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -50,11 +50,11 @@ import android.provider.Settings; import android.provider.Settings.Global; import android.telephony.CellSignalStrength; import android.telephony.NetworkRegistrationInfo; -import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; import android.testing.TestableLooper; @@ -106,7 +106,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected NetworkControllerImpl mNetworkController; protected MobileSignalController mMobileSignalController; - protected PhoneStateListener mPhoneStateListener; protected SignalStrength mSignalStrength; protected ServiceState mServiceState; protected TelephonyDisplayInfo mTelephonyDisplayInfo; @@ -250,8 +249,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase { setDefaultSubId(mSubId); setSubscriptions(mSubId); mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId); - mPhoneStateListener = mMobileSignalController.mMobileStatusTracker.getPhoneStateListener(); - ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackArg = ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); verify(mMockCm, atLeastOnce()) @@ -455,18 +452,16 @@ public class NetworkControllerBaseTest extends SysuiTestCase { private void updateSignalStrength() { Log.d(TAG, "Sending Signal Strength: " + mSignalStrength); - mPhoneStateListener.onSignalStrengthsChanged(mSignalStrength); + mMobileSignalController.mMobileStatusTracker.getTelephonyCallback() + .onSignalStrengthsChanged(mSignalStrength); } protected void updateServiceState() { Log.d(TAG, "Sending Service State: " + mServiceState); - mPhoneStateListener.onServiceStateChanged(mServiceState); - mPhoneStateListener.onDisplayInfoChanged(mTelephonyDisplayInfo); - } - - public void updateCallState(int state) { - // Inputs not currently used in NetworkControllerImpl. - mPhoneStateListener.onCallStateChanged(state, "0123456789"); + mMobileSignalController.mMobileStatusTracker.getTelephonyCallback() + .onServiceStateChanged(mServiceState); + mMobileSignalController.mMobileStatusTracker.getTelephonyCallback() + .onDisplayInfoChanged(mTelephonyDisplayInfo); } public void updateDataConnectionState(int dataState, int dataNetType) { @@ -478,16 +473,19 @@ public class NetworkControllerBaseTest extends SysuiTestCase { when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN)) .thenReturn(fakeRegInfo); when(mTelephonyDisplayInfo.getNetworkType()).thenReturn(dataNetType); - mPhoneStateListener.onDataConnectionStateChanged(dataState, dataNetType); + mMobileSignalController.mMobileStatusTracker.getTelephonyCallback() + .onDataConnectionStateChanged(dataState, dataNetType); } public void updateDataActivity(int dataActivity) { - mPhoneStateListener.onDataActivity(dataActivity); + mMobileSignalController.mMobileStatusTracker.getTelephonyCallback() + .onDataActivity(dataActivity); } public void setCarrierNetworkChange(boolean enable) { Log.d(TAG, "setCarrierNetworkChange(" + enable + ")"); - mPhoneStateListener.onCarrierNetworkChange(enable); + mMobileSignalController.mMobileStatusTracker.getTelephonyCallback() + .onCarrierNetworkChange(enable); } protected void verifyHasNoSims(boolean hasNoSimsVisible) { diff --git a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java index bafb641dcc9e..6828dd916701 100644 --- a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java +++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java @@ -40,29 +40,34 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection private final IAccessibilityInteractionConnectionCallback mServiceCallback; private final IAccessibilityInteractionConnection mConnectionWithReplacementActions; private final int mInteractionId; + private final int mNodeWithReplacementActionsInteractionId; private final Object mLock = new Object(); @GuardedBy("mLock") - List<AccessibilityNodeInfo> mNodesWithReplacementActions; + private boolean mReplacementNodeIsReadyOrFailed; + + @GuardedBy("mLock") + AccessibilityNodeInfo mNodeWithReplacementActions; @GuardedBy("mLock") List<AccessibilityNodeInfo> mNodesFromOriginalWindow; @GuardedBy("mLock") + boolean mSetFindNodeFromOriginalWindowCalled = false; + + @GuardedBy("mLock") AccessibilityNodeInfo mNodeFromOriginalWindow; - // Keep track of whether or not we've been called back for a single node @GuardedBy("mLock") - boolean mSingleNodeCallbackHappened; + boolean mSetFindNodesFromOriginalWindowCalled = false; + - // Keep track of whether or not we've been called back for multiple node @GuardedBy("mLock") - boolean mMultiNodeCallbackHappened; + List<AccessibilityNodeInfo> mPrefetchedNodesFromOriginalWindow; - // We shouldn't get any more callbacks after we've called back the original service, but - // keep track to make sure we catch such strange things @GuardedBy("mLock") - boolean mDone; + boolean mSetPrefetchFromOriginalWindowCalled = false; + public ActionReplacingCallback(IAccessibilityInteractionConnectionCallback serviceCallback, IAccessibilityInteractionConnection connectionWithReplacementActions, @@ -70,19 +75,20 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection mServiceCallback = serviceCallback; mConnectionWithReplacementActions = connectionWithReplacementActions; mInteractionId = interactionId; + mNodeWithReplacementActionsInteractionId = interactionId + 1; // Request the root node of the replacing window final long identityToken = Binder.clearCallingIdentity(); try { mConnectionWithReplacementActions.findAccessibilityNodeInfoByAccessibilityId( - AccessibilityNodeInfo.ROOT_NODE_ID, null, interactionId + 1, this, 0, + AccessibilityNodeInfo.ROOT_NODE_ID, null, + mNodeWithReplacementActionsInteractionId, this, 0, interrogatingPid, interrogatingTid, null, null); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()"); } - // Pretend we already got a (null) list of replacement nodes - mMultiNodeCallbackHappened = true; + mReplacementNodeIsReadyOrFailed = true; } finally { Binder.restoreCallingIdentity(identityToken); } @@ -90,46 +96,67 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection @Override public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info, int interactionId) { - boolean readyForCallback; - synchronized(mLock) { + synchronized (mLock) { if (interactionId == mInteractionId) { mNodeFromOriginalWindow = info; + mSetFindNodeFromOriginalWindowCalled = true; + } else if (interactionId == mNodeWithReplacementActionsInteractionId) { + mNodeWithReplacementActions = info; + mReplacementNodeIsReadyOrFailed = true; } else { Slog.e(LOG_TAG, "Callback with unexpected interactionId"); return; } - - mSingleNodeCallbackHappened = true; - readyForCallback = mMultiNodeCallbackHappened; - } - if (readyForCallback) { - replaceInfoActionsAndCallService(); } + replaceInfoActionsAndCallServiceIfReady(); } @Override public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos, int interactionId) { - boolean callbackForSingleNode; - boolean callbackForMultipleNodes; - synchronized(mLock) { + synchronized (mLock) { if (interactionId == mInteractionId) { mNodesFromOriginalWindow = infos; - } else if (interactionId == mInteractionId + 1) { - mNodesWithReplacementActions = infos; + mSetFindNodesFromOriginalWindowCalled = true; + } else if (interactionId == mNodeWithReplacementActionsInteractionId) { + setNodeWithReplacementActionsFromList(infos); + mReplacementNodeIsReadyOrFailed = true; } else { Slog.e(LOG_TAG, "Callback with unexpected interactionId"); return; } - callbackForSingleNode = mSingleNodeCallbackHappened; - callbackForMultipleNodes = mMultiNodeCallbackHappened; - mMultiNodeCallbackHappened = true; } - if (callbackForSingleNode) { - replaceInfoActionsAndCallService(); + replaceInfoActionsAndCallServiceIfReady(); + } + + @Override + public void setPrefetchAccessibilityNodeInfoResult(List<AccessibilityNodeInfo> infos, + int interactionId) + throws RemoteException { + synchronized (mLock) { + if (interactionId == mInteractionId) { + mPrefetchedNodesFromOriginalWindow = infos; + mSetPrefetchFromOriginalWindowCalled = true; + } else { + Slog.e(LOG_TAG, "Callback with unexpected interactionId"); + return; + } } - if (callbackForMultipleNodes) { - replaceInfosActionsAndCallService(); + replaceInfoActionsAndCallServiceIfReady(); + } + + private void replaceInfoActionsAndCallServiceIfReady() { + replaceInfoActionsAndCallService(); + replaceInfosActionsAndCallService(); + replacePrefetchInfosActionsAndCallService(); + } + + private void setNodeWithReplacementActionsFromList(List<AccessibilityNodeInfo> infos) { + for (int i = 0; i < infos.size(); i++) { + AccessibilityNodeInfo info = infos.get(i); + if (info.getSourceNodeId() == AccessibilityNodeInfo.ROOT_NODE_ID) { + mNodeWithReplacementActions = info; + } } } @@ -142,55 +169,81 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection private void replaceInfoActionsAndCallService() { final AccessibilityNodeInfo nodeToReturn; + boolean doCallback = false; synchronized (mLock) { - if (mDone) { - if (DEBUG) { - Slog.e(LOG_TAG, "Extra callback"); - } - return; - } - if (mNodeFromOriginalWindow != null) { + doCallback = mReplacementNodeIsReadyOrFailed + && mSetFindNodeFromOriginalWindowCalled; + if (doCallback && mNodeFromOriginalWindow != null) { replaceActionsOnInfoLocked(mNodeFromOriginalWindow); + mSetFindNodeFromOriginalWindowCalled = false; } - recycleReplaceActionNodesLocked(); nodeToReturn = mNodeFromOriginalWindow; - mDone = true; } - try { - mServiceCallback.setFindAccessibilityNodeInfoResult(nodeToReturn, mInteractionId); - } catch (RemoteException re) { - if (DEBUG) { - Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfoResult"); + if (doCallback) { + try { + mServiceCallback.setFindAccessibilityNodeInfoResult(nodeToReturn, mInteractionId); + } catch (RemoteException re) { + if (DEBUG) { + Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfoResult"); + } } } } private void replaceInfosActionsAndCallService() { - final List<AccessibilityNodeInfo> nodesToReturn; + List<AccessibilityNodeInfo> nodesToReturn = null; + boolean doCallback = false; synchronized (mLock) { - if (mDone) { + doCallback = mReplacementNodeIsReadyOrFailed + && mSetFindNodesFromOriginalWindowCalled; + if (doCallback) { + nodesToReturn = replaceActionsLocked(mNodesFromOriginalWindow); + mSetFindNodesFromOriginalWindowCalled = false; + } + } + if (doCallback) { + try { + mServiceCallback.setFindAccessibilityNodeInfosResult(nodesToReturn, mInteractionId); + } catch (RemoteException re) { if (DEBUG) { - Slog.e(LOG_TAG, "Extra callback"); + Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfosResult"); } - return; } - if (mNodesFromOriginalWindow != null) { - for (int i = 0; i < mNodesFromOriginalWindow.size(); i++) { - replaceActionsOnInfoLocked(mNodesFromOriginalWindow.get(i)); + } + } + + private void replacePrefetchInfosActionsAndCallService() { + List<AccessibilityNodeInfo> nodesToReturn = null; + boolean doCallback = false; + synchronized (mLock) { + doCallback = mReplacementNodeIsReadyOrFailed + && mSetPrefetchFromOriginalWindowCalled; + if (doCallback) { + nodesToReturn = replaceActionsLocked(mPrefetchedNodesFromOriginalWindow); + mSetPrefetchFromOriginalWindowCalled = false; + } + } + if (doCallback) { + try { + mServiceCallback.setPrefetchAccessibilityNodeInfoResult( + nodesToReturn, mInteractionId); + } catch (RemoteException re) { + if (DEBUG) { + Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfosResult"); } } - recycleReplaceActionNodesLocked(); - nodesToReturn = (mNodesFromOriginalWindow == null) - ? null : new ArrayList<>(mNodesFromOriginalWindow); - mDone = true; } - try { - mServiceCallback.setFindAccessibilityNodeInfosResult(nodesToReturn, mInteractionId); - } catch (RemoteException re) { - if (DEBUG) { - Slog.e(LOG_TAG, "Failed to setFindAccessibilityNodeInfosResult"); + } + + @GuardedBy("mLock") + private List<AccessibilityNodeInfo> replaceActionsLocked(List<AccessibilityNodeInfo> infos) { + if (infos != null) { + for (int i = 0; i < infos.size(); i++) { + replaceActionsOnInfoLocked(infos.get(i)); } } + return (infos == null) + ? null : new ArrayList<>(infos); } @GuardedBy("mLock") @@ -204,40 +257,22 @@ public class ActionReplacingCallback extends IAccessibilityInteractionConnection info.setDismissable(false); // We currently only replace actions for the root node if ((info.getSourceNodeId() == AccessibilityNodeInfo.ROOT_NODE_ID) - && mNodesWithReplacementActions != null) { - // This list should always contain a single node with the root ID - for (int i = 0; i < mNodesWithReplacementActions.size(); i++) { - AccessibilityNodeInfo nodeWithReplacementActions = - mNodesWithReplacementActions.get(i); - if (nodeWithReplacementActions.getSourceNodeId() - == AccessibilityNodeInfo.ROOT_NODE_ID) { - List<AccessibilityAction> actions = nodeWithReplacementActions.getActionList(); - if (actions != null) { - for (int j = 0; j < actions.size(); j++) { - info.addAction(actions.get(j)); - } - // The PIP needs to be able to take accessibility focus - info.addAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS); - info.addAction(AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS); - } - info.setClickable(nodeWithReplacementActions.isClickable()); - info.setFocusable(nodeWithReplacementActions.isFocusable()); - info.setContextClickable(nodeWithReplacementActions.isContextClickable()); - info.setScrollable(nodeWithReplacementActions.isScrollable()); - info.setLongClickable(nodeWithReplacementActions.isLongClickable()); - info.setDismissable(nodeWithReplacementActions.isDismissable()); + && mNodeWithReplacementActions != null) { + List<AccessibilityAction> actions = mNodeWithReplacementActions.getActionList(); + if (actions != null) { + for (int j = 0; j < actions.size(); j++) { + info.addAction(actions.get(j)); } + // The PIP needs to be able to take accessibility focus + info.addAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS); + info.addAction(AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS); } + info.setClickable(mNodeWithReplacementActions.isClickable()); + info.setFocusable(mNodeWithReplacementActions.isFocusable()); + info.setContextClickable(mNodeWithReplacementActions.isContextClickable()); + info.setScrollable(mNodeWithReplacementActions.isScrollable()); + info.setLongClickable(mNodeWithReplacementActions.isLongClickable()); + info.setDismissable(mNodeWithReplacementActions.isDismissable()); } } - - @GuardedBy("mLock") - private void recycleReplaceActionNodesLocked() { - if (mNodesWithReplacementActions == null) return; - for (int i = mNodesWithReplacementActions.size() - 1; i >= 0; i--) { - AccessibilityNodeInfo nodeWithReplacementAction = mNodesWithReplacementActions.get(i); - nodeWithReplacementAction.recycle(); - } - mNodesWithReplacementActions = null; - } } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index a3a0cb402c76..52237c934797 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -38,6 +38,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainRunna import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MINUTES; +import android.Manifest; import android.annotation.CheckResult; import android.annotation.NonNull; import android.annotation.Nullable; @@ -409,6 +410,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind checkCallerIsSystemOr(callingPackage); int userId = getCallingUserId(); checkUsesFeature(callingPackage, userId); + checkProfilePermissions(request); mFindDeviceCallback = callback; mRequest = request; @@ -519,6 +521,21 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } + private void checkProfilePermissions(AssociationRequest request) { + checkProfilePermission(request, + AssociationRequest.DEVICE_PROFILE_WATCH, + Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH); + } + + private void checkProfilePermission( + AssociationRequest request, String profile, String permission) { + if (profile.equals(request.getDeviceProfile()) + && getContext().checkCallingOrSelfPermission(permission) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Using " + profile + " requires " + permission); + } + } + @Override public PendingIntent requestNotificationAccess(ComponentName component) throws RemoteException { diff --git a/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java index fe3042d9da54..d83e2fd2090d 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/services/core/java/com/android/server/BootReceiver.java @@ -16,6 +16,8 @@ package com.android.server; +import static android.system.OsConstants.O_RDONLY; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -24,12 +26,15 @@ import android.os.Build; import android.os.DropBoxManager; import android.os.Environment; import android.os.FileUtils; +import android.os.MessageQueue.OnFileDescriptorEventListener; import android.os.RecoverySystem; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.storage.StorageManager; import android.provider.Downloads; +import android.system.ErrnoException; +import android.system.Os; import android.text.TextUtils; import android.util.AtomicFile; import android.util.EventLog; @@ -46,11 +51,15 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.BufferedReader; import java.io.File; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.regex.Matcher; @@ -116,6 +125,12 @@ public class BootReceiver extends BroadcastReceiver { private static final String METRIC_SYSTEM_SERVER = "shutdown_system_server"; private static final String METRIC_SHUTDOWN_TIME_START = "begin_shutdown"; + // Location of ftrace pipe for notifications from kernel memory tools like KFENCE and KASAN. + private static final String ERROR_REPORT_TRACE_PIPE = + "/sys/kernel/tracing/instances/bootreceiver/trace_pipe"; + // Avoid reporing the same bug from processDmesg() twice. + private static String sLastReportedBug = null; + @Override public void onReceive(final Context context, Intent intent) { // Log boot events in the background to avoid blocking the main thread with I/O @@ -143,6 +158,209 @@ public class BootReceiver extends BroadcastReceiver { } }.start(); + + FileDescriptor tracefd = null; + try { + tracefd = Os.open(ERROR_REPORT_TRACE_PIPE, O_RDONLY, 0600); + } catch (ErrnoException e) { + Slog.wtf(TAG, "Could not open " + ERROR_REPORT_TRACE_PIPE, e); + return; + } + + /* + * Event listener to watch for memory tool error reports. + * We read from /sys/kernel/tracing/instances/bootreceiver/trace_pipe (set up by the + * system), which will print an ftrace event when a memory corruption is detected in the + * kernel. + * When an error is detected, we run the dmesg shell command and process its output. + */ + OnFileDescriptorEventListener traceCallback = new OnFileDescriptorEventListener() { + final int mBufferSize = 1024; + byte[] mTraceBuffer = new byte[mBufferSize]; + @Override + public int onFileDescriptorEvents(FileDescriptor fd, int events) { + /* + * Read from the tracing pipe set up to monitor the error_report_end events. + * When a tracing event occurs, the kernel writes a short (~100 bytes) line to the + * pipe, e.g.: + * ...-11210 [004] d..1 285.322307: error_report_end: [kfence] ffffff8938a05000 + * The buffer size we use for reading should be enough to read the whole + * line, but to be on the safe side we keep reading until the buffer + * contains a '\n' character. In the unlikely case of a very buggy kernel + * the buffer may contain multiple tracing events that cannot be attributed + * to particular error reports. In that case the latest error report + * residing in dmesg is picked. + */ + try { + int nbytes = Os.read(fd, mTraceBuffer, 0, mBufferSize); + if (nbytes > 0) { + String readStr = new String(mTraceBuffer); + if (readStr.indexOf("\n") == -1) { + return OnFileDescriptorEventListener.EVENT_INPUT; + } + processDmesg(context); + } + } catch (Exception e) { + Slog.wtf(TAG, "Error processing dmesg output", e); + return 0; // Unregister the handler. + } + return OnFileDescriptorEventListener.EVENT_INPUT; + } + }; + + IoThread.get().getLooper().getQueue().addOnFileDescriptorEventListener( + tracefd, OnFileDescriptorEventListener.EVENT_INPUT, traceCallback); + + } + + /** + * Check whether it is safe to collect this dmesg line or not. + * + * We only consider lines belonging to KASAN or KFENCE reports, but those may still contain + * user information, namely the process name: + * + * [ 69.547684] [ T6006]c7 6006 CPU: 7 PID: 6006 Comm: sh Tainted: G S C O ... + * + * hardware information: + * + * [ 69.558923] [ T6006]c7 6006 Hardware name: <REDACTED> + * + * or register dump (in KASAN reports only): + * + * ... RIP: 0033:0x7f96443109da + * ... RSP: 002b:00007ffcf0b51b08 EFLAGS: 00000202 ORIG_RAX: 00000000000000af + * ... RAX: ffffffffffffffda RBX: 000055dc3ee521a0 RCX: 00007f96443109da + * + * (on x86_64) + * + * ... pc : lpm_cpuidle_enter+0x258/0x384 + * ... lr : lpm_cpuidle_enter+0x1d4/0x384 + * ... sp : ffffff800820bea0 + * ... x29: ffffff800820bea0 x28: ffffffc2305f3ce0 + * ... ... + * ... x9 : 0000000000000001 x8 : 0000000000000000 + * (on ARM64) + * + * We therefore omit the lines that contain "Comm:", "Hardware name:", or match the general + * purpose register regexp. + * + * @param line single line of `dmesg` output. + * @return updated line with sensitive data removed, or null if the line must be skipped. + */ + public static String stripSensitiveData(String line) { + /* + * General purpose register names begin with "R" on x86_64 and "x" on ARM64. The letter is + * followed by two symbols (numbers, letters or spaces) and a colon, which is followed by a + * 16-digit hex number. The optional "_" prefix accounts for ORIG_RAX on x86. + */ + final String registerRegex = "[ _][Rx]..: [0-9a-f]{16}"; + final Pattern registerPattern = Pattern.compile(registerRegex); + + final String corruptionRegex = "Detected corrupted memory at 0x[0-9a-f]+"; + final Pattern corruptionPattern = Pattern.compile(corruptionRegex); + + if (line.contains("Comm: ") || line.contains("Hardware name: ")) return null; + if (registerPattern.matcher(line).find()) return null; + + Matcher cm = corruptionPattern.matcher(line); + if (cm.find()) return cm.group(0); + return line; + } + + /* + * Search dmesg output for the last error report from KFENCE or KASAN and copy it to Dropbox. + * + * Example report printed by the kernel (redacted to fit into 100 column limit): + * [ 69.236673] [ T6006]c7 6006 ========================================================= + * [ 69.245688] [ T6006]c7 6006 BUG: KFENCE: out-of-bounds in kfence_handle_page_fault + * [ 69.245688] [ T6006]c7 6006 + * [ 69.257816] [ T6006]c7 6006 Out-of-bounds access at 0xffffffca75c45000 (...) + * [ 69.267102] [ T6006]c7 6006 kfence_handle_page_fault+0x1bc/0x208 + * [ 69.273536] [ T6006]c7 6006 __do_kernel_fault+0xa8/0x11c + * ... + * [ 69.355427] [ T6006]c7 6006 kfence-#2 [0xffffffca75c46f30-0xffffffca75c46fff, ... + * [ 69.366938] [ T6006]c7 6006 __d_alloc+0x3c/0x1b4 + * [ 69.371946] [ T6006]c7 6006 d_alloc_parallel+0x48/0x538 + * [ 69.377578] [ T6006]c7 6006 __lookup_slow+0x60/0x15c + * ... + * [ 69.547684] [ T6006]c7 6006 CPU: 7 PID: 6006 Comm: sh Tainted: G S C O ... + * [ 69.558923] [ T6006]c7 6006 Hardware name: <REDACTED> + * [ 69.567059] [ T6006]c7 6006 ========================================================= + * + * We rely on the kernel printing task/CPU ID for every log line (CONFIG_PRINTK_CALLER=y). + * E.g. for the above report the task ID is T6006. Report lines may interleave with lines + * printed by other kernel tasks, which will have different task IDs, so in order to collect + * the report we: + * - find the next occurrence of the "BUG: " line in the kernel log, parse it to obtain the + * task ID and the tool name; + * - scan the rest of dmesg output and pick every line that has the same task ID, until we + * encounter a horizontal ruler, i.e.: + * [ 69.567059] [ T6006]c7 6006 ====================================================== + * - add that line to the error report, unless it contains sensitive information (see + * logLinePotentiallySensitive()) + * - repeat the above steps till the last report is found. + */ + private void processDmesg(Context ctx) throws IOException { + + /* + * Only SYSTEM_KASAN_ERROR_REPORT and SYSTEM_KFENCE_ERROR_REPORT are supported at the + * moment. + */ + final String[] bugTypes = new String[] { "KASAN", "KFENCE" }; + final String tsRegex = "^\\[[^]]+\\] "; + final String bugRegex = + tsRegex + "\\[([^]]+)\\].*BUG: (" + String.join("|", bugTypes) + "):"; + final Pattern bugPattern = Pattern.compile(bugRegex); + + Process p = new ProcessBuilder("/system/bin/timeout", "-k", "90s", "60s", + "dmesg").redirectErrorStream(true).start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + String line = null; + String task = null; + String tool = null; + String bugTitle = null; + Pattern reportPattern = null; + ArrayList<String> currentReport = null; + String lastReport = null; + + while ((line = reader.readLine()) != null) { + if (currentReport == null) { + Matcher bm = bugPattern.matcher(line); + if (!bm.find()) continue; + task = bm.group(1); + tool = bm.group(2); + bugTitle = line; + currentReport = new ArrayList<String>(); + currentReport.add(line); + String reportRegex = tsRegex + "\\[" + task + "\\].*"; + reportPattern = Pattern.compile(reportRegex); + continue; + } + Matcher rm = reportPattern.matcher(line); + if (!rm.matches()) continue; + if ((line = stripSensitiveData(line)) == null) continue; + if (line.contains("================================")) { + lastReport = String.join("\n", currentReport); + currentReport = null; + continue; + } + currentReport.add(line); + } + if (lastReport == null) { + Slog.w(TAG, "Could not find report in dmesg."); + return; + } + + // Avoid sending the same bug report twice. + if (bugTitle == sLastReportedBug) return; + + final String reportTag = "SYSTEM_" + tool + "_ERROR_REPORT"; + final DropBoxManager db = ctx.getSystemService(DropBoxManager.class); + final String headers = getCurrentBootHeaders(); + final String reportText = headers + lastReport; + + addTextToDropBox(db, reportTag, reportText, "/dev/kmsg", LOG_SIZE); + sLastReportedBug = bugTitle; } private void removeOldUpdatePackages(Context context) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 256e963fdb91..d7559873025d 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1886,24 +1886,46 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + // TODO: Consider delete this function or turn it into a no-op method. @Override public NetworkState[] getAllNetworkState() { // This contains IMSI details, so make sure the caller is privileged. PermissionUtils.enforceNetworkStackPermission(mContext); final ArrayList<NetworkState> result = new ArrayList<>(); + for (NetworkStateSnapshot snapshot : getAllNetworkStateSnapshot()) { + // NetworkStateSnapshot doesn't contain NetworkInfo, so need to fetch it from the + // NetworkAgentInfo. + final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(snapshot.network); + if (nai != null && nai.networkInfo.isConnected()) { + result.add(new NetworkState(new NetworkInfo(nai.networkInfo), + snapshot.linkProperties, snapshot.networkCapabilities, snapshot.network, + snapshot.subscriberId)); + } + } + return result.toArray(new NetworkState[result.size()]); + } + + @Override + @NonNull + public List<NetworkStateSnapshot> getAllNetworkStateSnapshot() { + // This contains IMSI details, so make sure the caller is privileged. + PermissionUtils.enforceNetworkStackPermission(mContext); + + final ArrayList<NetworkStateSnapshot> result = new ArrayList<>(); for (Network network : getAllNetworks()) { final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); - // TODO: Consider include SUSPENDED networks. + // TODO: Consider include SUSPENDED networks, which should be considered as + // temporary shortage of connectivity of a connected network. if (nai != null && nai.networkInfo.isConnected()) { - // TODO (b/73321673) : NetworkState contains a copy of the + // TODO (b/73321673) : NetworkStateSnapshot contains a copy of the // NetworkCapabilities, which may contain UIDs of apps to which the // network applies. Should the UIDs be cleared so as not to leak or // interfere ? - result.add(nai.getNetworkState()); + result.add(nai.getNetworkStateSnapshot()); } } - return result.toArray(new NetworkState[result.size()]); + return result; } @Override @@ -5027,10 +5049,16 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onUserAdded(UserHandle user) { mPermissionMonitor.onUserAdded(user); + if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) { + handleSetOemNetworkPreference(mOemNetworkPreferences, null); + } } private void onUserRemoved(UserHandle user) { mPermissionMonitor.onUserRemoved(user); + if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) { + handleSetOemNetworkPreference(mOemNetworkPreferences, null); + } } private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @@ -7161,7 +7189,7 @@ public class ConnectivityService extends IConnectivityManager.Stub toUidRangeStableParcels(nri.getUids())); } } catch (RemoteException | ServiceSpecificException e) { - loge("Exception setting OEM network preference default network", e); + loge("Exception setting app default network", e); } } @@ -7248,7 +7276,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } void addRequestReassignment(@NonNull final RequestReassignment reassignment) { - if (!Build.IS_USER) { + if (Build.IS_DEBUGGABLE) { // The code is never supposed to add two reassignments of the same request. Make // sure this stays true, but without imposing this expensive check on all // reassignments on all user devices. @@ -9046,7 +9074,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } final ArraySet<NetworkRequestInfo> nris = new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference); - updateDefaultNetworksForOemNetworkPreference(nris); + replaceDefaultNetworkRequestsForPreference(nris); mOemNetworkPreferences = preference; // TODO http://b/176496396 persist data to shared preferences. @@ -9054,12 +9082,12 @@ public class ConnectivityService extends IConnectivityManager.Stub try { listener.onComplete(); } catch (RemoteException e) { - loge("handleMessage.EVENT_SET_OEM_NETWORK_PREFERENCE failed", e); + loge("Can't send onComplete in handleSetOemNetworkPreference", e); } } } - private void updateDefaultNetworksForOemNetworkPreference( + private void replaceDefaultNetworkRequestsForPreference( @NonNull final Set<NetworkRequestInfo> nris) { // Pass in a defensive copy as this collection will be updated on remove. handleRemoveNetworkRequests(new ArraySet<>(mDefaultNetworkRequests)); @@ -9145,6 +9173,14 @@ public class ConnectivityService extends IConnectivityManager.Stub return callbackRequestsToRegister; } + private static void setNetworkRequestUids(@NonNull final List<NetworkRequest> requests, + @NonNull final Set<UidRange> uids) { + final Set<UidRange> ranges = new ArraySet<>(uids); + for (final NetworkRequest req : requests) { + req.networkCapabilities.setUids(ranges); + } + } + /** * Class used to generate {@link NetworkRequestInfo} based off of {@link OemNetworkPreferences}. */ @@ -9173,6 +9209,14 @@ public class ConnectivityService extends IConnectivityManager.Stub @NonNull final OemNetworkPreferences preference) { final SparseArray<Set<Integer>> uids = new SparseArray<>(); final PackageManager pm = mContext.getPackageManager(); + final List<UserHandle> users = + mContext.getSystemService(UserManager.class).getUserHandles(true); + if (null == users || users.size() == 0) { + if (VDBG || DDBG) { + log("No users currently available for setting the OEM network preference."); + } + return uids; + } for (final Map.Entry<String, Integer> entry : preference.getNetworkPreferences().entrySet()) { @OemNetworkPreferences.OemNetworkPreference final int pref = entry.getValue(); @@ -9181,7 +9225,10 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!uids.contains(pref)) { uids.put(pref, new ArraySet<>()); } - uids.get(pref).add(uid); + for (final UserHandle ui : users) { + // Add the rules for all users as this policy is device wide. + uids.get(pref).add(UserHandle.getUid(ui, uid)); + } } catch (PackageManager.NameNotFoundException e) { // Although this may seem like an error scenario, it is ok that uninstalled // packages are sent on a network preference as the system will watch for @@ -9221,7 +9268,11 @@ public class ConnectivityService extends IConnectivityManager.Stub + " called with invalid preference of " + preference); } - setOemNetworkRequestUids(requests, uids); + final ArraySet ranges = new ArraySet<Integer>(); + for (final int uid : uids) { + ranges.add(new UidRange(uid, uid)); + } + setNetworkRequestUids(requests, ranges); return new NetworkRequestInfo(requests); } @@ -9254,16 +9305,5 @@ public class ConnectivityService extends IConnectivityManager.Stub netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName()); return netCap; } - - private void setOemNetworkRequestUids(@NonNull final List<NetworkRequest> requests, - @NonNull final Set<Integer> uids) { - final Set<UidRange> ranges = new ArraySet<>(); - for (final int uid : uids) { - ranges.add(new UidRange(uid, uid)); - } - for (final NetworkRequest req : requests) { - req.networkCapabilities.setUids(ranges); - } - } } } diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java index 2906ceebca58..906702866889 100644 --- a/services/core/java/com/android/server/IntentResolver.java +++ b/services/core/java/com/android/server/IntentResolver.java @@ -839,23 +839,34 @@ public abstract class IntentResolver<F, R extends Object> { } }; + // Helper method to copy some of the maps. + private static <E> void copyInto(ArrayMap<String, E[]> l, ArrayMap<String, E[]> r) { + final int end = r.size(); + l.ensureCapacity(end); + for (int i = 0; i < end; i++) { + final E[] val = r.valueAt(i); + final String key = r.keyAt(i); + l.put(key, Arrays.copyOf(val, val.length)); + } + } + // Make <this> a copy of <orig>. The presumption is that <this> is empty but all // arrays are cleared out explicitly, just to be sure. protected void copyFrom(IntentResolver orig) { mFilters.clear(); mFilters.addAll(orig.mFilters); mTypeToFilter.clear(); - mTypeToFilter.putAll(orig.mTypeToFilter); + copyInto(mTypeToFilter, orig.mTypeToFilter); mBaseTypeToFilter.clear(); - mBaseTypeToFilter.putAll(orig.mBaseTypeToFilter); + copyInto(mBaseTypeToFilter, orig.mBaseTypeToFilter); mWildTypeToFilter.clear(); - mWildTypeToFilter.putAll(orig.mWildTypeToFilter); + copyInto(mWildTypeToFilter, orig.mWildTypeToFilter); mSchemeToFilter.clear(); - mSchemeToFilter.putAll(orig.mSchemeToFilter); + copyInto(mSchemeToFilter, orig.mSchemeToFilter); mActionToFilter.clear(); - mActionToFilter.putAll(orig.mActionToFilter); + copyInto(mActionToFilter, orig.mActionToFilter); mTypedActionToFilter.clear(); - mTypedActionToFilter.putAll(orig.mTypedActionToFilter); + copyInto(mTypedActionToFilter, orig.mTypedActionToFilter); } /** diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 5a5f1a3f3723..978bd643f9b7 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -72,6 +72,7 @@ import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; @@ -153,7 +154,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; - boolean matchPhoneStateListenerEvent(int event) { + boolean matchTelephonyCallbackEvent(int event) { return (callback != null) && (this.eventList.contains(event)); } @@ -198,8 +199,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { public int getRegistrationLimit() { return Binder.withCleanCallingIdentity(() -> DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY, - PhoneStateListener.FLAG_PER_PID_REGISTRATION_LIMIT, - PhoneStateListener.DEFAULT_PER_PID_REGISTRATION_LIMIT)); + TelephonyCallback.FLAG_PER_PID_REGISTRATION_LIMIT, + TelephonyCallback.DEFAULT_PER_PID_REGISTRATION_LIMIT)); } /** @@ -210,7 +211,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { */ public boolean isRegistrationLimitEnabledInPlatformCompat(int uid) { return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled( - PhoneStateListener.PHONE_STATE_LISTENER_LIMIT_CHANGE_ID, uid)); + TelephonyCallback.PHONE_STATE_LISTENER_LIMIT_CHANGE_ID, uid)); } } @@ -332,37 +333,37 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { static { REQUIRE_PRECISE_PHONE_STATE_PERMISSION = new HashSet<Integer>(); REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); + TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED); + TelephonyCallback.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED); REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); + TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED); REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); + TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); + TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED); REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED); REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); + TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_DATA_ENABLED_CHANGED); + TelephonyCallback.EVENT_DATA_ENABLED_CHANGED); } private boolean isLocationPermissionRequired(Set<Integer> events) { - return events.contains(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED) - || events.contains(PhoneStateListener.EVENT_CELL_INFO_CHANGED) - || events.contains(PhoneStateListener.EVENT_REGISTRATION_FAILURE) - || events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + return events.contains(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED) + || events.contains(TelephonyCallback.EVENT_CELL_INFO_CHANGED) + || events.contains(TelephonyCallback.EVENT_REGISTRATION_FAILURE) + || events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED); } private boolean isPhoneStatePermissionRequired(Set<Integer> events) { - return events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED) - || events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED) - || events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED) - || events.contains(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); + return events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED) + || events.contains(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED) + || events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED) + || events.contains(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); } private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) { @@ -375,14 +376,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } private boolean isActiveEmergencySessionPermissionRequired(Set<Integer> events) { - return events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL) - || events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); + return events.contains(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL) + || events.contains(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS); } private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) { - return events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED) - || events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED) - || events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); + return events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED) + || events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED) + || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED); } private static final int MSG_USER_SWITCHED = 1; @@ -903,7 +904,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId); } if (notifyNow && validatePhoneId(phoneId)) { - if (events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)){ try { if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]); ServiceState rawSs = new ServiceState(mServiceState[phoneId]); @@ -920,7 +921,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)) { try { if (mSignalStrength[phoneId] != null) { int gsmSignalStrength = mSignalStrength[phoneId] @@ -933,7 +934,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (events.contains( - PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) { + TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) { try { r.callback.onMessageWaitingIndicatorChanged( mMessageWaiting[phoneId]); @@ -942,7 +943,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (events.contains( - PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) { + TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) { try { r.callback.onCallForwardingIndicatorChanged( mCallForwarding[phoneId]); @@ -951,7 +952,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (validateEventAndUserLocked( - r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) { + r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)) { try { if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]); if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) @@ -963,7 +964,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_CALL_STATE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) { try { r.callback.onCallStateChanged(mCallState[phoneId], getCallIncomingNumber(r, phoneId)); @@ -971,7 +972,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)) { try { r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId], mDataConnectionNetworkType[phoneId]); @@ -979,14 +980,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)) { try { r.callback.onDataActivity(mDataActivity[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)) { try { if (mSignalStrength[phoneId] != null) { r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); @@ -996,7 +997,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (events.contains( - PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { + TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { updateReportSignalStrengthDecision(r.subId); try { if (mSignalStrength[phoneId] != null) { @@ -1007,7 +1008,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (validateEventAndUserLocked( - r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) { + r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) { try { if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + mCellInfo.get(phoneId)); @@ -1019,14 +1020,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)) { try { r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) { try { r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId], mCallPreciseDisconnectCause[phoneId]); @@ -1034,7 +1035,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) { try { r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId)); } catch (RemoteException ex) { @@ -1042,7 +1043,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (events.contains( - PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) { + TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) { try { for (PreciseDataConnectionState pdcs : mPreciseDataConnectionStates.get(phoneId).values()) { @@ -1052,14 +1053,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)) { try { r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState); } catch (RemoteException ex) { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) { try { r.callback.onVoiceActivationStateChanged( mVoiceActivationState[phoneId]); @@ -1067,21 +1068,21 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED)) { try { r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) { try { r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)) { try { if (mTelephonyDisplayInfos[phoneId] != null) { r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]); @@ -1090,14 +1091,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) { try { r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); } catch (RemoteException ex) { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED)) { try { r.callback.onPhoneCapabilityChanged(mPhoneCapability); } catch (RemoteException ex) { @@ -1105,35 +1106,35 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (events.contains( - PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) { + TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) { try { r.callback.onActiveDataSubIdChanged(mActiveDataSubId); } catch (RemoteException ex) { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)) { try { r.callback.onRadioPowerStateChanged(mRadioPowerState); } catch (RemoteException ex) { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)) { try { r.callback.onSrvccStateChanged(mSrvccState[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)) { try { r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); } catch (RemoteException ex) { remove(r.binder); } } - if (events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED)) { BarringInfo barringInfo = mBarringInfo.get(phoneId); BarringInfo biNoLocation = barringInfo != null ? barringInfo.createLocationInfoSanitizedCopy() : null; @@ -1147,7 +1148,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (events.contains( - PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) { + TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) { try { r.callback.onPhysicalChannelConfigChanged( mPhysicalChannelConfigs); @@ -1156,7 +1157,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (events.contains( - PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) { + TelephonyCallback.EVENT_DATA_ENABLED_CHANGED)) { try { r.callback.onDataEnabledChanged( mIsDataEnabled[phoneId], mDataEnabledReason[phoneId]); @@ -1165,7 +1166,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (events.contains( - PhoneStateListener.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) { + TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) { try { r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList); } catch (RemoteException ex) { @@ -1183,8 +1184,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { // If any of the system clients wants to always listen to signal strength, // we need to set it on. - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { telephonyManager.createForSubscriptionId(subscriptionId) .setAlwaysReportSignalStrength(true); return; @@ -1233,7 +1234,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { throw new IllegalStateException(errorMsg); } } else if (doesLimitApply && numRecordsForPid - >= PhoneStateListener.DEFAULT_PER_PID_REGISTRATION_LIMIT / 2) { + >= TelephonyCallback.DEFAULT_PER_PID_REGISTRATION_LIMIT / 2) { // Log the warning independently of the dynamically set limit -- apps shouldn't be // doing this regardless of whether we're throwing them an exception for it. Rlog.w(TAG, "Pid " + callingPid + " has exceeded half the number of permissible" @@ -1284,8 +1285,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // Every time a client that is registrating to always receive the signal // strength is removed from registry records, we need to check if // the signal strength decision needs to update on its slot. - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { updateReportSignalStrengthDecision(r.subId); } return; @@ -1305,7 +1306,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED) + if (r.matchTelephonyCallbackEvent(TelephonyCallback.EVENT_CALL_STATE_CHANGED) && (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { try { // Ensure the listener has read call log permission; if they do not return @@ -1340,7 +1341,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallState[phoneId] = state; mCallIncomingNumber[phoneId] = incomingNumber; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED) + if (r.matchTelephonyCallbackEvent(TelephonyCallback.EVENT_CALL_STATE_CHANGED) && (r.subId == subId) && (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { try { @@ -1382,8 +1383,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId + " phoneId=" + phoneId + " state=" + state); } - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_SERVICE_STATE_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_SERVICE_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { @@ -1444,8 +1445,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } try { if ((activationType == SIM_ACTIVATION_TYPE_VOICE) - && r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED) + && r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r @@ -1455,8 +1456,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callback.onVoiceActivationStateChanged(activationState); } if ((activationType == SIM_ACTIVATION_TYPE_DATA) - && r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED) + && r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r @@ -1495,11 +1496,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId + " phoneId=" + phoneId + " ss=" + signalStrength); } - if ((r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED) - || r.matchPhoneStateListenerEvent( - PhoneStateListener. - EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) + if ((r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED) + || r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) && idMatch(r.subId, subId, phoneId)) { try { if (DBG) { @@ -1512,8 +1512,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mRemoveList.add(r.binder); } } - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { int gsmSignalStrength = signalStrength.getGsmSignalStrength(); @@ -1559,8 +1559,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId); } for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCarrierNetworkChange(active); @@ -1592,7 +1592,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCellInfo.set(phoneId, cellInfo); for (Record r : mRecords) { if (validateEventAndUserLocked( - r, PhoneStateListener.EVENT_CELL_INFO_CHANGED) + r, TelephonyCallback.EVENT_CELL_INFO_CHANGED) && idMatch(r.subId, subId, phoneId) && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { @@ -1625,8 +1625,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { mMessageWaiting[phoneId] = mwi; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onMessageWaitingIndicatorChanged(mwi); @@ -1652,8 +1652,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { mUserMobileDataState[phoneId] = state; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onUserMobileDataStateChanged(state); @@ -1691,8 +1691,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { mTelephonyDisplayInfos[phoneId] = telephonyDisplayInfo; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED) && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) { try { r.callback.onDisplayInfoChanged(telephonyDisplayInfo); @@ -1723,8 +1723,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { mCallForwarding[phoneId] = cfi; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallForwardingIndicatorChanged(cfi); @@ -1752,8 +1752,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mDataActivity[phoneId] = state; for (Record r : mRecords) { // Notify by correct subId. - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onDataActivity(state); @@ -1800,8 +1800,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log(str); mLocalLog.log(str); for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG) { @@ -1825,8 +1825,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { .remove(key); if (!Objects.equals(oldState, preciseState)) { for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onPreciseDataConnectionStateChanged(preciseState); @@ -1872,7 +1872,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCellIdentity[phoneId] = cellIdentity; for (Record r : mRecords) { if (validateEventAndUserLocked( - r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED) + r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED) && idMatch(r.subId, subId, phoneId) && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { @@ -1925,8 +1925,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]); @@ -1934,8 +1934,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mRemoveList.add(r.binder); } } - if (notifyCallAttributes && r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED) + if (notifyCallAttributes && r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); @@ -1959,8 +1959,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallDisconnectCause[phoneId] = disconnectCause; mCallPreciseDisconnectCause[phoneId] = preciseDisconnectCause; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener - .LISTEN_CALL_DISCONNECT_CAUSES) && idMatch(r.subId, subId, phoneId)) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId], mCallPreciseDisconnectCause[phoneId]); @@ -1983,8 +1984,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { mImsReasonInfo.set(phoneId, imsReasonInfo); for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { @@ -2015,8 +2016,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { mSrvccState[phoneId] = state; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_SRVCC_STATE_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_SRVCC_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { @@ -2044,8 +2045,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (VDBG) { log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId); } - if ((r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_OEM_HOOK_RAW)) + if ((r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_OEM_HOOK_RAW)) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onOemHookRawEvent(rawData); @@ -2072,8 +2073,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPhoneCapability = capability; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED)) { try { r.callback.onPhoneCapabilityChanged(capability); } catch (RemoteException ex) { @@ -2097,8 +2098,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mActiveDataSubId = activeDataSubId; synchronized (mRecords) { for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) { try { r.callback.onActiveDataSubIdChanged(activeDataSubId); } catch (RemoteException ex) { @@ -2124,8 +2125,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mRadioPowerState = state; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onRadioPowerStateChanged(state); @@ -2153,8 +2154,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mEmergencyNumberList = tm.getEmergencyNumberList(); for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); @@ -2185,8 +2186,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } for (Record r : mRecords) { // Send to all listeners regardless of subscription - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL)) { try { r.callback.onOutgoingEmergencyCall(emergencyNumber, subId); } catch (RemoteException ex) { @@ -2204,13 +2205,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (!checkNotifyPermission("notifyOutgoingEmergencySms()")) { return; } + synchronized (mRecords) { if (validatePhoneId(phoneId)) { mOutgoingSmsEmergencyNumber[phoneId] = emergencyNumber; for (Record r : mRecords) { // Send to all listeners regardless of subscription - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS)) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS)) { try { r.callback.onOutgoingEmergencySms(emergencyNumber, subId); } catch (RemoteException ex) { @@ -2239,8 +2241,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { callNetworkType, callQuality); for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); @@ -2270,8 +2272,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { if (validatePhoneId(phoneId)) { for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_REGISTRATION_FAILURE) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_REGISTRATION_FAILURE) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onRegistrationFailed( @@ -2313,8 +2315,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { BarringInfo biNoLocation = barringInfo.createLocationInfoSanitizedCopy(); if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_BARRING_INFO_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_BARRING_INFO_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { @@ -2356,8 +2358,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId)); for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { @@ -2386,7 +2388,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { * {@link TelephonyManager}. */ public void notifyDataEnabled(int phoneId, int subId, boolean enabled, - @TelephonyManager.DataEnabledReason int reason) { + @TelephonyManager.DataEnabledReason int reason) { if (!checkNotifyPermission("notifyDataEnabled()")) { return; } @@ -2401,8 +2403,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mIsDataEnabled[phoneId] = enabled; mDataEnabledReason[phoneId] = reason; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_DATA_ENABLED_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_DATA_ENABLED_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onDataEnabledChanged(enabled, reason); @@ -2435,8 +2437,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mAllowedNetworkTypesList = allowedNetworkTypesList; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED) + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (VDBG) { @@ -2817,7 +2819,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null); } - if ((events.contains(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) { + if ((events.contains(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null); } @@ -2847,7 +2849,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { foregroundUser = ActivityManager.getCurrentUser(); valid = UserHandle.getUserId(r.callerUid) == foregroundUser - && r.matchPhoneStateListenerEvent(event); + && r.matchTelephonyCallbackEvent(event); if (DBG | DBG_LOC) { log("validateEventAndUserLocked: valid=" + valid + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser @@ -2972,7 +2974,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return; } - if ((events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED))) { + if ((events.contains(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED))) { try { if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" + mServiceState[phoneId]); @@ -2991,9 +2993,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED) + if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED) || events.contains( - PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { + TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { try { if (mSignalStrength[phoneId] != null) { SignalStrength signalStrength = mSignalStrength[phoneId]; @@ -3008,7 +3010,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)) { try { if (mSignalStrength[phoneId] != null) { int gsmSignalStrength = mSignalStrength[phoneId] @@ -3025,7 +3027,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) { + if (validateEventAndUserLocked(r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) { try { if (DBG_LOC) { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " @@ -3040,7 +3042,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onUserMobileDataStateChanged phoneId=" @@ -3052,7 +3054,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onDisplayInfoChanged phoneId=" @@ -3066,7 +3068,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onMessageWaitingIndicatorChanged phoneId=" @@ -3079,7 +3081,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onCallForwardingIndicatorChanged phoneId=" @@ -3092,7 +3094,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) { + if (validateEventAndUserLocked(r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)) { try { if (DBG_LOC) { log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = " @@ -3108,7 +3110,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)) { try { if (DBG) { log("checkPossibleMissNotify: onDataConnectionStateChanged(mDataConnectionState" diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f409d713280a..9e5d7e4c3325 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -50,7 +50,6 @@ 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; @@ -1178,14 +1177,16 @@ public class ActivityManagerService extends IActivityManager.Stub final String tag; final int type; final @ReasonCode int reasonCode; + final int callingUid; PendingTempAllowlist(int targetUid, long duration, @ReasonCode int reasonCode, String tag, - int type) { + int type, int callingUid) { this.targetUid = targetUid; this.duration = duration; this.tag = tag; this.type = type; this.reasonCode = reasonCode; + this.callingUid = callingUid; } void dumpDebug(ProtoOutputStream proto, long fieldId) { @@ -1198,6 +1199,8 @@ public class ActivityManagerService extends IActivityManager.Stub proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TYPE, type); proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.REASON_CODE, reasonCode); + proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.CALLING_UID, + callingUid); proto.end(token); } } @@ -9231,6 +9234,8 @@ public class ActivityManagerService extends IActivityManager.Stub pw.print(ptw.type); pw.print(" "); pw.print(ptw.reasonCode); + pw.print(" "); + pw.print(ptw.callingUid); } } } @@ -10785,9 +10790,12 @@ public class ActivityManagerService extends IActivityManager.Stub pw.print(" unmapped + "); pw.print(stringifyKBSize(ionPool)); pw.println(" pools)"); + kernelUsed += ionUnmapped; // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being - // set on ION VMAs, therefore consider the entire ION heap as used kernel memory - kernelUsed += ionHeap; + // set on ION VMAs, however it might be included by the memtrack HAL. + // Replace memtrack HAL reported Graphics category with mapped dmabufs + ss[INDEX_TOTAL_PSS] -= ss[INDEX_TOTAL_MEMTRACK_GRAPHICS]; + ss[INDEX_TOTAL_PSS] += dmabufMapped; } else { final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb(); if (totalExportedDmabuf >= 0) { @@ -14510,7 +14518,8 @@ public class ActivityManagerService extends IActivityManager.Stub String reason, int type, int callingUid) { synchronized (mProcLock) { mPendingTempAllowlist.put(targetUid, - new PendingTempAllowlist(targetUid, duration, reasonCode, reason, type)); + new PendingTempAllowlist(targetUid, duration, reasonCode, reason, type, + callingUid)); setUidTempAllowlistStateLSP(targetUid, true); mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget(); @@ -14541,7 +14550,8 @@ 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.reasonCode, ptw.tag); + ptw.duration, ptw.type, true, ptw.reasonCode, ptw.tag, + ptw.callingUid); } } diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java index f8494d8a7c04..31ea14a73409 100644 --- a/services/core/java/com/android/server/am/AppProfiler.java +++ b/services/core/java/com/android/server/am/AppProfiler.java @@ -1517,17 +1517,21 @@ public class AppProfiler { long kernelUsed = memInfo.getKernelUsedSizeKb(); final long ionHeap = Debug.getIonHeapsSizeKb(); final long ionPool = Debug.getIonPoolsSizeKb(); + final long dmabufMapped = Debug.getDmabufMappedSizeKb(); if (ionHeap >= 0 && ionPool >= 0) { + final long ionUnmapped = ionHeap - dmabufMapped; memInfoBuilder.append(" ION: "); memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool)); memInfoBuilder.append("\n"); + kernelUsed += ionUnmapped; // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being - // set on ION VMAs, therefore consider the entire ION heap as used kernel memory - kernelUsed += ionHeap; + // set on ION VMAs, however it might be included by the memtrack HAL. + // Replace memtrack HAL reported Graphics category with mapped dmabufs + totalPss -= totalMemtrackGraphics; + totalPss += dmabufMapped; } else { final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb(); if (totalExportedDmabuf >= 0) { - final long dmabufMapped = Debug.getDmabufMappedSizeKb(); final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped; memInfoBuilder.append("DMA-BUF: "); memInfoBuilder.append(stringifyKBSize(totalExportedDmabuf)); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index ce2852ce7727..360e1b4e48a8 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -85,7 +85,6 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.net.module.util.NetworkCapabilitiesUtils; import com.android.server.LocalServices; import com.android.server.Watchdog; -import com.android.server.connectivity.DataConnectionStats; import com.android.server.net.BaseNetworkObserver; import com.android.server.pm.UserManagerInternal; diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 06cacc70a9b8..81c4c8605fb4 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -930,6 +930,8 @@ public final class BroadcastQueue { } else if (r.intent.getData() != null) { b.append(r.intent.getData()); } + b.append(",reason:"); + b.append(reason); if (DEBUG_BROADCAST) { Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration + " type=" + type + " : " + b.toString()); diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/am/DataConnectionStats.java index 15f43a0481bd..6e39a4c802d9 100644 --- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java +++ b/services/core/java/com/android/server/am/DataConnectionStats.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity; +package com.android.server.am; import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; @@ -34,11 +34,11 @@ import android.telephony.TelephonyManager; import android.util.Log; import com.android.internal.app.IBatteryStats; -import com.android.server.am.BatteryStatsService; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; +/** Class for receiving data connection state to report to {@link BatteryStatsService}. */ public class DataConnectionStats extends BroadcastReceiver { private static final String TAG = "DataConnectionStats"; private static final boolean DEBUG = false; @@ -62,14 +62,14 @@ public class DataConnectionStats extends BroadcastReceiver { new PhoneStateListenerImpl(new PhoneStateListenerExecutor(listenerHandler)); } + /** Start data connection state monitoring. */ public void startMonitoring() { - TelephonyManager phone = - (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); + TelephonyManager phone = mContext.getSystemService(TelephonyManager.class); phone.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE - | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS - | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE - | PhoneStateListener.LISTEN_DATA_ACTIVITY); + | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS + | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE + | PhoneStateListener.LISTEN_DATA_ACTIVITY); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SIM_STATE_CHANGED); @@ -103,8 +103,10 @@ public class DataConnectionStats extends BroadcastReceiver { if (mNrState == NetworkRegistrationInfo.NR_STATE_CONNECTED) { networkType = TelephonyManager.NETWORK_TYPE_NR; } - if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible", - networkType, visible ? "" : "not ")); + if (DEBUG) { + Log.d(TAG, String.format("Noting data connection for network type %s: %svisible", + networkType, visible ? "" : "not ")); + } try { mBatteryStats.notePhoneDataConnectionState(networkType, visible, mServiceState.getState()); @@ -113,7 +115,7 @@ public class DataConnectionStats extends BroadcastReceiver { } } - private final void updateSimState(Intent intent) { + private void updateSimState(Intent intent) { String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE); if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) { mSimState = TelephonyManager.SIM_STATE_ABSENT; diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationManagerInternal.java b/services/core/java/com/android/server/apphibernation/AppHibernationManagerInternal.java new file mode 100644 index 000000000000..b0335fe404f4 --- /dev/null +++ b/services/core/java/com/android/server/apphibernation/AppHibernationManagerInternal.java @@ -0,0 +1,46 @@ +/* + * 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.apphibernation; + +/** + * App hibernation manager local system service interface. + * + * @hide Only for use within the system server. + */ +public abstract class AppHibernationManagerInternal { + + /** + * @see AppHibernationService#isHibernatingForUser + */ + public abstract boolean isHibernatingForUser(String packageName, int userId); + + /** + * @see AppHibernationService#setHibernatingForUser + */ + public abstract void setHibernatingForUser(String packageName, int userId, + boolean isHibernating); + + /** + * @see AppHibernationService#isHibernatingGlobally + */ + public abstract boolean isHibernatingGlobally(String packageName); + + /** + * @see AppHibernationService#setHibernatingGlobally + */ + public abstract void setHibernatingGlobally(String packageName, boolean isHibernating); +} diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index 32ae87898085..968cf5f1df91 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -59,6 +59,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.LocalServices; import com.android.server.SystemService; import java.io.File; @@ -134,6 +135,8 @@ public final class AppHibernationService extends SystemService { intentFilter.addAction(ACTION_PACKAGE_REMOVED); intentFilter.addDataScheme("package"); userAllContext.registerReceiver(mBroadcastReceiver, intentFilter); + + LocalServices.addService(AppHibernationManagerInternal.class, mLocalService); } @Override @@ -545,6 +548,36 @@ public final class AppHibernationService extends SystemService { } } + private final AppHibernationManagerInternal mLocalService = new LocalService(this); + + private static final class LocalService extends AppHibernationManagerInternal { + private final AppHibernationService mService; + + LocalService(AppHibernationService service) { + mService = service; + } + + @Override + public boolean isHibernatingForUser(String packageName, int userId) { + return mService.isHibernatingForUser(packageName, userId); + } + + @Override + public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { + mService.setHibernatingForUser(packageName, userId, isHibernating); + } + + @Override + public void setHibernatingGlobally(String packageName, boolean isHibernating) { + mService.setHibernatingGlobally(packageName, isHibernating); + } + + @Override + public boolean isHibernatingGlobally(String packageName) { + return mService.isHibernatingGlobally(packageName); + } + } + private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this); static final class AppHibernationServiceStub extends IAppHibernationService.Stub { diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 11125dd55665..4a12ff6932de 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -848,10 +848,7 @@ public class AppOpsService extends IAppOpsService.Stub { proxyAttributionTag, uidState, flags); mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName, - tag, uidState, flags); - - mHistoricalRegistry.mDiscreteRegistry.recordDiscreteAccess(parent.uid, - parent.packageName, parent.op, tag, flags, uidState, accessTime, -1); + tag, uidState, flags, accessTime); } /** @@ -955,9 +952,10 @@ public class AppOpsService extends IAppOpsService.Stub { mInProgressEvents = new ArrayMap<>(1); } + long startTime = System.currentTimeMillis(); InProgressStartOpEvent event = mInProgressEvents.get(clientId); if (event == null) { - event = mInProgressStartOpEventPool.acquire(System.currentTimeMillis(), + event = mInProgressStartOpEventPool.acquire(startTime, SystemClock.elapsedRealtime(), clientId, PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId), proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags); @@ -971,7 +969,7 @@ public class AppOpsService extends IAppOpsService.Stub { event.numUnfinishedStarts++; mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName, - tag, uidState, flags); + tag, uidState, flags, startTime); } /** @@ -1017,11 +1015,7 @@ public class AppOpsService extends IAppOpsService.Stub { mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid, parent.packageName, tag, event.getUidState(), - event.getFlags(), finishedEvent.getDuration()); - - mHistoricalRegistry.mDiscreteRegistry.recordDiscreteAccess(parent.uid, - parent.packageName, parent.op, tag, event.getFlags(), event.getUidState(), - event.getStartTime(), accessDurationMillis); + event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration()); mInProgressStartOpEventPool.release(event); @@ -4769,7 +4763,7 @@ public class AppOpsService extends IAppOpsService.Stub { mFile.failWrite(stream); } } - mHistoricalRegistry.mDiscreteRegistry.writeAndClearAccessHistory(); + mHistoricalRegistry.writeAndClearDiscreteHistory(); } static class Shell extends ShellCommand { @@ -6125,8 +6119,7 @@ public class AppOpsService extends IAppOpsService.Stub { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APPOPS, "clearHistory"); // Must not hold the appops lock - mHistoricalRegistry.clearHistory(); - mHistoricalRegistry.mDiscreteRegistry.clearHistory(); + mHistoricalRegistry.clearAllHistory(); } @Override diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java index 76990453ee03..a99d90883f87 100644 --- a/services/core/java/com/android/server/appop/DiscreteRegistry.java +++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java @@ -495,6 +495,12 @@ final class DiscreteRegistry { int nAttributedOps = attributedOps.size(); for (int i = nAttributedOps - 1; i >= 0; i--) { DiscreteOpEvent previousOp = attributedOps.get(i); + if (i == nAttributedOps - 1 && previousOp.mNoteTime == accessTime + && accessDuration > -1) { + // existing event with updated duration + attributedOps.remove(i); + break; + } if (previousOp.mNoteTime < accessTime) { break; } diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index 1c43fedd3112..0fcf5ee35e11 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -138,7 +138,7 @@ final class HistoricalRegistry { private static final String PARAMETER_ASSIGNMENT = "="; private static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled"; - volatile @NonNull DiscreteRegistry mDiscreteRegistry; + private volatile @NonNull DiscreteRegistry mDiscreteRegistry; @GuardedBy("mLock") private @NonNull LinkedList<HistoricalOps> mPendingWrites = new LinkedList<>(); @@ -477,7 +477,8 @@ final class HistoricalRegistry { } void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, - @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) { + @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, + long accessTime) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { @@ -487,6 +488,9 @@ final class HistoricalRegistry { getUpdatedPendingHistoricalOpsMLocked( System.currentTimeMillis()).increaseAccessCount(op, uid, packageName, attributionTag, uidState, flags, 1); + + mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag, + flags, uidState, accessTime, -1); } } } @@ -508,7 +512,7 @@ final class HistoricalRegistry { void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, - long increment) { + long eventStartTime, long increment) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { @@ -518,6 +522,8 @@ final class HistoricalRegistry { getUpdatedPendingHistoricalOpsMLocked( System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName, attributionTag, uidState, flags, increment); + mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag, + flags, uidState, increment, eventStartTime); } } } @@ -562,7 +568,7 @@ final class HistoricalRegistry { return; } final List<HistoricalOps> history = mPersistence.readHistoryDLocked(); - clearHistory(); + clearHistoricalRegistry(); if (history != null) { final int historySize = history.size(); for (int i = 0; i < historySize; i++) { @@ -631,7 +637,16 @@ final class HistoricalRegistry { } } - void clearHistory() { + void writeAndClearDiscreteHistory() { + mDiscreteRegistry.writeAndClearAccessHistory(); + } + + void clearAllHistory() { + clearHistoricalRegistry(); + mDiscreteRegistry.clearHistory(); + } + + void clearHistoricalRegistry() { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { 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/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index cac6cab7074e..1d0e11569c80 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -35,7 +35,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkMonitorManager; import android.net.NetworkRequest; -import android.net.NetworkState; +import android.net.NetworkStateSnapshot; import android.net.QosCallbackException; import android.net.QosFilter; import android.net.QosFilterParcelable; @@ -890,15 +890,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { mScore = score; } - public NetworkState getNetworkState() { + /** + * Return a {@link NetworkStateSnapshot} for this network. + */ + @NonNull + public NetworkStateSnapshot getNetworkStateSnapshot() { synchronized (this) { // Network objects are outwardly immutable so there is no point in duplicating. // Duplicating also precludes sharing socket factories and connection pools. final String subscriberId = (networkAgentConfig != null) ? networkAgentConfig.subscriberId : null; - return new NetworkState(new NetworkInfo(networkInfo), - new LinkProperties(linkProperties), - new NetworkCapabilities(networkCapabilities), network, subscriberId); + return new NetworkStateSnapshot(network, new NetworkCapabilities(networkCapabilities), + new LinkProperties(linkProperties), subscriberId, networkInfo.getType()); } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 3709963b7caa..a0d93656b7f6 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -767,6 +767,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { } private void setDisplayBrightness(float brightness) { + // Ensure brightnessState is valid, before processing and sending to + // surface control + if (Float.isNaN(brightness)) { + return; + } + if (DEBUG) { Slog.d(TAG, "setDisplayBrightness(" + "id=" + physicalDisplayId diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 18f7a068657f..7235a921254d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -1073,7 +1073,21 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // Ignore this message. return true; } - setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message)); + boolean tvSystemAudioMode = isSystemAudioControlFeatureEnabled(); + boolean avrSystemAudioMode = HdmiUtils.parseCommandParamSystemAudioStatus(message); + // Set System Audio Mode according to TV's settings. + // Handle <System Audio Mode Status> here only when + // SystemAudioAutoInitiationAction timeout + HdmiDeviceInfo avr = getAvrDeviceInfo(); + if (avr == null) { + setSystemAudioMode(false); + } else if (avrSystemAudioMode != tvSystemAudioMode) { + addAndStartAction(new SystemAudioActionFromTv(this, avr.getLogicalAddress(), + tvSystemAudioMode, null)); + } else { + setSystemAudioMode(tvSystemAudioMode); + } + return true; } diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java index 05d0aefe7c89..dbd8dd9eff3b 100644 --- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java +++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java @@ -21,7 +21,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.SystemClock; -import android.telephony.PhoneStateListener; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import com.android.server.FgThread; @@ -55,8 +55,8 @@ public class SystemEmergencyHelper extends EmergencyHelper { // TODO: this doesn't account for multisim phones - mTelephonyManager.registerPhoneStateListener(FgThread.getExecutor(), - new EmergencyCallPhoneStateListener()); + mTelephonyManager.registerTelephonyCallback(FgThread.getExecutor(), + new EmergencyCallTelephonyCallback()); mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -78,8 +78,8 @@ public class SystemEmergencyHelper extends EmergencyHelper { || mTelephonyManager.isInEmergencySmsMode(); } - private class EmergencyCallPhoneStateListener extends PhoneStateListener implements - PhoneStateListener.CallStateChangedListener { + private class EmergencyCallTelephonyCallback extends TelephonyCallback implements + TelephonyCallback.CallStateListener{ @Override public void onCallStateChanged(int state, String incomingNumber) { diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index 7ab8b27075b7..a5763aee6336 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -35,6 +35,7 @@ import android.security.Scrypt; import android.service.gatekeeper.GateKeeperResponse; import android.service.gatekeeper.IGateKeeperService; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -906,7 +907,7 @@ public class SyntheticPasswordManager { if (!tokenMap.containsKey(userId)) { return Collections.emptySet(); } - return tokenMap.get(userId).keySet(); + return new ArraySet<>(tokenMap.get(userId).keySet()); } public boolean removePendingToken(long handle, int userId) { diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java index 639dda6f8981..23195bbe5d7e 100644 --- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java +++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java @@ -93,8 +93,7 @@ public final class MediaMetricsManagerService extends SystemService { StatsLog.write(statsEvent); } - @Override - public String getSessionId(int userId) { + private String getSessionIdInternal(int userId) { byte[] byteId = new byte[16]; // 128 bits mSecureRandom.nextBytes(byteId); String id = Base64.encodeToString(byteId, Base64.DEFAULT); @@ -102,6 +101,16 @@ public final class MediaMetricsManagerService extends SystemService { } @Override + public String getPlaybackSessionId(int userId) { + return getSessionIdInternal(userId); + } + + @Override + public String getRecordingSessionId(int userId) { + return getSessionIdInternal(userId); + } + + @Override public void reportPlaybackErrorEvent( String sessionId, PlaybackErrorEvent event, int userId) { StatsEvent statsEvent = StatsEvent.newBuilder() diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 067c5c0e7620..a2f2f98b57c7 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -169,7 +169,6 @@ import android.net.NetworkIdentity; import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.net.NetworkPolicyManager.UidState; -import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.NetworkStack; @@ -3164,14 +3163,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - @Override - @Deprecated - public NetworkQuotaInfo getNetworkQuotaInfo(NetworkState state) { - Log.w(TAG, "Shame on UID " + Binder.getCallingUid() - + " for calling the hidden API getNetworkQuotaInfo(). Shame!"); - return new NetworkQuotaInfo(); - } - private void enforceSubscriptionPlanAccess(int subId, int callingUid, String callingPackage) { // Verify they're not lying about package name mAppOps.checkPackage(callingUid, callingPackage); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7da53b50d927..b751503907fd 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -20368,6 +20368,11 @@ public class PackageManagerService extends IPackageManager.Stub return; } + if (isIncrementalPath(pkg.getPath()) && IncrementalManager.getVersion() + < IncrementalManager.MIN_VERSION_TO_SUPPORT_FSVERITY) { + return; + } + // Collect files we care for fs-verity setup. ArrayMap<String, String> fsverityCandidates = new ArrayMap<>(); if (legacyMode) { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index a377f1c72375..d1cf55de7254 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -103,6 +103,7 @@ import android.view.IWindowManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.infra.AndroidFuture; import com.android.internal.logging.MetricsLogger; import com.android.internal.os.BackgroundThread; import com.android.internal.util.CollectionUtils; @@ -142,6 +143,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Predicate; @@ -1885,8 +1887,8 @@ public class ShortcutService extends IShortcutService.Stub { // === APIs === @Override - public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, - @UserIdInt int userId) { + public void setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, + @UserIdInt int userId, @NonNull AndroidFuture callback) { verifyCaller(packageName, userId); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); @@ -1913,7 +1915,7 @@ public class ShortcutService extends IShortcutService.Stub { // Throttling. if (!ps.tryApiCall(unlimited)) { - return false; + callback.complete(false); } // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). @@ -1949,12 +1951,12 @@ public class ShortcutService extends IShortcutService.Stub { verifyStates(); - return true; + callback.complete(true); } @Override - public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, - @UserIdInt int userId) { + public void updateShortcuts(String packageName, ParceledListSlice shortcutInfoList, + @UserIdInt int userId, AndroidFuture callback) { verifyCaller(packageName, userId); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); @@ -1981,7 +1983,8 @@ public class ShortcutService extends IShortcutService.Stub { // Throttling. if (!ps.tryApiCall(unlimited)) { - return false; + callback.complete(false); + return; } // Initialize the implicit ranks for ShortcutPackage.adjustRanks(). @@ -2046,12 +2049,12 @@ public class ShortcutService extends IShortcutService.Stub { verifyStates(); - return true; + callback.complete(true); } @Override - public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, - @UserIdInt int userId) { + public void addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList, + @UserIdInt int userId, AndroidFuture callback) { verifyCaller(packageName, userId); final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); @@ -2081,7 +2084,8 @@ public class ShortcutService extends IShortcutService.Stub { // Throttling. if (!ps.tryApiCall(unlimited)) { - return false; + callback.complete(false); + return; } for (int i = 0; i < size; i++) { final ShortcutInfo newShortcut = newShortcuts.get(i); @@ -2109,7 +2113,7 @@ public class ShortcutService extends IShortcutService.Stub { verifyStates(); - return true; + callback.complete(true); } @Override @@ -2174,15 +2178,17 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut, - IntentSender resultIntent, int userId) { + public void requestPinShortcut(String packageName, ShortcutInfo shortcut, + IntentSender resultIntent, int userId, AndroidFuture callback) { Objects.requireNonNull(shortcut); + Objects.requireNonNull(callback); Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled"); - return requestPinItem(packageName, userId, shortcut, null, null, resultIntent); + callback.complete(requestPinItem(packageName, userId, shortcut, null, null, resultIntent)); } @Override - public Intent createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId) + public void createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId, + AndroidFuture callback) throws RemoteException { Objects.requireNonNull(shortcut); Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled"); @@ -2198,7 +2204,7 @@ public class ShortcutService extends IShortcutService.Stub { } verifyStates(); - return ret; + callback.complete(ret); } /** @@ -2455,8 +2461,9 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public ParceledListSlice<ShortcutInfo> getShortcuts(String packageName, - @ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId) { + public void getShortcuts(String packageName, + @ShortcutManager.ShortcutMatchFlags int matchFlags, @UserIdInt int userId, + AndroidFuture<ParceledListSlice<ShortcutInfo>> callback) { verifyCaller(packageName, userId); synchronized (mLock) { @@ -2472,16 +2479,16 @@ public class ShortcutService extends IShortcutService.Stub { | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0) | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0); - return getShortcutsWithQueryLocked( + callback.complete(getShortcutsWithQueryLocked( packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, (ShortcutInfo si) -> - si.isVisibleToPublisher() && (si.getFlags() & shortcutFlags) != 0); + si.isVisibleToPublisher() && (si.getFlags() & shortcutFlags) != 0)); } } @Override - public ParceledListSlice<ShortcutManager.ShareShortcutInfo> getShareTargets(String packageName, - IntentFilter filter, @UserIdInt int userId) { + public void getShareTargets(String packageName, IntentFilter filter, @UserIdInt int userId, + AndroidFuture<ParceledListSlice> callback) { Preconditions.checkStringNotEmpty(packageName, "packageName"); Objects.requireNonNull(filter, "intentFilter"); @@ -2497,7 +2504,7 @@ public class ShortcutService extends IShortcutService.Stub { final ShortcutUser user = getUserShortcutsLocked(userId); user.forAllPackages(p -> shortcutInfoList.addAll(p.getMatchingShareTargets(filter))); - return new ParceledListSlice<>(shortcutInfoList); + callback.complete(new ParceledListSlice<>(shortcutInfoList)); } } @@ -3081,8 +3088,14 @@ public class ShortcutService extends IShortcutService.Stub { @Override public List<ShortcutManager.ShareShortcutInfo> getShareTargets( @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId) { - return ShortcutService.this.getShareTargets( - callingPackage, intentFilter, userId).getList(); + final AndroidFuture<ParceledListSlice> future = new AndroidFuture<>(); + ShortcutService.this.getShareTargets( + callingPackage, intentFilter, userId, future); + try { + return future.get().getList(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } } @Override diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 871576e7c795..8283ac668d5e 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1544,7 +1544,7 @@ public class UserManagerService extends IUserManager.Stub { public String getUserName() { final int callingUid = Binder.getCallingUid(); if (!hasManageOrCreateUsersPermission() - || hasPermissionGranted( + && !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"); 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 bce218f8a74b..047e3b362b7a 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; @@ -430,7 +432,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; @@ -471,7 +472,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mHasSoftInput = false; boolean mHapticTextHandleEnabled; boolean mUseTvRouting; - int mVeryLongPressTimeout; boolean mAllowStartActivityForLongPressOnPowerDuringSetup; MetricsLogger mLogger; boolean mWakeOnDpadKeyPress; @@ -567,14 +567,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; @@ -585,10 +584,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; @@ -596,8 +592,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 @@ -638,22 +633,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; @@ -764,13 +746,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; @@ -810,28 +785,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(); @@ -853,10 +813,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; } @@ -866,11 +825,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. @@ -892,71 +846,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 { @@ -964,68 +867,37 @@ 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 " @@ -1176,6 +1048,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void powerLongPress(long eventTime) { final int behavior = getResolvedLongPressOnPowerBehavior(); + switch (behavior) { case LONG_PRESS_POWER_NOTHING: break; @@ -1183,7 +1056,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPowerKeyHandled = true; performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, "Power - Long Press - Global Actions"); - showGlobalActionsInternal(); + showGlobalActions(); break; case LONG_PRESS_POWER_SHUT_OFF: case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM: @@ -1214,14 +1087,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void powerVeryLongPress() { switch (mVeryLongPressOnPowerBehavior) { - case VERY_LONG_PRESS_POWER_NOTHING: - break; - case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS: - mPowerKeyHandled = true; - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, - "Power - Very Long Press - Show Global Actions"); - showGlobalActionsInternal(); - break; + case VERY_LONG_PRESS_POWER_NOTHING: + break; + case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS: + mPowerKeyHandled = true; + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, + "Power - Very Long Press - Show Global Actions"); + showGlobalActions(); + break; } } @@ -1814,8 +1687,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); @@ -1909,6 +1780,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }); initKeyCombinationRules(); + initSingleKeyGestureRules(); } private void initKeyCombinationRules() { @@ -1921,7 +1793,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) { @Override void execute() { - cancelPendingPowerKeyAction(); + mPowerKeyHandled = true; interceptScreenshotChord(); } @Override @@ -1956,7 +1828,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override void execute() { - cancelPendingPowerKeyAction(); + mPowerKeyHandled = true; interceptRingerToggleChord(); } @Override @@ -1970,7 +1842,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) { @Override void execute() { - cancelPendingBackKeyAction(); + mBackKeyHandled = true; interceptAccessibilityGestureTv(); } @@ -1984,7 +1856,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) { @Override void execute() { - cancelPendingBackKeyAction(); + mBackKeyHandled = true; interceptBugreportGestureTv(); } @@ -1997,6 +1869,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 eventTime) { + powerLongPress(eventTime); + } + + @Override + void onVeryLongPress(long eventTime) { + 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. @@ -3427,8 +3377,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 @@ -3443,12 +3406,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; } } @@ -3560,33 +3524,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 @@ -3767,6 +3715,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 @@ -4766,15 +4751,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..cae209353361 --- /dev/null +++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java @@ -0,0 +1,362 @@ +/* + * 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 eventTime) { // long press behavior. } + * void onVeryLongPress(long eventTime) { // 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 eventTime) {} + /** + * Callback when very long press has been detected. + */ + void onVeryLongPress(long eventTime) {} + + @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 + && mActiveRule.supportLongPress() && !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)) { + if (DEBUG) { + Log.i(TAG, "Intercept key by rule " + rule); + } + 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; + mKeyPressCounter = 0; + 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) { + if (DEBUG) { + Log.i(TAG, "press key " + KeyEvent.keyCodeToString(event.getKeyCode())); + } + 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 bc117094dd68..29adde37ab3b 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -5073,7 +5073,7 @@ public final class PowerManagerService extends SystemService } @Override // Binder call - public void userActivity(long eventTime, int event, int flags) { + public void userActivity(int displayId, long eventTime, int event, int flags) { final long now = mClock.uptimeMillis(); if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER) != PackageManager.PERMISSION_GRANTED diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java index 9409eb5d1ad9..a0e2286f72e2 100644 --- a/services/core/java/com/android/server/storage/StorageUserConnection.java +++ b/services/core/java/com/android/server/storage/StorageUserConnection.java @@ -42,8 +42,8 @@ import android.os.storage.StorageManagerInternal; import android.os.storage.StorageVolume; import android.service.storage.ExternalStorageService; import android.service.storage.IExternalStorageService; -import android.util.ArraySet; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; @@ -76,8 +76,8 @@ public final class StorageUserConnection { private final StorageSessionController mSessionController; private final StorageManagerInternal mSmInternal; private final ActiveConnection mActiveConnection = new ActiveConnection(); - @GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>(); - @GuardedBy("mLock") private final Set<Integer> mUidsBlockedOnIo = new ArraySet<>(); + @GuardedBy("mSessionsLock") private final Map<String, Session> mSessions = new HashMap<>(); + @GuardedBy("mSessionsLock") private final SparseArray<Integer> mUidsBlockedOnIo = new SparseArray<>(); private final HandlerThread mHandlerThread; public StorageUserConnection(Context context, int userId, StorageSessionController controller) { @@ -249,7 +249,8 @@ public final class StorageUserConnection { public void notifyAppIoBlocked(String volumeUuid, int uid, int tid, @StorageManager.AppIoBlockedReason int reason) { synchronized (mSessionsLock) { - mUidsBlockedOnIo.add(uid); + int ioBlockedCounter = mUidsBlockedOnIo.get(uid, 0); + mUidsBlockedOnIo.put(uid, ++ioBlockedCounter); } } @@ -262,7 +263,12 @@ public final class StorageUserConnection { public void notifyAppIoResumed(String volumeUuid, int uid, int tid, @StorageManager.AppIoBlockedReason int reason) { synchronized (mSessionsLock) { - mUidsBlockedOnIo.remove(uid); + int ioBlockedCounter = mUidsBlockedOnIo.get(uid, 0); + if (ioBlockedCounter == 0) { + mUidsBlockedOnIo.remove(uid); + } else { + mUidsBlockedOnIo.put(uid, --ioBlockedCounter); + } } } diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java index 95f6059c482e..eaba083c551c 100644 --- a/services/core/java/com/android/server/vibrator/VibratorController.java +++ b/services/core/java/com/android/server/vibrator/VibratorController.java @@ -87,8 +87,8 @@ final class VibratorController { static native long vibratorPerformEffect( long nativePtr, long effect, long strength, long vibrationId); - static native void vibratorPerformComposedEffect(long nativePtr, - VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); + static native long vibratorPerformComposedEffect( + long nativePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); static native void vibratorSetExternalControl(long nativePtr, boolean enabled); @@ -269,13 +269,9 @@ final class VibratorController { VibrationEffect.Composition.PrimitiveEffect[] primitives = effect.getPrimitiveEffects().toArray( new VibrationEffect.Composition.PrimitiveEffect[0]); - mNativeWrapper.compose(primitives, vibrationId); - notifyVibratorOnLocked(); - // Compose don't actually give us an estimated duration, so we just guess here. - long duration = 0; - for (VibrationEffect.Composition.PrimitiveEffect primitive : primitives) { - // TODO(b/177807015): use exposed durations from IVibrator here instead - duration += 20 + primitive.delay; + long duration = mNativeWrapper.compose(primitives, vibrationId); + if (duration > 0) { + notifyVibratorOnLocked(); } return duration; } @@ -393,9 +389,9 @@ final class VibratorController { } /** Turns vibrator on to perform one of the supported composed effects. */ - public void compose( + public long compose( VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) { - VibratorController.vibratorPerformComposedEffect(mNativePtr, effect, + return VibratorController.vibratorPerformComposedEffect(mNativePtr, effect, vibrationId); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 77c369f72323..298128a6a222 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -211,7 +211,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND; import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR; -import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO; +import static com.android.server.wm.WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowManagerService.letterboxBackgroundTypeToString; @@ -6994,6 +6994,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A float aspect = Math.max(parentWidth, parentHeight) / (float) Math.min(parentWidth, parentHeight); + // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with + // set-fixed-orientation-letterbox-aspect-ratio. + final float letterboxAspectRatioOverride = + mWmService.getFixedOrientationLetterboxAspectRatio(); + aspect = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO + ? letterboxAspectRatioOverride : aspect; + // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in // order to use the extra available space. final float maxAspectRatio = info.maxAspectRatio; @@ -7004,16 +7011,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A aspect = minAspectRatio; } - // Override from config_letterboxAspectRatio or via ADB with set-letterbox-aspect-ratio. - // TODO(b/175212232): Rename getTaskLetterboxAspectRatio and all related methods since fixed - // orientation letterbox is on the activity level now. - final float letterboxAspectRatioOverride = mWmService.getTaskLetterboxAspectRatio(); - // Activity min/max aspect ratio restrictions will be respected by the activity-level - // letterboxing (size-compat mode). Therefore this override can control the maximum screen - // area that can be occupied by the app in the letterbox mode. - aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO - ? letterboxAspectRatioOverride : aspect; - // Store the current bounds to be able to revert to size compat mode values below if needed. Rect mTmpFullBounds = new Rect(resolvedBounds); if (forcedOrientation == ORIENTATION_LANDSCAPE) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index bb0f1f0b6f5f..db751e9759fa 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -1504,7 +1504,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Prevent recursion. return; } - mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task); + if (task.isVisible()) { + mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task); + } else { + // Removing a non-visible task doesn't require a transition, but if there is one + // collecting, this should be a member just in case. + mService.getTransitionController().collect(task); + } task.mInRemoveTask = true; try { task.performClearTask(reason); diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 99289e0139b0..5b685b4a0499 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -1305,7 +1305,7 @@ public class AppTransition implements Dump { if (isTransitionSet()) { clear(); mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE; - mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent, + mRemoteAnimationController = new RemoteAnimationController(mService, remoteAnimationAdapter, mHandler); } } diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java index e6e866d8965d..5d6d51377c3f 100644 --- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java +++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java @@ -16,12 +16,6 @@ package com.android.server.wm; -import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; -import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; -import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; -import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT; -import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE; - import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS; import static com.android.server.wm.AnimationAdapterProto.REMOTE; import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; @@ -32,7 +26,6 @@ import android.os.SystemClock; import android.util.proto.ProtoOutputStream; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; -import android.view.WindowManager; import com.android.internal.protolog.common.ProtoLog; import com.android.server.policy.WindowManagerPolicy; @@ -42,7 +35,7 @@ import java.util.ArrayList; class NonAppWindowAnimationAdapter implements AnimationAdapter { - private final WindowContainer mWindow; + private final WindowState mWindow; private RemoteAnimationTarget mTarget; private SurfaceControl mCapturedLeash; @@ -54,43 +47,22 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { return false; } - NonAppWindowAnimationAdapter(WindowContainer w, long durationHint, - long statusBarTransitionDelay) { + NonAppWindowAnimationAdapter(WindowState w, + long durationHint, long statusBarTransitionDelay) { mWindow = w; mDurationHint = durationHint; mStatusBarTransitionDelay = statusBarTransitionDelay; } - static RemoteAnimationTarget[] startNonAppWindowAnimations(WindowManagerService service, - DisplayContent displayContent, @WindowManager.TransitionOldType int transit, - long durationHint, long statusBarTransitionDelay) { - final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); - if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY - || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) { - startNonAppWindowAnimationsForKeyguardExit( - service, durationHint, statusBarTransitionDelay, targets); - } else if (transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT - || transit == TRANSIT_OLD_WALLPAPER_CLOSE) { - final boolean shouldAttachNavBarToApp = - displayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition() - && service.getRecentsAnimationController() == null - && displayContent.getFixedRotationAnimationController() == null; - if (shouldAttachNavBarToApp) { - startNavigationBarWindowAnimation( - displayContent, durationHint, statusBarTransitionDelay, targets); - } - } - return targets.toArray(new RemoteAnimationTarget[targets.size()]); - } - /** * Creates and starts remote animations for all the visible non app windows. * * @return RemoteAnimationTarget[] targets for all the visible non app windows */ - private static void startNonAppWindowAnimationsForKeyguardExit(WindowManagerService service, - long durationHint, long statusBarTransitionDelay, - ArrayList<RemoteAnimationTarget> targets) { + public static RemoteAnimationTarget[] startNonAppWindowAnimationsForKeyguardExit( + WindowManagerService service, long durationHint, long statusBarTransitionDelay) { + final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); + final WindowManagerPolicy policy = service.mPolicy; service.mRoot.forAllWindows(nonAppWindow -> { if (nonAppWindow.mActivityRecord == null && policy.canBeHiddenByKeyguardLw(nonAppWindow) @@ -102,22 +74,7 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { targets.add(nonAppAdapter.createRemoteAnimationTarget()); } }, true /* traverseTopToBottom */); - } - - /** - * Creates and starts remote animation for the navigation bar windows. - * - * @return RemoteAnimationTarget[] targets for all the visible non app windows - */ - private static void startNavigationBarWindowAnimation(DisplayContent displayContent, - long durationHint, long statusBarTransitionDelay, - ArrayList<RemoteAnimationTarget> targets) { - final WindowState navWindow = displayContent.getDisplayPolicy().getNavigationBar(); - final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter( - navWindow.mToken, durationHint, statusBarTransitionDelay); - navWindow.mToken.startAnimation(navWindow.mToken.getPendingTransaction(), - nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION); - targets.add(nonAppAdapter.createRemoteAnimationTarget()); + return targets.toArray(new RemoteAnimationTarget[targets.size()]); } /** @@ -127,7 +84,7 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false, new Rect(), null, mWindow.getPrefixOrderIndex(), mWindow.getLastSurfacePosition(), mWindow.getBounds(), null, mWindow.getWindowConfiguration(), true, null, null, - null, mWindow.getWindowType()); + null); return mTarget; } @@ -163,8 +120,8 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { @Override public void dump(PrintWriter pw, String prefix) { pw.print(prefix); - pw.print("window="); - pw.println(mWindow); + pw.print("token="); + pw.println(mWindow.mToken); if (mTarget != null) { pw.print(prefix); pw.println("Target:"); diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 31663b707004..42cb96f65738 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; + import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS; import static com.android.server.wm.AnimationAdapterProto.REMOTE; import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; @@ -57,7 +60,6 @@ class RemoteAnimationController implements DeathRecipient { private static final long TIMEOUT_MS = 2000; private final WindowManagerService mService; - private final DisplayContent mDisplayContent; private final RemoteAnimationAdapter mRemoteAnimationAdapter; private final ArrayList<RemoteAnimationRecord> mPendingAnimations = new ArrayList<>(); private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations = @@ -70,10 +72,9 @@ class RemoteAnimationController implements DeathRecipient { private boolean mCanceled; private boolean mLinkedToDeathOfRunner; - RemoteAnimationController(WindowManagerService service, DisplayContent displayContent, + RemoteAnimationController(WindowManagerService service, RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) { mService = service; - mDisplayContent = displayContent; mRemoteAnimationAdapter = remoteAnimationAdapter; mHandler = handler; } @@ -220,11 +221,12 @@ class RemoteAnimationController implements DeathRecipient { private RemoteAnimationTarget[] createNonAppWindowAnimations( @WindowManager.TransitionOldType int transit) { ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createNonAppWindowAnimations()"); - return NonAppWindowAnimationAdapter.startNonAppWindowAnimations(mService, - mDisplayContent, - transit, + return (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY + || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) + ? NonAppWindowAnimationAdapter.startNonAppWindowAnimationsForKeyguardExit(mService, mRemoteAnimationAdapter.getDuration(), - mRemoteAnimationAdapter.getStatusBarTransitionDelay()); + mRemoteAnimationAdapter.getStatusBarTransitionDelay()) + : new RemoteAnimationTarget[0]; } private void onAnimationFinished() { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f850e85145df..ea9f2c0e2ae3 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3626,7 +3626,6 @@ class Task extends WindowContainer<WindowContainer> { @Override void resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t) { - if (isOrganized()) return; super.resetSurfacePositionForAnimationLeash(t); } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index ee4c66d05eb4..aadb2722a313 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -240,16 +240,18 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe for (int i = mTargets.size() - 1; i >= 0; --i) { final WindowContainer target = mTargets.valueAt(i); if (target.getParent() != null) { + final SurfaceControl targetLeash = getLeashSurface(target); + final SurfaceControl origParent = getOrigParentSurface(target); // Ensure surfaceControls are re-parented back into the hierarchy. - t.reparent(target.getSurfaceControl(), target.getParent().getSurfaceControl()); - t.setLayer(target.getSurfaceControl(), target.getLastLayer()); + t.reparent(targetLeash, origParent); + t.setLayer(targetLeash, target.getLastLayer()); // TODO(shell-transitions): Once all remotables have been moved, see if there is // a more appropriate place to do the following. This may // involve passing an SF transaction from shell on finish. target.getRelativePosition(tmpPos); - t.setPosition(target.getSurfaceControl(), tmpPos.x, tmpPos.y); - t.setCornerRadius(target.getSurfaceControl(), 0); - t.setShadowRadius(target.getSurfaceControl(), 0); + t.setPosition(targetLeash, tmpPos.x, tmpPos.y); + t.setCornerRadius(targetLeash, 0); + t.setShadowRadius(targetLeash, 0); displays.add(target.getDisplayContent()); } } @@ -465,8 +467,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe final int depth = getChildDepth(topTargets.valueAt(j), sibling); if (depth < 0) continue; if (depth == 0) { - final int siblingMode = sibling.isVisibleRequested() - ? TRANSIT_OPEN : TRANSIT_CLOSE; + final int siblingMode = changes.get(sibling).getTransitMode(sibling); ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " sibling is a top target with mode %s", TransitionInfo.modeToString(siblingMode)); @@ -648,6 +649,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } } + /** Gets the leash surface for a window container */ + private static SurfaceControl getLeashSurface(WindowContainer wc) { + return wc.getSurfaceControl(); + } + + private static SurfaceControl getOrigParentSurface(WindowContainer wc) { + return wc.getParent().getSurfaceControl(); + } + /** * Construct a TransitionInfo object from a set of targets and changes. Also populates the * root surface. @@ -723,7 +733,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe final ChangeInfo info = changes.get(target); final TransitionInfo.Change change = new TransitionInfo.Change( target.mRemoteToken != null ? target.mRemoteToken.toWindowContainerToken() - : null, target.getSurfaceControl()); + : null, getLeashSurface(target)); // TODO(shell-transitions): Use leash for non-organized windows. if (info.mParent != null) { change.setParent(info.mParent.mRemoteToken.toWindowContainerToken()); diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 5f46ffe604a6..6338f39e2e67 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -69,7 +69,7 @@ class TransitionController { * Creates a transition. It can immediately collect participants. */ @NonNull - Transition createTransition(@WindowManager.TransitionOldType int type, + Transition createTransition(@WindowManager.TransitionType int type, @WindowManager.TransitionFlags int flags) { if (mTransitionPlayer == null) { throw new IllegalStateException("Shell Transitions not enabled"); @@ -113,6 +113,14 @@ class TransitionController { } /** + * @return {@code true} if transition is actively collecting changes and `wc` is one of them. + * This is {@code false} once a transition is playing. + */ + boolean isCollecting(@NonNull WindowContainer wc) { + return mCollectingTransition != null && mCollectingTransition.mParticipants.contains(wc); + } + + /** * @return {@code true} if transition is actively playing. This is not necessarily {@code true} * during collection. */ @@ -128,9 +136,7 @@ class TransitionController { /** @return {@code true} if wc is in a participant subtree */ boolean inTransition(@NonNull WindowContainer wc) { - if (mCollectingTransition != null && mCollectingTransition.mParticipants.contains(wc)) { - return true; - } + if (isCollecting(wc)) return true; for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { for (WindowContainer p = wc; p != null; p = p.getParent()) { if (mPlayingTransitions.get(i).mParticipants.contains(p)) { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index b3d2afb1cfb2..0c4ff2fe6365 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -29,7 +29,6 @@ import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.os.UserHandle.USER_NULL; import static android.view.SurfaceControl.Transaction; -import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; @@ -3305,8 +3304,4 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mListeners.remove(listener); unregisterConfigurationChangeListener(listener); } - - @WindowManager.LayoutParams.WindowType int getWindowType() { - return INVALID_WINDOW_TYPE; - } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c9e1605f7f0d..04a560b21da3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -463,12 +463,12 @@ public class WindowManagerService extends IWindowManager.Stub private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000; /** - * Override of task letterbox aspect ratio that is set via ADB with - * set-task-letterbox-aspect-ratio or via {@link - * com.android.internal.R.dimen.config_taskLetterboxAspectRatio} will be ignored + * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with + * set-fixed-orientation-letterbox-aspect-ratio or via {@link + * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored * if it is <= this value. */ - static final float MIN_TASK_LETTERBOX_ASPECT_RATIO = 1.0f; + static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f; @VisibleForTesting WindowManagerConstants mConstants; @@ -1003,9 +1003,9 @@ public class WindowManagerService extends IWindowManager.Stub private boolean mAnimationsDisabled = false; boolean mPointerLocationEnabled = false; - // Aspect ratio of task level letterboxing, values <= MIN_TASK_LETTERBOX_ASPECT_RATIO will be - // ignored. - private volatile float mTaskLetterboxAspectRatio; + // Aspect ratio of letterbox for fixed orientation, values <= + // MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored. + private volatile float mFixedOrientationLetterboxAspectRatio; /** Enum for Letterbox background type. */ @Retention(RetentionPolicy.SOURCE) @@ -1256,8 +1256,8 @@ public class WindowManagerService extends IWindowManager.Stub mAssistantOnTopOfDream = context.getResources().getBoolean( com.android.internal.R.bool.config_assistantOnTopOfDream); - mTaskLetterboxAspectRatio = context.getResources().getFloat( - com.android.internal.R.dimen.config_taskLetterboxAspectRatio); + mFixedOrientationLetterboxAspectRatio = context.getResources().getFloat( + com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio); mLetterboxActivityCornersRadius = context.getResources().getInteger( com.android.internal.R.integer.config_letterboxActivityCornersRadius); mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor( @@ -3853,29 +3853,29 @@ public class WindowManagerService extends IWindowManager.Stub } /** - * Overrides the aspect ratio of task level letterboxing. If given value is <= {@link - * #MIN_TASK_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link - * com.android.internal.R.dimen.config_taskLetterboxAspectRatio} will be ignored and + * Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link + * #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link + * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and * the framework implementation will be used to determine the aspect ratio. */ - void setTaskLetterboxAspectRatio(float aspectRatio) { - mTaskLetterboxAspectRatio = aspectRatio; + void setFixedOrientationLetterboxAspectRatio(float aspectRatio) { + mFixedOrientationLetterboxAspectRatio = aspectRatio; } /** - * Resets the aspect ratio of task level letterboxing to {@link - * com.android.internal.R.dimen.config_taskLetterboxAspectRatio}. + * Resets the aspect ratio of letterbox for fixed orientation to {@link + * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}. */ - void resetTaskLetterboxAspectRatio() { - mTaskLetterboxAspectRatio = mContext.getResources().getFloat( - com.android.internal.R.dimen.config_taskLetterboxAspectRatio); + void resetFixedOrientationLetterboxAspectRatio() { + mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio); } /** - * Gets the aspect ratio of task level letterboxing. + * Gets the aspect ratio of letterbox for fixed orientation. */ - float getTaskLetterboxAspectRatio() { - return mTaskLetterboxAspectRatio; + float getFixedOrientationLetterboxAspectRatio() { + return mFixedOrientationLetterboxAspectRatio; } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 645786c02fc0..a46a8d56e226 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -117,10 +117,10 @@ public class WindowManagerShellCommand extends ShellCommand { return runGetIgnoreOrientationRequest(pw); case "dump-visible-window-views": return runDumpVisibleWindowViews(pw); - case "set-task-letterbox-aspect-ratio": - return runSetTaskLetterboxAspectRatio(pw); - case "get-task-letterbox-aspect-ratio": - return runGetTaskLetterboxAspectRatio(pw); + case "set-fixed-orientation-letterbox-aspect-ratio": + return runSetFixedOrientationLetterboxAspectRatio(pw); + case "get-fixed-orientation-letterbox-aspect-ratio": + return runGetFixedOrientationLetterboxAspectRatio(pw); case "set-letterbox-activity-corners-radius": return runSetLetterboxActivityCornersRadius(pw); case "get-letterbox-activity-corners-radius": @@ -531,12 +531,12 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } - private int runSetTaskLetterboxAspectRatio(PrintWriter pw) throws RemoteException { + private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException { final float aspectRatio; try { String arg = getNextArgRequired(); if ("reset".equals(arg)) { - mInternal.resetTaskLetterboxAspectRatio(); + mInternal.resetFixedOrientationLetterboxAspectRatio(); return 0; } aspectRatio = Float.parseFloat(arg); @@ -549,13 +549,13 @@ public class WindowManagerShellCommand extends ShellCommand { return -1; } - mInternal.setTaskLetterboxAspectRatio(aspectRatio); + mInternal.setFixedOrientationLetterboxAspectRatio(aspectRatio); return 0; } - private int runGetTaskLetterboxAspectRatio(PrintWriter pw) throws RemoteException { - final float aspectRatio = mInternal.getTaskLetterboxAspectRatio(); - if (aspectRatio <= WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO) { + private int runGetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException { + final float aspectRatio = mInternal.getFixedOrientationLetterboxAspectRatio(); + if (aspectRatio <= WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) { pw.println("Letterbox aspect ratio is not set"); } else { pw.println("Letterbox aspect ratio is " + aspectRatio); @@ -692,8 +692,8 @@ public class WindowManagerShellCommand extends ShellCommand { // set-ignore-orientation-request mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */); - // set-task-letterbox-aspect-ratio - mInternal.resetTaskLetterboxAspectRatio(); + // set-fixed-orientation-letterbox-aspect-ratio + mInternal.resetFixedOrientationLetterboxAspectRatio(); // set-letterbox-activity-corners-radius mInternal.resetLetterboxActivityCornersRadius(); @@ -734,12 +734,12 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]"); pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] "); pw.println(" If app requested orientation should be ignored."); - pw.println(" set-task-letterbox-aspect-ratio [reset|aspectRatio]"); - pw.println(" get-task-letterbox-aspect-ratio"); - pw.println(" Aspect ratio of task level letterboxing. If aspectRatio <= " - + WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO); - pw.println(" both it and R.dimen.config_taskLetterboxAspectRatio will be ignored"); - pw.println(" and framework implementation will be used to determine aspect ratio."); + pw.println(" set-fixed-orientation-letterbox-aspect-ratio [reset|aspectRatio]"); + pw.println(" get-fixed-orientation-letterbox-aspect-ratio"); + pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= " + + WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO); + pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will be"); + pw.println(" ignored and framework implementation will determine aspect ratio."); pw.println(" set-letterbox-activity-corners-radius [reset|cornersRadius]"); pw.println(" get-letterbox-activity-corners-radius"); pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,"); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 0a4451f15d75..1830c0758f9a 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5205,7 +5205,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mIsDimming = true; final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0; final int blurRadius = shouldDrawBlurBehind() ? mAttrs.getBlurBehindRadius() : 0; - getDimmer().dimBelow(getSyncTransaction(), this, mAttrs.dimAmount, blurRadius); + getDimmer().dimBelow(getSyncTransaction(), this, dimAmount, blurRadius); } } @@ -5777,9 +5777,4 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top, -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom); } - - @Override - @WindowManager.LayoutParams.WindowType int getWindowType() { - return mAttrs.type; - } } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 5276d9c8a5f1..8867aa747379 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -764,9 +764,4 @@ class WindowToken extends WindowContainer<WindowState> { forAllWindows(WindowState::clearFrozenInsetsState, true /* traverseTopToBottom */); } } - - @Override - @WindowManager.LayoutParams.WindowType int getWindowType() { - return windowType; - } } diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index 7b78b8d23b4b..8efbaf577134 100644 --- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -39,6 +39,8 @@ #include "dataloader.h" +// #define VERBOSE_READ_LOGS + namespace android { namespace { @@ -631,28 +633,65 @@ private: }; void onPageReadsWithUid(dataloader::PageReadsWithUid pageReads) final { + if (!pageReads.size()) { + return; + } + auto trace = atrace_is_tag_enabled(ATRACE_TAG); if (CC_LIKELY(!trace)) { return; } TracedRead last = {}; + auto lastSerialNo = mLastSerialNo; for (auto&& read : pageReads) { - if (read.id != last.fileId || read.uid != last.uid || - read.block != last.firstBlockIdx + last.count) { - traceRead(last); - last = TracedRead{ - .timestampUs = read.bootClockTsUs, - .fileId = read.id, - .uid = read.uid, - .firstBlockIdx = (uint32_t)read.block, - .count = 1, - }; - } else { + const auto expectedSerialNo = lastSerialNo + last.count; +#ifdef VERBOSE_READ_LOGS + { + FileIdx fileIdx = convertFileIdToFileIndex(read.id); + + auto appId = multiuser_get_app_id(read.uid); + auto userId = multiuser_get_user_id(read.uid); + auto trace = android::base:: + StringPrintf("verbose_page_read: serialNo=%lld (expected=%lld) index=%lld " + "file=%d appid=%d userid=%d", + static_cast<long long>(read.serialNo), + static_cast<long long>(expectedSerialNo), + static_cast<long long>(read.block), static_cast<int>(fileIdx), + static_cast<int>(appId), static_cast<int>(userId)); + + ATRACE_BEGIN(trace.c_str()); + ATRACE_END(); + } +#endif // VERBOSE_READ_LOGS + + if (read.serialNo == expectedSerialNo && read.id == last.fileId && + read.uid == last.uid && read.block == last.firstBlockIdx + last.count) { ++last.count; + continue; + } + + // First, trace the reads. + traceRead(last); + + // Second, report missing reads, if any. + if (read.serialNo != expectedSerialNo) { + const auto readsMissing = read.serialNo - expectedSerialNo; + traceMissingReads(readsMissing); } + + last = TracedRead{ + .timestampUs = read.bootClockTsUs, + .fileId = read.id, + .uid = read.uid, + .firstBlockIdx = (uint32_t)read.block, + .count = 1, + }; + lastSerialNo = read.serialNo; } + traceRead(last); + mLastSerialNo = lastSerialNo + last.count; } void traceRead(const TracedRead& read) { @@ -682,6 +721,13 @@ private: ATRACE_END(); } + void traceMissingReads(int64_t count) { + const auto trace = android::base::StringPrintf("missing_page_reads: count=%lld", + static_cast<long long>(count)); + ATRACE_BEGIN(trace.c_str()); + ATRACE_END(); + } + void receiver(unique_fd inout, MetadataMode mode) { std::vector<uint8_t> data; std::vector<IncFsDataBlock> instructions; @@ -828,6 +874,7 @@ private: std::atomic<bool> mStopReceiving = false; std::atomic<bool> mReadLogsEnabled = false; std::chrono::milliseconds mWaitOnEofInterval{WaitOnEofMinInterval}; + int64_t mLastSerialNo{1}; /** Tracks which files have been requested */ std::unordered_set<FileIdx> mRequestedFiles; }; diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp index 89b931d35c40..ef2d0baff031 100644 --- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp +++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp @@ -238,12 +238,12 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, j return result.isOk() ? result.value().count() : -1; } -static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, - jobjectArray composition, jlong vibrationId) { +static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, + jobjectArray composition, jlong vibrationId) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorPerformComposedEffect failed because native wrapper was not initialized"); - return; + return -1; } size_t size = env->GetArrayLength(composition); std::vector<aidl::CompositeEffect> effects; @@ -252,7 +252,8 @@ static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong effects.push_back(effectFromJavaPrimitive(env, element)); } auto callback = wrapper->createCallback(vibrationId); - wrapper->hal()->performComposedEffect(effects, callback); + auto result = wrapper->hal()->performComposedEffect(effects, callback); + return result.isOk() ? result.value().count() : -1; } static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong ptr) { @@ -296,7 +297,7 @@ static const JNINativeMethod method_table[] = { {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude}, {"vibratorPerformEffect", "(JJJJ)J", (void*)vibratorPerformEffect}, {"vibratorPerformComposedEffect", - "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)V", + "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)J", (void*)vibratorPerformComposedEffect}, {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects}, {"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives}, diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java new file mode 100644 index 000000000000..489e2f769a3d --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.test.AndroidTestCase; + +/** + * Tests for {@link com.android.server.BootReceiver} + */ +public class BootReceiverTest extends AndroidTestCase { + public void testLogLinePotentiallySensitive() throws Exception { + /* + * Strings to be dropped from the log as potentially sensitive: register dumps, process + * names, hardware info. + */ + final String[] becomeNull = { + "CPU: 4 PID: 120 Comm: kunit_try_catch Tainted: G W 5.8.0-rc6+ #7", + "Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1 04/01/2014", + "[ 0.083207] RSP: 0000:ffffffff8fe07ca8 EFLAGS: 00010046 ORIG_RAX: 0000000000000000", + "[ 0.084709] RAX: 0000000000000000 RBX: ffffffffff240000 RCX: ffffffff815fcf01", + "[ 0.086109] RDX: dffffc0000000000 RSI: 0000000000000001 RDI: ffffffffff240004", + "[ 0.087509] RBP: ffffffff8fe07d60 R08: fffffbfff1fc0f21 R09: fffffbfff1fc0f21", + "[ 0.088911] R10: ffffffff8fe07907 R11: fffffbfff1fc0f20 R12: ffffffff8fe07d38", + "R13: 0000000000000001 R14: 0000000000000001 R15: ffffffff8fe07e80", + "x29: ffff00003ce07150 x28: ffff80001aa29cc0", + "x1 : 0000000000000000 x0 : ffff00000f628000", + }; + + /* Strings to be left unchanged, including non-sensitive registers and parts of reports. */ + final String[] leftAsIs = { + "FS: 0000000000000000(0000) GS:ffffffff92409000(0000) knlGS:0000000000000000", + "[ 69.2366] [ T6006]c7 6006 =======================================================", + "[ 69.245688] [ T6006] BUG: KFENCE: out-of-bounds in kfence_handle_page_fault", + "[ 69.257816] [ T6006]c7 6006 Out-of-bounds access at 0xffffffca75c45000 ", + "[ 69.273536] [ T6006]c7 6006 __do_kernel_fault+0xa8/0x11c", + "pc : __mutex_lock+0x428/0x99c ", + "sp : ffff00003ce07150", + "Call trace:", + "", + }; + + final String[][] stripped = { + { "Detected corrupted memory at 0xffffffffb6797ff9 [ 0xac . . . . . . ]:", + "Detected corrupted memory at 0xffffffffb6797ff9" }, + }; + for (int i = 0; i < becomeNull.length; i++) { + assertEquals(BootReceiver.stripSensitiveData(becomeNull[i]), null); + } + + for (int i = 0; i < leftAsIs.length; i++) { + assertEquals(BootReceiver.stripSensitiveData(leftAsIs[i]), leftAsIs[i]); + } + + for (int i = 0; i < stripped.length; i++) { + assertEquals(BootReceiver.stripSensitiveData(stripped[i][0]), stripped[i][1]); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index f897d5ca3cc8..d6c11a549dfa 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -495,7 +495,8 @@ public class AbstractAccessibilityServiceConnectionTest { mServiceConnection.performAccessibilityAction(PIP_WINDOWID, ROOT_NODE_ID, ACTION_ACCESSIBILITY_FOCUS, null, INTERACTION_ID, mMockCallback, TID); - verify(mMockIPowerManager).userActivity(anyLong(), anyInt(), anyInt()); + verify(mMockIPowerManager).userActivity(eq(Display.DEFAULT_DISPLAY), anyLong(), anyInt(), + anyInt()); verify(mMockIA11yInteractionConnection).performAccessibilityAction(eq(ROOT_NODE_ID), eq(ACTION_ACCESSIBILITY_FOCUS), any(), eq(INTERACTION_ID), eq(mMockCallback), anyInt(), eq(PID), eq(TID)); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java new file mode 100644 index 000000000000..170f561aa2da --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java @@ -0,0 +1,581 @@ +/* + * 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.accessibility; + + +import static android.view.accessibility.AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; +import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS; +import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS; +import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.Instrumentation; +import android.content.Context; +import android.os.RemoteException; +import android.view.AccessibilityInteractionController; +import android.view.View; +import android.view.ViewRootImpl; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityNodeIdManager; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; +import android.view.accessibility.IAccessibilityInteractionConnectionCallback; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tests that verify expected node and prefetched node results when finding a view by node id. We + * send some requests to the controller via View methods to control message timing. + */ +@RunWith(AndroidJUnit4.class) +public class AccessibilityInteractionControllerNodeRequestsTest { + private AccessibilityInteractionController mAccessibilityInteractionController; + @Mock + private IAccessibilityInteractionConnectionCallback mMockClientCallback1; + @Mock + private IAccessibilityInteractionConnectionCallback mMockClientCallback2; + + @Captor + private ArgumentCaptor<AccessibilityNodeInfo> mFindInfoCaptor; + @Captor private ArgumentCaptor<List<AccessibilityNodeInfo>> mPrefetchInfoListCaptor; + + private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); + private static final int MOCK_CLIENT_1_THREAD_AND_PROCESS_ID = 1; + private static final int MOCK_CLIENT_2_THREAD_AND_PROCESS_ID = 2; + + private static final String FRAME_LAYOUT_DESCRIPTION = "frameLayout"; + private static final String TEXT_VIEW_1_DESCRIPTION = "textView1"; + private static final String TEXT_VIEW_2_DESCRIPTION = "textView2"; + + private TestFrameLayout mFrameLayout; + private TestTextView mTextView1; + private TestTextView2 mTextView2; + + private boolean mSendClient1RequestForTextAfterTextPrefetched; + private boolean mSendClient2RequestForTextAfterTextPrefetched; + private boolean mSendRequestForTextAndIncludeUnImportantViews; + private int mMockClient1InteractionId; + private int mMockClient2InteractionId; + + @Before + public void setUp() throws Throwable { + MockitoAnnotations.initMocks(this); + + mInstrumentation.runOnMainSync(() -> { + final Context context = mInstrumentation.getTargetContext(); + final ViewRootImpl viewRootImpl = new ViewRootImpl(context, context.getDisplay()); + + mFrameLayout = new TestFrameLayout(context); + mTextView1 = new TestTextView(context); + mTextView2 = new TestTextView2(context); + + mFrameLayout.addView(mTextView1); + mFrameLayout.addView(mTextView2); + + // The controller retrieves views through this manager, and registration happens on + // when attached to a window, which we don't have. We can simply reference FrameLayout + // with ROOT_NODE_ID + AccessibilityNodeIdManager.getInstance().registerViewWithId( + mTextView1, mTextView1.getAccessibilityViewId()); + AccessibilityNodeIdManager.getInstance().registerViewWithId( + mTextView2, mTextView2.getAccessibilityViewId()); + + try { + viewRootImpl.setView(mFrameLayout, new WindowManager.LayoutParams(), null); + + } catch (WindowManager.BadTokenException e) { + // activity isn't running, we will ignore BadTokenException. + } + + mAccessibilityInteractionController = + new AccessibilityInteractionController(viewRootImpl); + }); + + } + + @After + public void tearDown() throws Throwable { + AccessibilityNodeIdManager.getInstance().unregisterViewWithId( + mTextView1.getAccessibilityViewId()); + AccessibilityNodeIdManager.getInstance().unregisterViewWithId( + mTextView2.getAccessibilityViewId()); + } + + /** + * Tests a basic request for the root node with prefetch flag + * {@link AccessibilityNodeInfo#FLAG_PREFETCH_DESCENDANTS} + * + * @throws RemoteException + */ + @Test + public void testFindRootView_withOneClient_shouldReturnRootNodeAndPrefetchDescendants() + throws RemoteException { + // Request for our FrameLayout + sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1, + mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS); + mInstrumentation.waitForIdleSync(); + + // Verify we get FrameLayout + verify(mMockClientCallback1).setFindAccessibilityNodeInfoResult( + mFindInfoCaptor.capture(), eq(mMockClient1InteractionId)); + AccessibilityNodeInfo infoSentToService = mFindInfoCaptor.getValue(); + assertEquals(FRAME_LAYOUT_DESCRIPTION, infoSentToService.getContentDescription()); + + verify(mMockClientCallback1).setPrefetchAccessibilityNodeInfoResult( + mPrefetchInfoListCaptor.capture(), eq(mMockClient1InteractionId)); + // The descendants are our two TextViews + List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue(); + assertEquals(2, prefetchedNodes.size()); + assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetchedNodes.get(0).getContentDescription()); + assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNodes.get(1).getContentDescription()); + + } + + /** + * Tests a basic request for TestTextView1's node with prefetch flag + * {@link AccessibilityNodeInfo#FLAG_PREFETCH_SIBLINGS} + * + * @throws RemoteException + */ + @Test + public void testFindTextView_withOneClient_shouldReturnNodeAndPrefetchedSiblings() + throws RemoteException { + // Request for TextView1 + sendNodeRequestToController(AccessibilityNodeInfo.makeNodeId( + mTextView1.getAccessibilityViewId(), AccessibilityNodeProvider.HOST_VIEW_ID), + mMockClientCallback1, mMockClient1InteractionId, FLAG_PREFETCH_SIBLINGS); + mInstrumentation.waitForIdleSync(); + + // Verify we get TextView1 + verify(mMockClientCallback1).setFindAccessibilityNodeInfoResult( + mFindInfoCaptor.capture(), eq(mMockClient1InteractionId)); + AccessibilityNodeInfo infoSentToService = mFindInfoCaptor.getValue(); + assertEquals(TEXT_VIEW_1_DESCRIPTION, infoSentToService.getContentDescription()); + + // Verify the prefetched sibling of TextView1 is TextView2 + verify(mMockClientCallback1).setPrefetchAccessibilityNodeInfoResult( + mPrefetchInfoListCaptor.capture(), eq(mMockClient1InteractionId)); + // TextView2 is the prefetched sibling + List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue(); + assertEquals(1, prefetchedNodes.size()); + assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNodes.get(0).getContentDescription()); + } + + /** + * Tests a series of controller requests to prevent prefetching. + * Request 1: Client 1 requests the root node + * Request 2: When the root node is initialized in + * {@link TestFrameLayout#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)}, + * Client 2 requests TestTextView1's node + * + * Request 2 on the queue prevents prefetching for Request 1. + * + * @throws RemoteException + */ + @Test + public void testFindRootAndTextNodes_withTwoClients_shouldPreventClient1Prefetch() + throws RemoteException { + mFrameLayout.setAccessibilityDelegate(new View.AccessibilityDelegate() { + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + final long nodeId = AccessibilityNodeInfo.makeNodeId( + mTextView1.getAccessibilityViewId(), + AccessibilityNodeProvider.HOST_VIEW_ID); + + // Enqueue a request when this node is found from a different service for + // TextView1 + sendNodeRequestToController(nodeId, mMockClientCallback2, + mMockClient2InteractionId, FLAG_PREFETCH_SIBLINGS); + } + }); + // Client 1 request for FrameLayout + sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1, + mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS); + + mInstrumentation.waitForIdleSync(); + + // Verify client 1 gets FrameLayout + verify(mMockClientCallback1).setFindAccessibilityNodeInfoResult( + mFindInfoCaptor.capture(), eq(mMockClient1InteractionId)); + AccessibilityNodeInfo infoSentToService = mFindInfoCaptor.getValue(); + assertEquals(FRAME_LAYOUT_DESCRIPTION, infoSentToService.getContentDescription()); + + // The second request is put in the queue in the FrameLayout's onInitializeA11yNodeInfo, + // meaning prefetching is interrupted and does not even begin for the first request + verify(mMockClientCallback1, never()) + .setPrefetchAccessibilityNodeInfoResult(anyList(), anyInt()); + + // Verify client 2 gets TextView1 + verify(mMockClientCallback2).setFindAccessibilityNodeInfoResult( + mFindInfoCaptor.capture(), eq(mMockClient2InteractionId)); + infoSentToService = mFindInfoCaptor.getValue(); + assertEquals(TEXT_VIEW_1_DESCRIPTION, infoSentToService.getContentDescription()); + + // Verify the prefetched sibling of TextView1 is TextView2 (FLAG_PREFETCH_SIBLINGS) + verify(mMockClientCallback2).setPrefetchAccessibilityNodeInfoResult( + mPrefetchInfoListCaptor.capture(), eq(mMockClient2InteractionId)); + List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue(); + assertEquals(1, prefetchedNodes.size()); + assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNodes.get(0).getContentDescription()); + } + + /** + * Tests a series of controller same-service requests to interrupt prefetching and satisfy a + * pending node request. + * Request 1: Request the root node + * Request 2: When TextTextView1's node is initialized as part of Request 1's prefetching, + * request TestTextView1's node + * + * Request 1 prefetches TestTextView1's node, is interrupted by a pending request, and checks + * if its prefetched nodes satisfy any pending requests. It satisfies Request 2's request for + * TestTextView1's node. Request 2 is fulfilled, so it is removed from queue and does not + * prefetch. + * + * @throws RemoteException + */ + @Test + public void testFindRootAndTextNode_withOneClient_shouldInterruptPrefetchAndSatisfyPendingMsg() + throws RemoteException { + mSendClient1RequestForTextAfterTextPrefetched = true; + + mTextView1.setAccessibilityDelegate(new View.AccessibilityDelegate(){ + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + info.setContentDescription(TEXT_VIEW_1_DESCRIPTION); + final long nodeId = AccessibilityNodeInfo.makeNodeId( + mTextView1.getAccessibilityViewId(), + AccessibilityNodeProvider.HOST_VIEW_ID); + + if (mSendClient1RequestForTextAfterTextPrefetched) { + // Prevent a loop when processing second request + mSendClient1RequestForTextAfterTextPrefetched = false; + // TextView1 is prefetched here after the FrameLayout is found. Now enqueue a + // same-client request for TextView1 + sendNodeRequestToController(nodeId, mMockClientCallback1, + ++mMockClient1InteractionId, FLAG_PREFETCH_SIBLINGS); + + } + } + }); + // Client 1 requests FrameLayout + sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1, + mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS); + + // Flush out all messages + mInstrumentation.waitForIdleSync(); + + // When TextView1 is prefetched for FrameLayout, we put a message on the queue in + // TextView1's onInitializeA11yNodeInfo that requests for TextView1. The service thus get + // two node results for FrameLayout and TextView1. + verify(mMockClientCallback1, times(2)) + .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(), anyInt()); + + List<AccessibilityNodeInfo> foundNodes = mFindInfoCaptor.getAllValues(); + assertEquals(FRAME_LAYOUT_DESCRIPTION, foundNodes.get(0).getContentDescription()); + assertEquals(TEXT_VIEW_1_DESCRIPTION, foundNodes.get(1).getContentDescription()); + + // The controller will look at FrameLayout's prefetched nodes and find matching nodes in + // pending requests. The prefetched TextView1 matches the second request. The second + // request was removed from queue and prefetching for this request never occurred. + verify(mMockClientCallback1, times(1)) + .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(), + eq(mMockClient1InteractionId - 1)); + List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue(); + assertEquals(1, prefetchedNodes.size()); + assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetchedNodes.get(0).getContentDescription()); + } + + /** + * Like above, but tests a series of controller requests from different services to interrupt + * prefetching and satisfy a pending node request. + * + * @throws RemoteException + */ + @Test + public void testFindRootAndTextNode_withTwoClients_shouldInterruptPrefetchAndSatisfyPendingMsg() + throws RemoteException { + mSendClient2RequestForTextAfterTextPrefetched = true; + mTextView1.setAccessibilityDelegate(new View.AccessibilityDelegate(){ + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + info.setContentDescription(TEXT_VIEW_1_DESCRIPTION); + final long nodeId = AccessibilityNodeInfo.makeNodeId( + mTextView1.getAccessibilityViewId(), + AccessibilityNodeProvider.HOST_VIEW_ID); + + if (mSendClient2RequestForTextAfterTextPrefetched) { + mSendClient2RequestForTextAfterTextPrefetched = false; + // TextView1 is prefetched here. Now enqueue client 2's request for + // TextView1 + sendNodeRequestToController(nodeId, mMockClientCallback2, + mMockClient2InteractionId, FLAG_PREFETCH_SIBLINGS); + } + } + }); + // Client 1 requests FrameLayout + sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1, + mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS); + + mInstrumentation.waitForIdleSync(); + + // Verify client 1 gets FrameLayout + verify(mMockClientCallback1, times(1)) + .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(), anyInt()); + assertEquals(FRAME_LAYOUT_DESCRIPTION, + mFindInfoCaptor.getValue().getContentDescription()); + + // Verify client 1 has prefetched nodes + verify(mMockClientCallback1, times(1)) + .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(), + eq(mMockClient1InteractionId)); + + // Verify client 1's only prefetched node is TextView1 + List<AccessibilityNodeInfo> prefetchedNodes = mPrefetchInfoListCaptor.getValue(); + assertEquals(1, prefetchedNodes.size()); + assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetchedNodes.get(0).getContentDescription()); + + // Verify client 2 gets TextView1 + verify(mMockClientCallback2, times(1)) + .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(), anyInt()); + + assertEquals(TEXT_VIEW_1_DESCRIPTION, mFindInfoCaptor.getValue().getContentDescription()); + + // The second request was removed from queue and prefetching for this client request never + // occurred as it was satisfied. + verify(mMockClientCallback2, never()) + .setPrefetchAccessibilityNodeInfoResult(anyList(), anyInt()); + + } + + @Test + public void testFindNodeById_withTwoDifferentPrefetchFlags_shouldNotSatisfyPendingRequest() + throws RemoteException { + mSendRequestForTextAndIncludeUnImportantViews = true; + mTextView1.setAccessibilityDelegate(new View.AccessibilityDelegate(){ + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + info.setContentDescription(TEXT_VIEW_1_DESCRIPTION); + final long nodeId = AccessibilityNodeInfo.makeNodeId( + mTextView1.getAccessibilityViewId(), + AccessibilityNodeProvider.HOST_VIEW_ID); + + if (mSendRequestForTextAndIncludeUnImportantViews) { + mSendRequestForTextAndIncludeUnImportantViews = false; + // TextView1 is prefetched here for client 1. Now enqueue a request from a + // different client that holds different fetch flags for TextView1 + sendNodeRequestToController(nodeId, mMockClientCallback2, + mMockClient2InteractionId, + FLAG_PREFETCH_SIBLINGS | FLAG_INCLUDE_NOT_IMPORTANT_VIEWS); + } + } + }); + + // Mockito does not make copies of objects when called. It holds references, so + // the captor would point to client 2's results after all requests are processed. Verify + // prefetched node immediately + doAnswer(invocation -> { + List<AccessibilityNodeInfo> prefetched = invocation.getArgument(0); + assertEquals(TEXT_VIEW_1_DESCRIPTION, prefetched.get(0).getContentDescription()); + return null; + }).when(mMockClientCallback1).setPrefetchAccessibilityNodeInfoResult(anyList(), + eq(mMockClient1InteractionId)); + + // Client 1 requests FrameLayout + sendNodeRequestToController(ROOT_NODE_ID, mMockClientCallback1, + mMockClient1InteractionId, FLAG_PREFETCH_DESCENDANTS); + + mInstrumentation.waitForIdleSync(); + + // Verify client 1 gets FrameLayout + verify(mMockClientCallback1, times(1)) + .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(), + eq(mMockClient1InteractionId)); + + assertEquals(FRAME_LAYOUT_DESCRIPTION, + mFindInfoCaptor.getValue().getContentDescription()); + + // Verify client 1 has prefetched results. The only prefetched node is TextView1 + // (from above doAnswer) + verify(mMockClientCallback1, times(1)) + .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(), + eq(mMockClient1InteractionId)); + + // Verify client 2 gets TextView1 + verify(mMockClientCallback2, times(1)) + .setFindAccessibilityNodeInfoResult(mFindInfoCaptor.capture(), + eq(mMockClient2InteractionId)); + assertEquals(TEXT_VIEW_1_DESCRIPTION, + mFindInfoCaptor.getValue().getContentDescription()); + // Verify client 2 has TextView2 as a prefetched node + verify(mMockClientCallback2, times(1)) + .setPrefetchAccessibilityNodeInfoResult(mPrefetchInfoListCaptor.capture(), + eq(mMockClient2InteractionId)); + List<AccessibilityNodeInfo> prefetchedNode = mPrefetchInfoListCaptor.getValue(); + assertEquals(1, prefetchedNode.size()); + assertEquals(TEXT_VIEW_2_DESCRIPTION, prefetchedNode.get(0).getContentDescription()); + } + + private void sendNodeRequestToController(long requestedNodeId, + IAccessibilityInteractionConnectionCallback callback, int interactionId, + int prefetchFlags) { + final int processAndThreadId = callback == mMockClientCallback1 + ? MOCK_CLIENT_1_THREAD_AND_PROCESS_ID + : MOCK_CLIENT_2_THREAD_AND_PROCESS_ID; + + mAccessibilityInteractionController.findAccessibilityNodeInfoByAccessibilityIdClientThread( + requestedNodeId, + null, interactionId, + callback, prefetchFlags, + processAndThreadId, + processAndThreadId, null, null); + + } + + private class TestFrameLayout extends FrameLayout { + + TestFrameLayout(Context context) { + super(context); + } + + @Override + public int getWindowVisibility() { + // We aren't attached to a window so let's pretend + return VISIBLE; + } + + @Override + public boolean isShown() { + // Controller check + return true; + } + + @Override + public int getAccessibilityViewId() { + // static id doesn't reset after tests so return the same one + return 0; + } + + @Override + public void addChildrenForAccessibility(ArrayList<View> outChildren) { + // ViewGroup#addChildrenForAccessbility sorting logic will switch these two + outChildren.add(mTextView1); + outChildren.add(mTextView2); + } + + @Override + public boolean includeForAccessibility() { + return true; + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setContentDescription(FRAME_LAYOUT_DESCRIPTION); + } + } + + private class TestTextView extends TextView { + TestTextView(Context context) { + super(context); + } + + @Override + public int getWindowVisibility() { + return VISIBLE; + } + + @Override + public boolean isShown() { + return true; + } + + @Override + public int getAccessibilityViewId() { + return 1; + } + + @Override + public boolean includeForAccessibility() { + return true; + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setContentDescription(TEXT_VIEW_1_DESCRIPTION); + } + } + + private class TestTextView2 extends TextView { + TestTextView2(Context context) { + super(context); + } + + @Override + public int getWindowVisibility() { + return VISIBLE; + } + + @Override + public boolean isShown() { + return true; + } + + @Override + public int getAccessibilityViewId() { + return 2; + } + + @Override + public boolean includeForAccessibility() { + return true; + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setContentDescription(TEXT_VIEW_2_DESCRIPTION); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java index 85b8fcbbcc61..ebb73e877db4 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java @@ -39,6 +39,7 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; +import android.view.Display; import android.view.KeyEvent; import androidx.test.InstrumentationRegistry; @@ -172,8 +173,8 @@ public class KeyEventDispatcherTest { mFilter1SequenceCaptor.getValue()); assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT)); - verify(mMockPowerManagerService, times(1)).userActivity(anyLong(), - eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); + verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY), + anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); assertFalse(isTimeoutPending(mMessageCapturingHandler)); } @@ -204,8 +205,8 @@ public class KeyEventDispatcherTest { mFilter2SequenceCaptor.getValue()); assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT)); - verify(mMockPowerManagerService, times(1)).userActivity(anyLong(), - eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); + verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY), + anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); assertFalse(isTimeoutPending(mMessageCapturingHandler)); } @@ -221,8 +222,8 @@ public class KeyEventDispatcherTest { mFilter2SequenceCaptor.getValue()); assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT)); - verify(mMockPowerManagerService, times(1)).userActivity(anyLong(), - eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); + verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY), + anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); assertFalse(isTimeoutPending(mMessageCapturingHandler)); } @@ -238,8 +239,8 @@ public class KeyEventDispatcherTest { mFilter2SequenceCaptor.getValue()); assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT)); - verify(mMockPowerManagerService, times(1)).userActivity(anyLong(), - eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); + verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY), + anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); assertFalse(isTimeoutPending(mMessageCapturingHandler)); } @@ -308,8 +309,8 @@ public class KeyEventDispatcherTest { mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0)); assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT)); - verify(mMockPowerManagerService, times(1)).userActivity(anyLong(), - eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); + verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY), + anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); } @Test @@ -357,8 +358,8 @@ public class KeyEventDispatcherTest { mKeyEventDispatcher.handleMessage(getTimedMessage(mMessageCapturingHandler, 0)); assertFalse(mInputEventsHandler.hasMessages(SEND_FRAMEWORK_KEY_EVENT)); - verify(mMockPowerManagerService, times(1)).userActivity(anyLong(), - eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); + verify(mMockPowerManagerService, times(1)).userActivity(eq(Display.DEFAULT_DISPLAY), + anyLong(), eq(PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY), eq(0)); } /* diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java index 97daea3011ea..0b1c120d8a1e 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java @@ -67,7 +67,6 @@ public class SnippetTest { .setPropertyName(propertyKeyString) .addSnippetMatches( SnippetMatchProto.newBuilder() - .setValuesIndex(0) .setExactMatchPosition(29) .setExactMatchBytes(3) .setWindowPosition(26) @@ -174,7 +173,6 @@ public class SnippetTest { .setPropertyName("sender.name") .addSnippetMatches( SnippetMatchProto.newBuilder() - .setValuesIndex(0) .setExactMatchPosition(0) .setExactMatchBytes(4) .setWindowPosition(0) @@ -186,7 +184,6 @@ public class SnippetTest { .setPropertyName("sender.email") .addSnippetMatches( SnippetMatchProto.newBuilder() - .setValuesIndex(0) .setExactMatchPosition(0) .setExactMatchBytes(20) .setWindowPosition(0) diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index 2d6605ae222f..c0a38b874914 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -239,8 +239,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); assertTrue(mService.hasPendingEscrowToken(PRIMARY_USER_ID)); - mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */) - .getResponseCode(); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode()); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID)); @@ -268,8 +268,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */) - .getResponseCode(); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode()); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); mLocalService.setLockCredentialWithToken(nonePassword(), handle, token, PRIMARY_USER_ID); @@ -294,8 +294,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */) - .getResponseCode(); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode()); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); mService.setLockCredential(pattern, password, PRIMARY_USER_ID); @@ -369,6 +369,36 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } @Test + public void testActivateMultipleEscrowTokens() throws Exception { + byte[] token0 = "some-high-entropy-secure-token-0".getBytes(); + byte[] token1 = "some-high-entropy-secure-token-1".getBytes(); + byte[] token2 = "some-high-entropy-secure-token-2".getBytes(); + + LockscreenCredential password = newPassword("password"); + LockscreenCredential pattern = newPattern("123654"); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); + + long handle0 = mLocalService.addEscrowToken(token0, PRIMARY_USER_ID, null); + long handle1 = mLocalService.addEscrowToken(token1, PRIMARY_USER_ID, null); + long handle2 = mLocalService.addEscrowToken(token2, PRIMARY_USER_ID, null); + + // Activate token + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode()); + + // Verify tokens work + assertTrue(mLocalService.isEscrowTokenActive(handle0, PRIMARY_USER_ID)); + assertTrue(mLocalService.setLockCredentialWithToken( + pattern, handle0, token0, PRIMARY_USER_ID)); + assertTrue(mLocalService.isEscrowTokenActive(handle1, PRIMARY_USER_ID)); + assertTrue(mLocalService.setLockCredentialWithToken( + pattern, handle1, token1, PRIMARY_USER_ID)); + assertTrue(mLocalService.isEscrowTokenActive(handle2, PRIMARY_USER_ID)); + assertTrue(mLocalService.setLockCredentialWithToken( + pattern, handle2, token2, PRIMARY_USER_ID)); + } + + @Test public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception { LockscreenCredential password = newPassword("password"); LockscreenCredential pattern = newPattern("123654"); @@ -494,8 +524,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { reset(mDevicePolicyManager); long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); - mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */) - .getResponseCode(); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode()); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); mService.onCleanupUser(PRIMARY_USER_ID); diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index ea27331ac4ca..6e5fbd0b6ed0 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -764,7 +764,7 @@ public class PowerManagerServiceTest { createService(); startSystem(); - mService.getBinderServiceInstance().userActivity(mClock.now(), + mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(), PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); verify(mInattentiveSleepWarningControllerMock, never()).show(); @@ -773,7 +773,7 @@ public class PowerManagerServiceTest { verify(mInattentiveSleepWarningControllerMock, never()).dismiss(anyBoolean()); when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(true); - mService.getBinderServiceInstance().userActivity(mClock.now(), + mService.getBinderServiceInstance().userActivity(Display.DEFAULT_DISPLAY, mClock.now(), PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0); verify(mInattentiveSleepWarningControllerMock, times(1)).dismiss(true); } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java index 4634e12f1530..2a3c2c46ce4e 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java @@ -100,16 +100,17 @@ final class FakeVibratorControllerProvider { return EFFECT_DURATION; } - public void compose(VibrationEffect.Composition.PrimitiveEffect[] effect, + public long compose(VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) { VibrationEffect.Composed composed = new VibrationEffect.Composed(Arrays.asList(effect)); mEffects.add(composed); applyLatency(); - long duration = EFFECT_DURATION * effect.length; + long duration = 0; for (VibrationEffect.Composition.PrimitiveEffect e : effect) { - duration += e.delay; + duration += EFFECT_DURATION + e.delay; } scheduleListener(duration, vibrationId); + return duration; } public void setExternalControl(boolean enabled) { diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java index 815aa8ee66ae..bad3e4c2ed92 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java @@ -19,6 +19,7 @@ package com.android.server.vibrator; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; @@ -202,7 +203,7 @@ public class VibratorControllerTest { VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked) VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK); - controller.on(effect, 11); + assertEquals(10L, controller.on(effect, 11)); assertTrue(controller.isVibrating()); verify(mNativeWrapperMock).perform(eq((long) VibrationEffect.EFFECT_CLICK), @@ -212,13 +213,14 @@ public class VibratorControllerTest { @Test public void on_withComposed_performsEffect() { mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + when(mNativeWrapperMock.compose(any(), anyLong())).thenReturn(15L); VibratorController controller = createController(); VibrationEffect.Composed effect = (VibrationEffect.Composed) VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10) .compose(); - controller.on(effect, 12); + assertEquals(15L, controller.on(effect, 12)); ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor = ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class); 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/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index ea971809c8f8..2fdd63ed93d5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -18,13 +18,11 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; -import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -37,9 +35,7 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITIO import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -98,7 +94,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50, true /* changeNeedsSnapshot */); mAdapter.setCallingPidUid(123, 456); runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0); - mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter, mHandler); + mController = new RemoteAnimationController(mWm, mAdapter, mHandler); } private WindowState createAppOverlayWindow() { @@ -524,164 +520,6 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { } } - @Test - public void testNonAppTarget_sendNavBar() throws Exception { - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - mDisplayContent.mOpeningApps.add(win.mActivityRecord); - final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"); - mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); - final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); - spyOn(policy); - doReturn(true).when(policy).shouldAttachNavBarToAppDuringTransition(); - - final AnimationAdapter adapter = mController.createRemoteAnimationRecord( - win.mActivityRecord, new Point(50, 100), null, - new Rect(50, 100, 150, 150), null).mAdapter; - adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, - mFinishedCallback); - mController.goodToGo(TRANSIT_OLD_TASK_OPEN); - mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); - final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = - ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN), - appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), - finishedCaptor.capture()); - boolean containNavTarget = false; - for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { - if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { - containNavTarget = true; - break; - } - } - assertTrue(containNavTarget); - } - - @Test - public void testNonAppTarget_notSendNavBar_notAttachToApp() throws Exception { - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - mDisplayContent.mOpeningApps.add(win.mActivityRecord); - final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"); - mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); - final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); - spyOn(policy); - doReturn(false).when(policy).shouldAttachNavBarToAppDuringTransition(); - - final AnimationAdapter adapter = mController.createRemoteAnimationRecord( - win.mActivityRecord, new Point(50, 100), null, - new Rect(50, 100, 150, 150), null).mAdapter; - adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, - mFinishedCallback); - mController.goodToGo(TRANSIT_OLD_TASK_OPEN); - mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); - final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = - ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN), - appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), - finishedCaptor.capture()); - boolean containNavTarget = false; - for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { - if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { - containNavTarget = true; - break; - } - } - assertFalse(containNavTarget); - } - - @Test - public void testNonAppTarget_notSendNavBar_controlledByFixedRotation() throws Exception { - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - mDisplayContent.mOpeningApps.add(win.mActivityRecord); - final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"); - mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); - final FixedRotationAnimationController mockController = - mock(FixedRotationAnimationController.class); - doReturn(mockController).when(mDisplayContent).getFixedRotationAnimationController(); - final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); - spyOn(policy); - doReturn(true).when(policy).shouldAttachNavBarToAppDuringTransition(); - - final AnimationAdapter adapter = mController.createRemoteAnimationRecord( - win.mActivityRecord, new Point(50, 100), null, - new Rect(50, 100, 150, 150), null).mAdapter; - adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, - mFinishedCallback); - mController.goodToGo(TRANSIT_OLD_TASK_OPEN); - mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); - final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = - ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN), - appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), - finishedCaptor.capture()); - boolean containNavTarget = false; - for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { - if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { - containNavTarget = true; - break; - } - } - assertFalse(containNavTarget); - } - - @Test - public void testNonAppTarget_notSendNavBar_controlledByRecents() throws Exception { - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - mDisplayContent.mOpeningApps.add(win.mActivityRecord); - final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"); - mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); - final RecentsAnimationController mockController = - mock(RecentsAnimationController.class); - doReturn(mockController).when(mWm).getRecentsAnimationController(); - final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); - spyOn(policy); - doReturn(true).when(policy).shouldAttachNavBarToAppDuringTransition(); - - final AnimationAdapter adapter = mController.createRemoteAnimationRecord( - win.mActivityRecord, new Point(50, 100), null, - new Rect(50, 100, 150, 150), null).mAdapter; - adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, - mFinishedCallback); - mController.goodToGo(TRANSIT_OLD_TASK_OPEN); - mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); - final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = - ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN), - appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), - finishedCaptor.capture()); - boolean containNavTarget = false; - for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { - if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { - containNavTarget = true; - break; - } - } - assertFalse(containNavTarget); - } - private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { verify(binder, atLeast(0)).asBinder(); verifyNoMoreInteractions(binder); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index b73c66407874..2f1d7eb404ad 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -655,7 +655,7 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); // Portrait fixed app without max aspect. - prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); + prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT); final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); final Rect activityBounds = new Rect(mActivity.getBounds()); @@ -676,6 +676,96 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + public void testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMinAspectRatio() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed + // orientation letterbox. + mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(1.1f); + mActivity.info.minAspectRatio = 3; + prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT); + + final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + final Rect activityBounds = new Rect(mActivity.getBounds()); + + // Display shouldn't be rotated. + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, + mActivity.mDisplayContent.getLastOrientation()); + assertTrue(displayBounds.width() > displayBounds.height()); + + // App should launch in fixed orientation letterbox. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); + assertFalse(mActivity.inSizeCompatMode()); + + // Activity bounds should respect minimum aspect ratio for activity. + assertEquals(displayBounds.height(), activityBounds.height()); + assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.minAspectRatio), + activityBounds.width()); + } + + @Test + public void testDisplayIgnoreOrientationRequest_fixedOrientationAppRespectMaxAspectRatio() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + // Portrait fixed app with max aspect ratio lower that aspect ratio override for fixed + // orientation letterbox. + mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(3); + prepareUnresizable(mActivity, /* maxAspect= */ 2, SCREEN_ORIENTATION_PORTRAIT); + + final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + final Rect activityBounds = new Rect(mActivity.getBounds()); + + // Display shouldn't be rotated. + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, + mActivity.mDisplayContent.getLastOrientation()); + assertTrue(displayBounds.width() > displayBounds.height()); + + // App should launch in fixed orientation letterbox. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); + assertFalse(mActivity.inSizeCompatMode()); + + // Activity bounds should respect maximum aspect ratio for activity. + assertEquals(displayBounds.height(), activityBounds.height()); + assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.maxAspectRatio), + activityBounds.width()); + } + + @Test + public void testDisplayIgnoreOrientationRequest_fixedOrientationAppWithAspectRatioOverride() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed + // orientation letterbox. + final float fixedOrientationLetterboxAspectRatio = 1.1f; + mActivity.mWmService.setFixedOrientationLetterboxAspectRatio( + fixedOrientationLetterboxAspectRatio); + prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); + + final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + final Rect activityBounds = new Rect(mActivity.getBounds()); + + // Display shouldn't be rotated. + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, + mActivity.mDisplayContent.getLastOrientation()); + assertTrue(displayBounds.width() > displayBounds.height()); + + // App should launch in fixed orientation letterbox. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); + assertFalse(mActivity.inSizeCompatMode()); + + // Activity bounds should respect aspect ratio override for fixed orientation letterbox. + assertEquals(displayBounds.height(), activityBounds.height()); + assertEquals((int) Math.rint(displayBounds.height() / fixedOrientationLetterboxAspectRatio), + activityBounds.width()); + } + + @Test public void testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() { // Set up a display in landscape and ignoring orientation request. diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 154a899fb5ff..79ef8680dfec 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -25,11 +25,14 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; +import static android.window.TransitionInfo.isIndependent; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -343,6 +346,76 @@ public class TransitionTests extends WindowTestsBase { tasks[showWallpaperTask].mRemoteToken.toWindowContainerToken()).getFlags()); } + @Test + public void testIndependent() { + final Transition transition = createTestTransition(TRANSIT_OPEN); + ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; + ArraySet<WindowContainer> participants = transition.mParticipants; + ITaskOrganizer mockOrg = mock(ITaskOrganizer.class); + + final Task openTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task openInOpenTask = createTaskInStack(openTask, 0); + final ActivityRecord openInOpen = createActivityRecord(openInOpenTask); + + final Task changeTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task changeInChangeTask = createTaskInStack(changeTask, 0); + final Task openInChangeTask = createTaskInStack(changeTask, 0); + final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask); + final ActivityRecord openInChange = createActivityRecord(openInChangeTask); + // set organizer for everything so that they all get added to transition info + for (Task t : new Task[]{ + openTask, openInOpenTask, changeTask, changeInChangeTask, openInChangeTask}) { + t.mTaskOrganizer = mockOrg; + } + + // Start states. + changes.put(openTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); + changes.put(changeTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); + changes.put(openInOpenTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); + changes.put(openInChangeTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); + changes.put(changeInChangeTask, + new Transition.ChangeInfo(true /* vis */, false /* exChg */)); + changes.put(openInOpen, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); + changes.put(openInChange, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); + changes.put(changeInChange, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); + fillChangeMap(changes, openTask); + // End states. + changeInChange.mVisibleRequested = true; + openInOpen.mVisibleRequested = true; + openInChange.mVisibleRequested = true; + + int transit = TRANSIT_OLD_TASK_OPEN; + int flags = 0; + + // Check full promotion from leaf + participants.add(changeInChange); + participants.add(openInOpen); + participants.add(openInChange); + // Explicitly add changeTask (to test independence with parents) + participants.add(changeTask); + ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes); + TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + // Root changes should always be considered independent + assertTrue(isIndependent( + info.getChange(openTask.mRemoteToken.toWindowContainerToken()), info)); + assertTrue(isIndependent( + info.getChange(changeTask.mRemoteToken.toWindowContainerToken()), info)); + + // Children of a open/close change are not independent + assertFalse(isIndependent( + info.getChange(openInOpenTask.mRemoteToken.toWindowContainerToken()), info)); + + // Non-root changes are not independent + assertFalse(isIndependent( + info.getChange(changeInChangeTask.mRemoteToken.toWindowContainerToken()), info)); + + // open/close within a change are independent + assertTrue(isIndependent( + info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info)); + } + /** Fill the change map with all the parents of top. Change maps are usually fully populated */ private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes, WindowContainer top) { diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java index 5b5570b27a9b..2f89bfb60d3d 100644 --- a/telephony/java/android/telephony/TelephonyDisplayInfo.java +++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java @@ -30,8 +30,8 @@ import java.util.Objects; * necessarily a precise or accurate representation of the current state and should be treated * accordingly. * To be notified of changes in TelephonyDisplayInfo, use - * {@link TelephonyManager#registerPhoneStateListener} with a {@link PhoneStateListener} - * that implements {@link PhoneStateListener.DisplayInfoChangedListener}. + * {@link TelephonyManager#registerTelephonyCallback} with a {@link TelephonyCallback} + * that implements {@link TelephonyCallback.DisplayInfoListener}. * Override the onDisplayInfoChanged() method to handle the broadcast. */ public final class TelephonyDisplayInfo implements Parcelable { diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index b46440d7d557..c48bd211fac2 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -5949,25 +5949,20 @@ public class TelephonyManager { * @param events The telephony state(s) of interest to the listener, * as a bitwise-OR combination of {@link PhoneStateListener} * LISTEN_ flags. - * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}. + * @deprecated Use {@link #registerTelephonyCallback(Executor, TelephonyCallback)}. */ @Deprecated public void listen(PhoneStateListener listener, int events) { - if (!listener.isExecutorSet()) { - throw new IllegalStateException("PhoneStateListener should be created on a thread " - + "with Looper.myLooper() != null"); - } - boolean notifyNow = getITelephony() != null; - mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); - if (mTelephonyRegistryMgr != null) { - if (events != PhoneStateListener.LISTEN_NONE) { - mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(mSubId, - getOpPackageName(), getAttributionTag(), listener, events, notifyNow); - } else { - unregisterPhoneStateListener(listener); - } + if (mContext == null) return; + boolean notifyNow = (getITelephony() != null); + TelephonyRegistryManager telephonyRegistry = + (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (telephonyRegistry != null) { + telephonyRegistry.listenFromListener(mSubId, getOpPackageName(), + getAttributionTag(), listener, events, notifyNow); } else { - throw new IllegalStateException("telephony service is null."); + Rlog.w(TAG, "telephony registry not ready."); } } @@ -15049,66 +15044,69 @@ public class TelephonyManager { } /** - * Registers a listener object to receive notification of changes - * in specified telephony states. + * Registers a callback object to receive notification of changes in specified telephony states. * <p> - * To register a listener, pass a {@link PhoneStateListener} which implements + * To register a callback, pass a {@link TelephonyCallback} which implements * interfaces of events. For example, - * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements - * {@link PhoneStateListener.ServiceStateChangedListener}. + * FakeServiceStateCallback extends {@link TelephonyCallback} implements + * {@link TelephonyCallback.ServiceStateListener}. * * At registration, and when a specified telephony state changes, the telephony manager invokes - * the appropriate callback method on the listener object and passes the current (updated) + * the appropriate callback method on the callback object and passes the current (updated) * values. * <p> * * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, * applies to the given subId. Otherwise, applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds, - * pass a separate listener object to each TelephonyManager object created with + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To register events for multiple + * subIds, pass a separate callback object to each TelephonyManager object created with * {@link #createForSubscriptionId}. * * Note: if you call this method while in the middle of a binder transaction, you <b>must</b> * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A * {@link SecurityException} will be thrown otherwise. * - * This API should be used sparingly -- large numbers of listeners will cause system - * instability. If a process has registered too many listeners without unregistering them, it - * may encounter an {@link IllegalStateException} when trying to register more listeners. + * This API should be used sparingly -- large numbers of callbacks will cause system + * instability. If a process has registered too many callbacks without unregistering them, it + * may encounter an {@link IllegalStateException} when trying to register more callbacks. * * @param executor The executor of where the callback will execute. - * @param listener The {@link PhoneStateListener} object to register. + * @param callback The {@link TelephonyCallback} object to register. */ - public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, - @NonNull PhoneStateListener listener) { - if (executor == null || listener == null) { - throw new IllegalArgumentException("PhoneStateListener and executor must be non-null"); + public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull TelephonyCallback callback) { + if (executor == null || callback == null) { + throw new IllegalArgumentException("TelephonyCallback and executor must be non-null"); } mTelephonyRegistryMgr = (TelephonyRegistryManager) mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); if (mTelephonyRegistryMgr != null) { - mTelephonyRegistryMgr.registerPhoneStateListener(executor, mSubId, - getOpPackageName(), getAttributionTag(), listener, getITelephony() != null); + mTelephonyRegistryMgr.registerTelephonyCallback(executor, mSubId, getOpPackageName(), + getAttributionTag(), callback, getITelephony() != null); } else { throw new IllegalStateException("telephony service is null."); } } /** - * Unregister an existing {@link PhoneStateListener}. + * Unregister an existing {@link TelephonyCallback}. * - * @param listener The {@link PhoneStateListener} object to unregister. + * @param callback The {@link TelephonyCallback} object to unregister. */ - public void unregisterPhoneStateListener(@NonNull PhoneStateListener listener) { + public void unregisterTelephonyCallback(@NonNull TelephonyCallback callback) { if (mContext == null) { throw new IllegalStateException("telephony service is null."); } + if (callback.callback == null) { + return; + } + mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); if (mTelephonyRegistryMgr != null) { - mTelephonyRegistryMgr.unregisterPhoneStateListener(mSubId, getOpPackageName(), - getAttributionTag(), listener, getITelephony() != null); + mTelephonyRegistryMgr.unregisterTelephonyCallback(mSubId, getOpPackageName(), + getAttributionTag(), callback, getITelephony() != null); } else { throw new IllegalStateException("telephony service is null."); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index e87f3d9aec76..7e04baa5f82b 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1200,15 +1200,6 @@ interface ITelephony { void shutdownMobileRadios(); /** - * Set phone radio type and access technology. - * - * @param rafs an RadioAccessFamily array to indicate all phone's - * new radio access family. The length of RadioAccessFamily - * must equ]]al to phone count. - */ - void setRadioCapability(in RadioAccessFamily[] rafs); - - /** * Get phone radio type and access technology. * * @param phoneId which phone you want to get diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt index 9b0cfa9db30f..c1315f64c56b 100644 --- a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt +++ b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt @@ -21,7 +21,7 @@ import android.content.res.Resources import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY -import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdChangedListener +import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener import android.provider.Settings import android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI import android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE @@ -120,9 +120,9 @@ class MultinetworkPolicyTrackerTest { MULTIPATH_PREFERENCE_PERFORMANCE.toString()) val listenerCaptor = ArgumentCaptor.forClass( - ActiveDataSubscriptionIdChangedListener::class.java) + ActiveDataSubscriptionIdListener::class.java) verify(telephonyManager, times(1)) - .registerPhoneStateListener(any(), listenerCaptor.capture()) + .registerTelephonyCallback(any(), listenerCaptor.capture()) val listener = listenerCaptor.value listener.onActiveDataSubscriptionIdChanged(testSubId) diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index ba64b302aeda..d72517147e4d 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -103,6 +103,7 @@ import static com.android.testutils.ConcurrentUtils.await; import static com.android.testutils.ConcurrentUtils.durationOf; import static com.android.testutils.ExceptionUtils.ignoreExceptions; import static com.android.testutils.HandlerUtils.waitForIdleSerialExecutor; +import static com.android.testutils.MiscAsserts.assertContainsAll; import static com.android.testutils.MiscAsserts.assertContainsExactly; import static com.android.testutils.MiscAsserts.assertEmpty; import static com.android.testutils.MiscAsserts.assertLength; @@ -203,6 +204,7 @@ import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.NetworkStack; import android.net.NetworkStackClient; +import android.net.NetworkStateSnapshot; import android.net.NetworkTestResultParcelable; import android.net.OemNetworkPreferences; import android.net.ProxyInfo; @@ -1083,9 +1085,11 @@ public class ConnectivityServiceTest { } } - private Set<UidRange> uidRangesForUid(int uid) { + private Set<UidRange> uidRangesForUids(int... uids) { final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); + for (final int uid : uids) { + ranges.add(new UidRange(uid, uid)); + } return ranges; } @@ -1215,13 +1219,13 @@ public class ConnectivityServiceTest { public void establishForMyUid(LinkProperties lp) throws Exception { final int uid = Process.myUid(); - establish(lp, uid, uidRangesForUid(uid), true, true, false); + establish(lp, uid, uidRangesForUids(uid), true, true, false); } public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode) throws Exception { final int uid = Process.myUid(); - establish(makeLinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet, + establish(makeLinkProperties(), uid, uidRangesForUids(uid), validated, hasInternet, isStrictMode); } @@ -1330,7 +1334,7 @@ public class ConnectivityServiceTest { } - private void processBroadcastForVpn(Intent intent) { + private void processBroadcast(Intent intent) { mServiceContext.sendBroadcast(intent); HandlerUtils.waitForIdle(mVMSHandlerThread, TIMEOUT_MS); waitForIdle(); @@ -1421,6 +1425,7 @@ public class ConnectivityServiceTest { private static final int VPN_UID = UserHandle.getUid(PRIMARY_USER, 10043); private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "", UserInfo.FLAG_PRIMARY); + private static final UserHandle PRIMARY_USER_HANDLE = new UserHandle(PRIMARY_USER); private static final int RESTRICTED_USER = 1; private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "", @@ -1438,6 +1443,8 @@ public class ConnectivityServiceTest { MockitoAnnotations.initMocks(this); when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO)); + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE)); when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO); // canHaveRestrictedProfile does not take a userId. It applies to the userId of the context // it was started from, i.e., PRIMARY_USER. @@ -1663,6 +1670,7 @@ public class ConnectivityServiceTest { assertNull(mCm.getActiveNetworkForUid(Process.myUid())); // Test getAllNetworks() assertEmpty(mCm.getAllNetworks()); + assertEmpty(mCm.getAllNetworkStateSnapshot()); } /** @@ -6387,7 +6395,7 @@ public class ConnectivityServiceTest { vpnNetworkCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - final Set<UidRange> ranges = uidRangesForUid(uid); + final Set<UidRange> ranges = uidRangesForUids(uid); mMockVpn.registerAgent(ranges); mMockVpn.setUnderlyingNetworks(new Network[0]); @@ -6859,7 +6867,7 @@ public class ConnectivityServiceTest { final int uid = Process.myUid(); NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); assertNotNull("nc=" + nc, nc.getUids()); - assertEquals(nc.getUids(), uidRangesForUid(uid)); + assertEquals(nc.getUids(), uidRangesForUids(uid)); assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE); // Set an underlying network and expect to see the VPN transports change. @@ -6880,7 +6888,7 @@ public class ConnectivityServiceTest { addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); // Send a USER_ADDED broadcast for it. - processBroadcastForVpn(addedIntent); + processBroadcast(addedIntent); // Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added // restricted user. @@ -6905,7 +6913,7 @@ public class ConnectivityServiceTest { final Intent removedIntent = new Intent(ACTION_USER_REMOVED); removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - processBroadcastForVpn(removedIntent); + processBroadcast(removedIntent); // Expect that the VPN gains the UID range for the restricted user, and that the capability // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved. @@ -6963,7 +6971,7 @@ public class ConnectivityServiceTest { final Intent addedIntent = new Intent(ACTION_USER_ADDED); addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - processBroadcastForVpn(addedIntent); + processBroadcast(addedIntent); assertNull(mCm.getActiveNetworkForUid(uid)); assertNull(mCm.getActiveNetworkForUid(restrictedUid)); @@ -6974,7 +6982,7 @@ public class ConnectivityServiceTest { final Intent removedIntent = new Intent(ACTION_USER_REMOVED); removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - processBroadcastForVpn(removedIntent); + processBroadcast(removedIntent); assertNull(mCm.getActiveNetworkForUid(uid)); assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); @@ -7129,7 +7137,7 @@ public class ConnectivityServiceTest { assertFalse(mCm.isActiveNetworkMetered()); // Connect VPN network. - mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUid(Process.myUid()), + mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUids(Process.myUid()), new LinkProperties()); mMockVpn.connect(true); waitForIdle(); @@ -7568,7 +7576,7 @@ public class ConnectivityServiceTest { final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED); addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - processBroadcastForVpn(addedIntent); + processBroadcast(addedIntent); // Lockdown VPN disables teardown and enables lockdown. assertFalse(mMockVpn.getEnableTeardown()); @@ -9287,7 +9295,7 @@ public class ConnectivityServiceTest { private void assertUidRangesUpdatedForMyUid(boolean add) throws Exception { final int uid = Process.myUid(); - assertVpnUidRangesUpdated(add, uidRangesForUid(uid), uid); + assertVpnUidRangesUpdated(add, uidRangesForUids(uid), uid); } private void assertVpnUidRangesUpdated(boolean add, Set<UidRange> vpnRanges, int exemptUid) @@ -9676,7 +9684,7 @@ public class ConnectivityServiceTest { } @Test - public void testOemNetworkRequestFactoryCorrectlySetsUids() + public void testOemNetworkRequestFactoryMultiplePrefsCorrectlySetsUids() throws Exception { // Arrange PackageManager mocks final String testPackageName2 = "com.google.apps.dialer"; @@ -9707,6 +9715,46 @@ public class ConnectivityServiceTest { } @Test + public void testOemNetworkRequestFactoryMultipleUsersCorrectlySetsUids() + throws Exception { + // Arrange users + final int secondUser = 10; + final UserHandle secondUserHandle = new UserHandle(secondUser); + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle)); + + // Arrange PackageManager mocks + mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); + + // Build OemNetworkPreferences object + final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; + final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() + .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) + .build(); + + // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() + final List<ConnectivityService.NetworkRequestInfo> nris = + new ArrayList<>( + mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences( + pref)); + + // UIDs for all users and all managed packages should be present. + // Two users each with two packages. + final int expectedUidSize = 2; + final List<UidRange> uids = + new ArrayList<>(nris.get(0).mRequests.get(0).networkCapabilities.getUids()); + assertEquals(expectedUidSize, uids.size()); + + // Sort by uid to access nris by index + uids.sort(Comparator.comparingInt(uid -> uid.start)); + final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID); + assertEquals(TEST_PACKAGE_UID, uids.get(0).start); + assertEquals(TEST_PACKAGE_UID, uids.get(0).stop); + assertEquals(secondUserTestPackageUid, uids.get(1).start); + assertEquals(secondUserTestPackageUid, uids.get(1).stop); + } + + @Test public void testOemNetworkRequestFactoryAddsPackagesToCorrectPreference() throws Exception { // Expectations @@ -9911,7 +9959,7 @@ public class ConnectivityServiceTest { assertEquals(1, mService.mDefaultNetworkRequests.size()); final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUid(testPackageUid)); + toUidRangeStableParcels(uidRangesForUids(testPackageUid)); setupSetOemNetworkPreferenceForPreferenceTest( networkPrefToSetup, uidRanges, testPackageName); } @@ -10141,6 +10189,10 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(defaultNetworkCallback); } + /** + * This method assumes that the same uidRanges input will be used to verify that dependencies + * are called as expected. + */ private void verifySetOemNetworkPreferenceForPreference( @NonNull final UidRangeParcel[] uidRanges, final int addUidRangesNetId, @@ -10148,16 +10200,30 @@ public class ConnectivityServiceTest { final int removeUidRangesNetId, final int removeUidRangesTimes, final boolean shouldDestroyNetwork) throws RemoteException { + verifySetOemNetworkPreferenceForPreference(uidRanges, uidRanges, + addUidRangesNetId, addUidRangesTimes, removeUidRangesNetId, removeUidRangesTimes, + shouldDestroyNetwork); + } + + private void verifySetOemNetworkPreferenceForPreference( + @NonNull final UidRangeParcel[] addedUidRanges, + @NonNull final UidRangeParcel[] removedUidRanges, + final int addUidRangesNetId, + final int addUidRangesTimes, + final int removeUidRangesNetId, + final int removeUidRangesTimes, + final boolean shouldDestroyNetwork) throws RemoteException { final boolean useAnyIdForAdd = OEM_PREF_ANY_NET_ID == addUidRangesNetId; final boolean useAnyIdForRemove = OEM_PREF_ANY_NET_ID == removeUidRangesNetId; // Validate netd. verify(mMockNetd, times(addUidRangesTimes)) .networkAddUidRanges( - (useAnyIdForAdd ? anyInt() : eq(addUidRangesNetId)), eq(uidRanges)); + (useAnyIdForAdd ? anyInt() : eq(addUidRangesNetId)), eq(addedUidRanges)); verify(mMockNetd, times(removeUidRangesTimes)) .networkRemoveUidRanges( - (useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)), eq(uidRanges)); + (useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)), + eq(removedUidRanges)); if (shouldDestroyNetwork) { verify(mMockNetd, times(1)) .networkDestroy((useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId))); @@ -10175,7 +10241,7 @@ public class ConnectivityServiceTest { final int testPackageUid = 123; final String testPackageName = "com.google.apps.contacts"; final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUid(testPackageUid)); + toUidRangeStableParcels(uidRangesForUids(testPackageUid)); // Validate the starting requests only includes the fallback request. assertEquals(1, mService.mDefaultNetworkRequests.size()); @@ -10204,9 +10270,8 @@ public class ConnectivityServiceTest { OEM_NETWORK_PREFERENCE_OEM_PAID; // Arrange PackageManager mocks - final int testPackageNameUid = 123; final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUid(testPackageNameUid)); + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); // Verify the starting state. No networks should be connected. @@ -10271,9 +10336,8 @@ public class ConnectivityServiceTest { OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; // Arrange PackageManager mocks - final int testPackageNameUid = 123; final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUid(testPackageNameUid)); + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); // Verify the starting state. This preference doesn't support using the fallback network @@ -10334,9 +10398,8 @@ public class ConnectivityServiceTest { OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; // Arrange PackageManager mocks - final int testPackageNameUid = 123; final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUid(testPackageNameUid)); + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); // Verify the starting state. This preference doesn't support using the fallback network @@ -10387,9 +10450,8 @@ public class ConnectivityServiceTest { OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; // Arrange PackageManager mocks - final int testPackageNameUid = 123; final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUid(testPackageNameUid)); + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); // Verify the starting state. This preference doesn't support using the fallback network @@ -10428,6 +10490,109 @@ public class ConnectivityServiceTest { true /* shouldDestroyNetwork */); } + @Test + public void testMultilayerForMultipleUsersEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID; + + // Arrange users + final int secondUser = 10; + final UserHandle secondUserHandle = new UserHandle(secondUser); + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle)); + + // Arrange PackageManager mocks + final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID); + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels( + uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid)); + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + + // Verify the starting state. No networks should be connected. + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Test that we correctly add the expected values for multiple users. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Test that we correctly remove the expected values for multiple users. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + mCellNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + } + + @Test + public void testMultilayerForBroadcastedUsersEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID; + + // Arrange users + final int secondUser = 10; + final UserHandle secondUserHandle = new UserHandle(secondUser); + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE)); + + // Arrange PackageManager mocks + final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID); + final UidRangeParcel[] uidRangesSingleUser = + toUidRangeStableParcels( + uidRangesForUids(TEST_PACKAGE_UID)); + final UidRangeParcel[] uidRangesBothUsers = + toUidRangeStableParcels( + uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid)); + setupSetOemNetworkPreferenceForPreferenceTest( + networkPref, uidRangesSingleUser, TEST_PACKAGE_NAME); + + // Verify the starting state. No networks should be connected. + verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Test that we correctly add the expected values for multiple users. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Send a broadcast indicating a user was added. + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle)); + final Intent addedIntent = new Intent(ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser)); + processBroadcast(addedIntent); + + // Test that we correctly add values for all users and remove for the single user. + verifySetOemNetworkPreferenceForPreference(uidRangesBothUsers, uidRangesSingleUser, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + + // Send a broadcast indicating a user was removed. + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE)); + final Intent removedIntent = new Intent(ACTION_USER_REMOVED); + removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser)); + processBroadcast(removedIntent); + + // Test that we correctly add values for the single user and remove for the all users. + verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser, uidRangesBothUsers, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + } + /** * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order: * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback @@ -10659,4 +10824,83 @@ public class ConnectivityServiceTest { // default NCs will be unregistered in tearDown } + + @Test + public void testGetAllNetworkStateSnapshot() throws Exception { + verifyNoNetwork(); + + // Setup test cellular network with specified LinkProperties and NetworkCapabilities, + // verify the content of the snapshot matches. + final LinkProperties cellLp = new LinkProperties(); + final LinkAddress myIpv4Addr = new LinkAddress(InetAddress.getByName("192.0.2.129"), 25); + final LinkAddress myIpv6Addr = new LinkAddress(InetAddress.getByName("2001:db8::1"), 64); + cellLp.setInterfaceName("test01"); + cellLp.addLinkAddress(myIpv4Addr); + cellLp.addLinkAddress(myIpv6Addr); + cellLp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234"))); + cellLp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); + cellLp.addRoute(new RouteInfo(myIpv4Addr, null)); + cellLp.addRoute(new RouteInfo(myIpv6Addr, null)); + final NetworkCapabilities cellNcTemplate = new NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_CELLULAR).addCapability(NET_CAPABILITY_MMS).build(); + + final TestNetworkCallback cellCb = new TestNetworkCallback(); + mCm.requestNetwork(new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(), + cellCb); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp, cellNcTemplate); + mCellNetworkAgent.connect(true); + cellCb.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + List<NetworkStateSnapshot> snapshots = mCm.getAllNetworkStateSnapshot(); + assertLength(1, snapshots); + + // Compose the expected cellular snapshot for verification. + final NetworkCapabilities cellNc = + mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()); + final NetworkStateSnapshot cellSnapshot = new NetworkStateSnapshot( + mCellNetworkAgent.getNetwork(), cellNc, cellLp, + null, ConnectivityManager.TYPE_MOBILE); + assertEquals(cellSnapshot, snapshots.get(0)); + + // Connect wifi and verify the snapshots. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + waitForIdle(); + // Compose the expected wifi snapshot for verification. + final NetworkCapabilities wifiNc = + mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()); + final NetworkStateSnapshot wifiSnapshot = new NetworkStateSnapshot( + mWiFiNetworkAgent.getNetwork(), wifiNc, new LinkProperties(), null, + ConnectivityManager.TYPE_WIFI); + + snapshots = mCm.getAllNetworkStateSnapshot(); + assertLength(2, snapshots); + assertContainsAll(snapshots, cellSnapshot, wifiSnapshot); + + // Set cellular as suspended, verify the snapshots will not contain suspended networks. + // TODO: Consider include SUSPENDED networks, which should be considered as + // temporary shortage of connectivity of a connected network. + mCellNetworkAgent.suspend(); + waitForIdle(); + snapshots = mCm.getAllNetworkStateSnapshot(); + assertLength(1, snapshots); + assertEquals(wifiSnapshot, snapshots.get(0)); + + // Disconnect wifi, verify the snapshots contain nothing. + mWiFiNetworkAgent.disconnect(); + waitForIdle(); + snapshots = mCm.getAllNetworkStateSnapshot(); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertLength(0, snapshots); + + mCellNetworkAgent.resume(); + waitForIdle(); + snapshots = mCm.getAllNetworkStateSnapshot(); + assertLength(1, snapshots); + assertEquals(cellSnapshot, snapshots.get(0)); + + mCellNetworkAgent.disconnect(); + waitForIdle(); + verifyNoNetwork(); + mCm.unregisterNetworkCallback(cellCb); + } } |