diff options
445 files changed, 10366 insertions, 3561 deletions
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java index bccef533be32..3a114173869a 100644 --- a/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java +++ b/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java @@ -53,7 +53,7 @@ public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase "applyPostLayoutPolicy", "applySurfaceChanges", "AppTransitionReady", - "closeSurfaceTransactiom", + "closeSurfaceTransaction", "openSurfaceTransaction", "performLayout", "performSurfacePlacement", @@ -98,6 +98,10 @@ public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase } mTraceMarkParser.forAllSlices((key, slices) -> { + if (slices.size() < 2) { + Log.w(TAG, "No sufficient samples: " + key); + return; + } for (TraceMarkSlice slice : slices) { state.addExtraResult(key, (long) (slice.getDurationInSeconds() * NANOS_PER_S)); } diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index f6bfaa356799..e08d22ce194c 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -24,14 +24,6 @@ package android.app.appsearch { method @Deprecated @NonNull public android.app.appsearch.AppSearchManager.SearchContext.Builder setDatabaseName(@NonNull String); } - public interface AppSearchMigrationHelper { - method public void queryAndTransform(@NonNull String, @NonNull android.app.appsearch.AppSearchMigrationHelper.Transformer) throws java.lang.Exception; - } - - public static interface AppSearchMigrationHelper.Transformer { - method @NonNull public android.app.appsearch.GenericDocument transform(int, int, @NonNull android.app.appsearch.GenericDocument) throws java.lang.Exception; - } - public final class AppSearchResult<ValueType> { method @Nullable public String getErrorMessage(); method public int getResultCode(); @@ -109,11 +101,6 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.AppSearchSchema.Int64PropertyConfig.Builder setCardinality(int); } - public static interface AppSearchSchema.Migrator { - method public default void onDowngrade(int, int, @NonNull android.app.appsearch.AppSearchMigrationHelper) throws java.lang.Exception; - method public default void onUpgrade(int, int, @NonNull android.app.appsearch.AppSearchMigrationHelper) throws java.lang.Exception; - } - public abstract static class AppSearchSchema.PropertyConfig { method public int getCardinality(); method @NonNull public String getName(); @@ -221,6 +208,13 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec); } + public abstract class Migrator { + ctor public Migrator(); + ctor public Migrator(int); + method @NonNull @WorkerThread public abstract android.app.appsearch.GenericDocument onDowngrade(int, int, @NonNull android.app.appsearch.GenericDocument); + method @NonNull @WorkerThread public abstract android.app.appsearch.GenericDocument onUpgrade(int, int, @NonNull android.app.appsearch.GenericDocument); + } + public class PackageIdentifier { ctor public PackageIdentifier(@NonNull String, @NonNull byte[]); method @NonNull public String getPackageName(); @@ -357,7 +351,7 @@ 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.Map<java.lang.String,android.app.appsearch.Migrator> getMigrators(); method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas(); method @NonNull public java.util.Set<java.lang.String> getSchemasNotDisplayedBySystem(); method @Deprecated @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi(); @@ -371,7 +365,7 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchemas(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>); method @NonNull public android.app.appsearch.SetSchemaRequest build(); method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean); - method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.AppSearchSchema.Migrator); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.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 @Deprecated @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean); diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 486acb4a3d7e..9ea73a9773bc 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -114,8 +114,6 @@ public final class AppSearchSession implements Closeable { * @param executor Executor on which to invoke the callback. * @param callback Callback to receive errors resulting from setting the schema. If the * operation succeeds, the callback will be invoked with {@code null}. - * @see android.app.appsearch.AppSearchSchema.Migrator - * @see android.app.appsearch.AppSearchMigrationHelper.Transformer */ // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are // exposed. diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchMigrationHelper.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchMigrationHelper.java deleted file mode 100644 index 37943fc24ded..000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchMigrationHelper.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import android.annotation.NonNull; -import android.annotation.SuppressLint; - -/** - * The helper class for {@link AppSearchSchema} migration. - * - * <p>It will query and migrate {@link GenericDocument} in given type to a new version. - */ -public interface AppSearchMigrationHelper { - - /** - * Queries all documents that need to be migrated to the different version, and transform - * documents to that version by passing them to the provided {@link Transformer}. - * - * @param schemaType The schema that need be updated and migrated {@link GenericDocument} under - * this type. - * @param transformer The {@link Transformer} that will upgrade or downgrade a {@link - * GenericDocument} to new version. - * @see Transformer#transform - */ - // Rethrow the Generic Exception thrown from the Transformer. - @SuppressLint("GenericException") - void queryAndTransform(@NonNull String schemaType, @NonNull Transformer transformer) - throws Exception; - - /** The class to migrate {@link GenericDocument} between different version. */ - interface Transformer { - - /** - * Translates a {@link GenericDocument} from a version to a different version. - * - * <p>If the uri, schema type or namespace is changed via the transform, it will apply to - * the new {@link GenericDocument}. - * - * @param currentVersion The current version of the document's schema. - * @param finalVersion The final version that documents need to be migrated to. - * @param document The {@link GenericDocument} need to be translated to new version. - * @return A {@link GenericDocument} in new version. - */ - @NonNull - // This method will be overridden by users, allow them to throw any customer Exceptions. - @SuppressLint("GenericException") - GenericDocument transform( - int currentVersion, int finalVersion, @NonNull GenericDocument document) - throws Exception; - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java index 2cf52716cffc..55f0c80b16d0 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.app.appsearch.exceptions.IllegalSchemaException; import android.app.appsearch.util.BundleUtil; import android.os.Bundle; @@ -179,7 +178,7 @@ public final class AppSearchSchema { * @throws IllegalStateException if the version is negative or the builder has already been * used. * @see AppSearchSession#setSchema - * @see AppSearchSchema.Migrator + * @see Migrator * @see SetSchemaRequest.Builder#setMigrator */ @NonNull @@ -861,43 +860,4 @@ public final class AppSearchSchema { } } } - - /** - * A migrator class to translate {@link GenericDocument} from different version of {@link - * AppSearchSchema} - */ - public interface Migrator { - - /** - * Migrates {@link GenericDocument} to a newer version of {@link AppSearchSchema}. - * - * <p>This methods will be invoked only if the {@link SetSchemaRequest} is setting a higher - * version number than the current {@link AppSearchSchema} saved in AppSearch. - * - * @param currentVersion The current version of the document's schema. - * @param targetVersion The final version that documents need to be migrated to. - * @param helper The helper class could help to query all documents need to be migrated. - */ - // This method will be overridden by users, allow them to throw any customer Exceptions. - @SuppressLint("GenericException") - default void onUpgrade( - int currentVersion, int targetVersion, @NonNull AppSearchMigrationHelper helper) - throws Exception {} - - /** - * Migrates {@link GenericDocument} to an older version of {@link AppSearchSchema}. - * - * <p>The methods will be invoked only if the {@link SetSchemaRequest} is setting a higher - * version number than the current {@link AppSearchSchema} saved in AppSearch. - * - * @param currentVersion The current version of the document's schema. - * @param targetVersion The final version that documents need to be migrated to. - * @param helper The helper class could help to query all documents need to be migrated. - */ - // This method will be overridden by users, allow them to throw any customer Exceptions. - @SuppressLint("GenericException") - default void onDowngrade( - int currentVersion, int targetVersion, @NonNull AppSearchMigrationHelper helper) - throws Exception {} - } } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/Migrator.java b/apex/appsearch/framework/java/external/android/app/appsearch/Migrator.java new file mode 100644 index 000000000000..5ae9a4186a52 --- /dev/null +++ b/apex/appsearch/framework/java/external/android/app/appsearch/Migrator.java @@ -0,0 +1,107 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import android.annotation.NonNull; +import android.annotation.WorkerThread; + +import com.android.internal.util.Preconditions; + +/** + * A migrator class to translate {@link GenericDocument} from different version of {@link + * AppSearchSchema} + * + * <p>Make non-backwards-compatible changes will delete all stored documents in old schema. You can + * save your documents by setting {@link Migrator} via the {@link + * SetSchemaRequest.Builder#setMigrator} for each type and target version you want to save. + * + * <p>{@link #onDowngrade} or {@link #onUpgrade} will be triggered if the version number of the + * schema stored in AppSearch is different with the version in the request. + * + * <p>If any error or Exception occurred in the {@link #onDowngrade} or {@link #onUpgrade}, all the + * setSchema request will be rejected unless the schema changes are backwards-compatible, and stored + * documents won't have any observable changes. + */ +public abstract class Migrator { + private final int mStartVersion; + + /** + * Creates a {@link Migrator} will trigger migration for any version less than the final version + * in the new schema. + */ + public Migrator() { + this(/*startVersion=*/ 0); + } + + /** + * Creates a {@link Migrator} with a non-negative start version. + * + * <p>Providing 0 will trigger migration for any version less than the final version in the new + * schema. + * + * @param startVersion The migration will be only triggered for those versions greater or equal + * to the given startVersion. + */ + public Migrator(int startVersion) { + Preconditions.checkArgumentNonnegative(startVersion); + mStartVersion = startVersion; + } + + /** + * @return {@code True} if the current version need to be migrated. + * @hide + */ + public boolean shouldMigrateToFinalVersion(int currentVersion, int finalVersion) { + return currentVersion >= mStartVersion && currentVersion != finalVersion; + } + + /** + * Migrates {@link GenericDocument} to a newer version of {@link AppSearchSchema}. + * + * <p>This method will be invoked only if the {@link SetSchemaRequest} is setting a higher + * version number than the current {@link AppSearchSchema} saved in AppSearch. + * + * <p>This method will be invoked on the background worker thread. + * + * @param currentVersion The current version of the document's schema. + * @param targetVersion The final version that documents need to be migrated to. + * @param document The {@link GenericDocument} need to be translated to new version. + * @return A {@link GenericDocument} in new version. + */ + @WorkerThread + @NonNull + public abstract GenericDocument onUpgrade( + int currentVersion, int targetVersion, @NonNull GenericDocument document); + + /** + * Migrates {@link GenericDocument} to an older version of {@link AppSearchSchema}. + * + * <p>This method will be invoked only if the {@link SetSchemaRequest} is setting a lower + * version number than the current {@link AppSearchSchema} saved in AppSearch. + * + * <p>This method will be invoked on the background worker thread. + * + * @param currentVersion The current version of the document's schema. + * @param targetVersion The final version that documents need to be migrated to. + * @param document The {@link GenericDocument} need to be translated to new version. + * @return A {@link GenericDocument} in new version. + */ + @WorkerThread + @NonNull + public abstract GenericDocument onDowngrade( + int currentVersion, int targetVersion, @NonNull GenericDocument document); +} 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 c05406358995..c1eedcd63dbf 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java @@ -63,24 +63,33 @@ import java.util.Set; * android.app.appsearch.exceptions.AppSearchException}, with a message describing the * incompatibility. As a result, the previously set schema will remain unchanged. * - * <p>Backward incompatible changes can be made by setting {@link - * SetSchemaRequest.Builder#setForceOverride} method to {@code true}. This deletes all documents - * that are incompatible with the new schema. The new schema is then saved and persisted to disk. + * <p>Backward incompatible changes can be made by : + * + * <ul> + * <li>setting {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. This + * deletes all documents that are incompatible with the new schema. The new schema is then + * saved and persisted to disk. + * <li>Add a {@link Migrator} for each incompatible type and make no deletion. The migrator will + * migrate documents from it's old schema version to the new version. Migrated types will be + * set into both {@link SetSchemaResponse#getIncompatibleTypes()} and {@link + * SetSchemaResponse#getMigratedTypes()}. See the migration section below. + * </ul> * * @see AppSearchSession#setSchema + * @see Migrator */ public final class SetSchemaRequest { private final Set<AppSearchSchema> mSchemas; private final Set<String> mSchemasNotDisplayedBySystem; private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages; - private final Map<String, AppSearchSchema.Migrator> mMigrators; + private final Map<String, Migrator> mMigrators; private final boolean mForceOverride; SetSchemaRequest( @NonNull Set<AppSearchSchema> schemas, @NonNull Set<String> schemasNotDisplayedBySystem, @NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages, - @NonNull Map<String, AppSearchSchema.Migrator> migrators, + @NonNull Map<String, Migrator> migrators, boolean forceOverride) { mSchemas = Preconditions.checkNotNull(schemas); mSchemasNotDisplayedBySystem = Preconditions.checkNotNull(schemasNotDisplayedBySystem); @@ -129,9 +138,12 @@ public final class SetSchemaRequest { return copy; } - /** Returns the map of {@link android.app.appsearch.AppSearchSchema.Migrator}. */ + /** + * Returns the map of {@link Migrator}, the key will be the schema type of the {@link Migrator} + * associated with. + */ @NonNull - public Map<String, AppSearchSchema.Migrator> getMigrators() { + public Map<String, Migrator> getMigrators() { return Collections.unmodifiableMap(mMigrators); } @@ -164,7 +176,7 @@ public final class SetSchemaRequest { 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<>(); + private final Map<String, Migrator> mMigrators = new ArrayMap<>(); private boolean mForceOverride = false; private boolean mBuilt = false; @@ -295,7 +307,7 @@ public final class SetSchemaRequest { } /** - * Sets the {@link android.app.appsearch.AppSearchSchema.Migrator}. + * Sets the {@link Migrator}. * * @param schemaType The schema type to set migrator on. * @param migrator The migrator translate a document from it's old version to a new @@ -303,8 +315,7 @@ public final class SetSchemaRequest { */ @NonNull @SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects. - public Builder setMigrator( - @NonNull String schemaType, @NonNull AppSearchSchema.Migrator migrator) { + public Builder setMigrator(@NonNull String schemaType, @NonNull Migrator migrator) { Preconditions.checkNotNull(schemaType); Preconditions.checkNotNull(migrator); mMigrators.put(schemaType, migrator); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java index 98cd49b79737..d63e4372f61f 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java @@ -128,8 +128,8 @@ public class SetSchemaResponse { * Returns a {@link Set} of schema type whose new definitions set in the {@link * AppSearchSession#setSchema} call were incompatible with the pre-existing schema. * - * <p>If a {@link android.app.appsearch.AppSearchSchema.Migrator} is provided for this type and - * the migration is success triggered. The type will also appear in {@link #getMigratedTypes()}. + * <p>If a {@link Migrator} is provided for this type and the migration is success triggered. + * The type will also appear in {@link #getMigratedTypes()}. * * @see AppSearchSession#setSchema * @see SetSchemaRequest.Builder#setForceOverride diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java new file mode 100644 index 000000000000..fae8ad48e61c --- /dev/null +++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java @@ -0,0 +1,118 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch.util; + +import android.annotation.NonNull; +import android.app.appsearch.AppSearchResult; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.Migrator; +import android.app.appsearch.exceptions.AppSearchException; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * Utilities for schema migration. + * + * @hide + */ +public final class SchemaMigrationUtil { + private static final String TAG = "AppSearchMigrateUtil"; + + private SchemaMigrationUtil() {} + + /** + * Finds out which incompatible schema type won't be migrated by comparing its current and final + * version number. + */ + @NonNull + public static Set<String> getUnmigratedIncompatibleTypes( + @NonNull Set<String> incompatibleSchemaTypes, + @NonNull Map<String, Migrator> migrators, + @NonNull Map<String, Integer> currentVersionMap, + @NonNull Map<String, Integer> finalVersionMap) + throws AppSearchException { + Set<String> unmigratedSchemaTypes = new ArraySet<>(); + for (String unmigratedSchemaType : incompatibleSchemaTypes) { + Integer currentVersion = currentVersionMap.get(unmigratedSchemaType); + Integer finalVersion = finalVersionMap.get(unmigratedSchemaType); + if (currentVersion == null) { + // impossible, we have done something wrong. + throw new AppSearchException( + AppSearchResult.RESULT_UNKNOWN_ERROR, + "Cannot find the current version number for schema type: " + + unmigratedSchemaType); + } + if (finalVersion == null) { + // The schema doesn't exist in the SetSchemaRequest. + unmigratedSchemaTypes.add(unmigratedSchemaType); + continue; + } + // we don't have migrator or won't trigger migration for this schema type. + Migrator migrator = migrators.get(unmigratedSchemaType); + if (migrator == null + || !migrator.shouldMigrateToFinalVersion(currentVersion, finalVersion)) { + unmigratedSchemaTypes.add(unmigratedSchemaType); + } + } + return Collections.unmodifiableSet(unmigratedSchemaTypes); + } + + /** + * Triggers upgrade or downgrade migration for the given schema type if its version stored in + * AppSearch is different with the version in the request. + * + * @return {@code True} if we trigger the migration for the given type. + */ + public static boolean shouldTriggerMigration( + @NonNull String schemaType, + @NonNull Migrator migrator, + @NonNull Map<String, Integer> currentVersionMap, + @NonNull Map<String, Integer> finalVersionMap) + throws AppSearchException { + Integer currentVersion = currentVersionMap.get(schemaType); + Integer finalVersion = finalVersionMap.get(schemaType); + if (currentVersion == null) { + Log.d(TAG, "The SchemaType: " + schemaType + " not present in AppSearch."); + return false; + } + if (finalVersion == null) { + throw new AppSearchException( + AppSearchResult.RESULT_INVALID_ARGUMENT, + "Receive a migrator for schema type : " + + schemaType + + ", but the schema doesn't exist in the request."); + } + return migrator.shouldMigrateToFinalVersion(currentVersion, finalVersion); + } + + /** Builds a Map of SchemaType and its version of given set of {@link AppSearchSchema}. */ + @NonNull + public static Map<String, Integer> buildVersionMap( + @NonNull Collection<AppSearchSchema> schemas) { + Map<String, Integer> currentVersionMap = new ArrayMap<>(schemas.size()); + for (AppSearchSchema currentSchema : schemas) { + currentVersionMap.put(currentSchema.getSchemaType(), currentSchema.getVersion()); + } + return currentVersionMap; + } +} 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 27c9ccb2cca6..77aa14c37d79 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -193,7 +193,7 @@ public class AppSearchManagerService extends SystemService { try { // TODO(b/173451571): reduce burden of binder thread by enqueue request onto // a separate thread. - impl.putDocument(packageName, databaseName, document); + impl.putDocument(packageName, databaseName, document, /*logger=*/ null); resultBuilder.setSuccess(document.getUri(), /*result=*/ null); } catch (Throwable t) { resultBuilder.setResult(document.getUri(), throwableToFailedResult(t)); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java index 7c92456bea49..ad94a0a57d27 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java @@ -368,7 +368,8 @@ public class VisibilityStore { packageAccessibleDocuments.toArray(new GenericDocument[0])); } - mAppSearchImpl.putDocument(PACKAGE_NAME, DATABASE_NAME, visibilityDocument.build()); + mAppSearchImpl.putDocument( + PACKAGE_NAME, DATABASE_NAME, visibilityDocument.build(), /*logger=*/ null); // Update derived data structures. mNotPlatformSurfaceableMap.put(prefix, schemasNotPlatformSurfaceable); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java index e2c211b7d303..5e8760ec35c2 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java @@ -17,6 +17,7 @@ package com.android.server.appsearch.external.localstorage; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.WorkerThread; import android.app.appsearch.AppSearchResult; import android.app.appsearch.AppSearchSchema; @@ -29,6 +30,7 @@ import android.app.appsearch.SetSchemaResponse; import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; import android.os.Bundle; +import android.os.SystemClock; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -43,6 +45,7 @@ import com.android.server.appsearch.external.localstorage.converter.SearchResult import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter; import com.android.server.appsearch.external.localstorage.converter.SetSchemaResponseToProtoConverter; import com.android.server.appsearch.external.localstorage.converter.TypePropertyPathToProtoConverter; +import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; import com.google.android.icing.IcingSearchEngine; import com.google.android.icing.proto.DeleteByQueryResultProto; @@ -449,23 +452,65 @@ public final class AppSearchImpl implements Closeable { public void putDocument( @NonNull String packageName, @NonNull String databaseName, - @NonNull GenericDocument document) + @NonNull GenericDocument document, + @Nullable AppSearchLogger logger) throws AppSearchException { + PutDocumentStats.Builder pStatsBuilder = null; + if (logger != null) { + pStatsBuilder = new PutDocumentStats.Builder(packageName, databaseName); + } + long totalStartTimeMillis = SystemClock.elapsedRealtime(); + mReadWriteLock.writeLock().lock(); try { throwIfClosedLocked(); + // Generate Document Proto + long generateDocumentProtoStartTimeMillis = SystemClock.elapsedRealtime(); DocumentProto.Builder documentBuilder = GenericDocumentToProtoConverter.toDocumentProto(document).toBuilder(); + long generateDocumentProtoEndTimeMillis = SystemClock.elapsedRealtime(); + + // Rewrite Document Type + long rewriteDocumentTypeStartTimeMillis = SystemClock.elapsedRealtime(); String prefix = createPrefix(packageName, databaseName); addPrefixToDocument(documentBuilder, prefix); + long rewriteDocumentTypeEndTimeMillis = SystemClock.elapsedRealtime(); PutResultProto putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build()); addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace()); + // Logging stats + if (logger != null) { + pStatsBuilder + .getGeneralStatsBuilder() + .setStatusCode( + statusProtoToAppSearchException(putResultProto.getStatus()) + .getResultCode()); + pStatsBuilder + .setGenerateDocumentProtoLatencyMillis( + (int) + (generateDocumentProtoEndTimeMillis + - generateDocumentProtoStartTimeMillis)) + .setRewriteDocumentTypesLatencyMillis( + (int) + (rewriteDocumentTypeEndTimeMillis + - rewriteDocumentTypeStartTimeMillis)); + AppSearchLoggerHelper.copyNativeStats( + putResultProto.getPutDocumentStats(), pStatsBuilder); + } + checkSuccess(putResultProto.getStatus()); } finally { mReadWriteLock.writeLock().unlock(); + + if (logger != null) { + long totalEndTimeMillis = SystemClock.elapsedRealtime(); + pStatsBuilder + .getGeneralStatsBuilder() + .setTotalLatencyMillis((int) (totalEndTimeMillis - totalStartTimeMillis)); + logger.logStats(pStatsBuilder.build()); + } } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java new file mode 100644 index 000000000000..5680670629ae --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java @@ -0,0 +1,58 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appsearch.external.localstorage; + +import android.annotation.NonNull; + +import com.android.internal.util.Preconditions; +import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; + +import com.google.android.icing.proto.PutDocumentStatsProto; + +/** + * Class contains helper functions for logging. + * + * <p>E.g. we need to have helper functions to copy numbers from IcingLib to stats classes. + * + * @hide + */ +public final class AppSearchLoggerHelper { + private AppSearchLoggerHelper() {} + + /** + * Copies native stats to builder. + * + * @param fromNativeStats stats copied from + * @param toStatsBuilder stats copied to + */ + static void copyNativeStats( + @NonNull PutDocumentStatsProto fromNativeStats, + @NonNull PutDocumentStats.Builder toStatsBuilder) { + Preconditions.checkNotNull(fromNativeStats); + Preconditions.checkNotNull(toStatsBuilder); + toStatsBuilder + .setNativeLatencyMillis(fromNativeStats.getLatencyMs()) + .setNativeDocumentStoreLatencyMillis(fromNativeStats.getDocumentStoreLatencyMs()) + .setNativeIndexLatencyMillis(fromNativeStats.getIndexLatencyMs()) + .setNativeIndexMergeLatencyMillis(fromNativeStats.getIndexMergeLatencyMs()) + .setNativeDocumentSizeBytes(fromNativeStats.getDocumentSize()) + .setNativeNumTokensIndexed( + fromNativeStats.getTokenizationStats().getNumTokensIndexed()) + .setNativeExceededMaxNumTokens( + fromNativeStats.getTokenizationStats().getExceededMaxTokenNum()); + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java deleted file mode 100644 index 4b8ce6d2c1d1..000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage; - -import static android.app.appsearch.AppSearchResult.throwableToFailedResult; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchBatchResult; -import android.app.appsearch.AppSearchMigrationHelper; -import android.app.appsearch.GenericDocument; -import android.app.appsearch.SearchResultPage; -import android.app.appsearch.SearchSpec; -import android.app.appsearch.SetSchemaResponse; -import android.app.appsearch.exceptions.AppSearchException; -import android.os.Bundle; -import android.os.Parcel; - -import com.android.internal.util.Preconditions; - -import com.google.protobuf.CodedInputStream; -import com.google.protobuf.CodedOutputStream; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; - -/** - * An implementation of {@link AppSearchMigrationHelper} which query document and save post-migrated - * documents to locally in the app's storage space. - */ -class AppSearchMigrationHelperImpl implements AppSearchMigrationHelper { - private final AppSearchImpl mAppSearchImpl; - private final String mPackageName; - private final String mDatabaseName; - private final File mFile; - private final Map<String, Integer> mCurrentVersionMap; - private final Map<String, Integer> mFinalVersionMap; - - AppSearchMigrationHelperImpl( - @NonNull AppSearchImpl appSearchImpl, - @NonNull Map<String, Integer> currentVersionMap, - @NonNull Map<String, Integer> finalVersionMap, - @NonNull String packageName, - @NonNull String databaseName) - throws IOException { - mAppSearchImpl = Preconditions.checkNotNull(appSearchImpl); - mCurrentVersionMap = Preconditions.checkNotNull(currentVersionMap); - mFinalVersionMap = Preconditions.checkNotNull(finalVersionMap); - mPackageName = Preconditions.checkNotNull(packageName); - mDatabaseName = Preconditions.checkNotNull(databaseName); - mFile = File.createTempFile(/*prefix=*/ "appsearch", /*suffix=*/ null); - } - - @Override - public void queryAndTransform( - @NonNull String schemaType, @NonNull AppSearchMigrationHelper.Transformer migrator) - throws Exception { - Preconditions.checkState(mFile.exists(), "Internal temp file does not exist."); - int currentVersion = mCurrentVersionMap.get(schemaType); - int finalVersion = mFinalVersionMap.get(schemaType); - try (FileOutputStream outputStream = new FileOutputStream(mFile)) { - // TODO(b/151178558) change the output stream so that we can use it in platform - CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream); - SearchResultPage searchResultPage = - mAppSearchImpl.query( - mPackageName, - mDatabaseName, - /*queryExpression=*/ "", - new SearchSpec.Builder() - .addFilterSchemas(schemaType) - .setTermMatch(SearchSpec.TERM_MATCH_PREFIX) - .build()); - while (!searchResultPage.getResults().isEmpty()) { - for (int i = 0; i < searchResultPage.getResults().size(); i++) { - GenericDocument newDocument = - migrator.transform( - currentVersion, - finalVersion, - searchResultPage.getResults().get(i).getGenericDocument()); - Bundle bundle = newDocument.getBundle(); - Parcel parcel = Parcel.obtain(); - parcel.writeBundle(bundle); - byte[] serializedMessage = parcel.marshall(); - parcel.recycle(); - codedOutputStream.writeByteArrayNoTag(serializedMessage); - } - codedOutputStream.flush(); - searchResultPage = mAppSearchImpl.getNextPage(searchResultPage.getNextPageToken()); - outputStream.flush(); - } - } - } - - /** - * Reads {@link GenericDocument} from the temperate file and saves them to AppSearch. - * - * <p>This method should be only called once. - * - * @return the {@link AppSearchBatchResult} for migration documents. - */ - @NonNull - public SetSchemaResponse readAndPutDocuments(SetSchemaResponse.Builder responseBuilder) - throws IOException, AppSearchException { - Preconditions.checkState(mFile.exists(), "Internal temp file does not exist."); - try (InputStream inputStream = new FileInputStream(mFile)) { - CodedInputStream codedInputStream = CodedInputStream.newInstance(inputStream); - while (!codedInputStream.isAtEnd()) { - GenericDocument document = readDocumentFromInputStream(codedInputStream); - try { - mAppSearchImpl.putDocument(mPackageName, mDatabaseName, document); - } catch (Throwable t) { - responseBuilder.addMigrationFailure( - new SetSchemaResponse.MigrationFailure.Builder() - .setNamespace(document.getNamespace()) - .setSchemaType(document.getSchemaType()) - .setUri(document.getUri()) - .setAppSearchResult(throwableToFailedResult(t)) - .build()); - } - } - mAppSearchImpl.persistToDisk(); - return responseBuilder.build(); - } finally { - mFile.delete(); - } - } - - void deleteTempFile() { - mFile.delete(); - } - - /** - * Reads {@link GenericDocument} from given {@link CodedInputStream}. - * - * @param codedInputStream The codedInputStream to read from - * @throws IOException on File operation error. - */ - @NonNull - private static GenericDocument readDocumentFromInputStream( - @NonNull CodedInputStream codedInputStream) throws IOException { - byte[] serializedMessage = codedInputStream.readByteArray(); - - Parcel parcel = Parcel.obtain(); - parcel.unmarshall(serializedMessage, 0, serializedMessage.length); - parcel.setDataPosition(0); - Bundle bundle = parcel.readBundle(); - parcel.recycle(); - - return new GenericDocument(bundle); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java index 81a5067c9fa1..a724f95ad8fa 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java @@ -76,7 +76,7 @@ public class CallStats { CallStats(@NonNull Builder builder) { Preconditions.checkNotNull(builder); - mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStats); + mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStatsBuilder).build(); mCallType = builder.mCallType; mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis; mNumOperationsSucceeded = builder.mNumOperationsSucceeded; @@ -132,15 +132,23 @@ public class CallStats { /** Builder for {@link CallStats}. */ public static class Builder { - @NonNull final GeneralStats mGeneralStats; + @NonNull final GeneralStats.Builder mGeneralStatsBuilder; @CallType int mCallType; int mEstimatedBinderLatencyMillis; int mNumOperationsSucceeded; int mNumOperationsFailed; - /** Builder takes {@link GeneralStats} to hold general stats. */ - public Builder(@NonNull GeneralStats generalStats) { - mGeneralStats = Preconditions.checkNotNull(generalStats); + /** Builder takes {@link GeneralStats.Builder}. */ + public Builder(@NonNull String packageName, @NonNull String database) { + Preconditions.checkNotNull(packageName); + Preconditions.checkNotNull(database); + mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database); + } + + /** Returns {@link GeneralStats.Builder}. */ + @NonNull + public GeneralStats.Builder getGeneralStatsBuilder() { + return mGeneralStatsBuilder; } /** Sets type of the call. */ diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java index d2a45d5304f9..8ce8eda3d7a7 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java @@ -82,18 +82,18 @@ public final class GeneralStats { public static class Builder { @NonNull final String mPackageName; @NonNull final String mDatabase; - @AppSearchResult.ResultCode int mStatusCode; + @AppSearchResult.ResultCode int mStatusCode = AppSearchResult.RESULT_UNKNOWN_ERROR; int mTotalLatencyMillis; /** * Constructor * * @param packageName name of the package logging stats - * @param dataBase name of the database logging stats + * @param database name of the database logging stats */ - public Builder(@NonNull String packageName, @NonNull String dataBase) { + public Builder(@NonNull String packageName, @NonNull String database) { mPackageName = Preconditions.checkNotNull(packageName); - mDatabase = Preconditions.checkNotNull(dataBase); + mDatabase = Preconditions.checkNotNull(database); } /** Sets status code returned from {@link AppSearchResult#getResultCode()} */ diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java index b1b643b66859..c1f6fb118797 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java @@ -54,12 +54,14 @@ public final class PutDocumentStats { /** Number of tokens added to the index. */ private final int mNativeNumTokensIndexed; - /** Number of tokens clipped for exceeding the max number. */ - private final int mNativeNumTokensClipped; + /** + * Whether the number of tokens to be indexed exceeded the max number of tokens per document. + */ + private final boolean mNativeExceededMaxNumTokens; PutDocumentStats(@NonNull Builder builder) { Preconditions.checkNotNull(builder); - mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStats); + mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStatsBuilder).build(); mGenerateDocumentProtoLatencyMillis = builder.mGenerateDocumentProtoLatencyMillis; mRewriteDocumentTypesLatencyMillis = builder.mRewriteDocumentTypesLatencyMillis; mNativeLatencyMillis = builder.mNativeLatencyMillis; @@ -68,7 +70,7 @@ public final class PutDocumentStats { mNativeIndexMergeLatencyMillis = builder.mNativeIndexMergeLatencyMillis; mNativeDocumentSizeBytes = builder.mNativeDocumentSizeBytes; mNativeNumTokensIndexed = builder.mNativeNumTokensIndexed; - mNativeNumTokensClipped = builder.mNativeNumTokensClipped; + mNativeExceededMaxNumTokens = builder.mNativeExceededMaxNumTokens; } /** Returns the {@link GeneralStats} object attached to this instance. */ @@ -117,14 +119,17 @@ public final class PutDocumentStats { return mNativeNumTokensIndexed; } - /** Returns number of tokens clipped for exceeding the max number. */ - public int getNativeNumTokensClipped() { - return mNativeNumTokensClipped; + /** + * Returns whether the number of tokens to be indexed exceeded the max number of tokens per + * document. + */ + public boolean getNativeExceededMaxNumTokens() { + return mNativeExceededMaxNumTokens; } /** Builder for {@link PutDocumentStats}. */ public static class Builder { - @NonNull final GeneralStats mGeneralStats; + @NonNull final GeneralStats.Builder mGeneralStatsBuilder; int mGenerateDocumentProtoLatencyMillis; int mRewriteDocumentTypesLatencyMillis; int mNativeLatencyMillis; @@ -133,11 +138,19 @@ public final class PutDocumentStats { int mNativeIndexMergeLatencyMillis; int mNativeDocumentSizeBytes; int mNativeNumTokensIndexed; - int mNativeNumTokensClipped; + boolean mNativeExceededMaxNumTokens; - /** Builder takes {@link GeneralStats} to hold general stats. */ - public Builder(@NonNull GeneralStats generalStats) { - mGeneralStats = Preconditions.checkNotNull(generalStats); + /** Builder takes {@link GeneralStats.Builder}. */ + public Builder(@NonNull String packageName, @NonNull String database) { + Preconditions.checkNotNull(packageName); + Preconditions.checkNotNull(database); + mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database); + } + + /** Returns {@link GeneralStats.Builder}. */ + @NonNull + public GeneralStats.Builder getGeneralStatsBuilder() { + return mGeneralStatsBuilder; } /** Sets how much time we spend for generating document proto, in milliseconds. */ @@ -200,10 +213,13 @@ public final class PutDocumentStats { return this; } - /** Sets number of tokens clipped for exceeding the max number. */ + /** + * Sets whether the number of tokens to be indexed exceeded the max number of tokens per + * document. + */ @NonNull - public Builder setNativeNumTokensClipped(int nativeNumTokensClipped) { - mNativeNumTokensClipped = nativeNumTokensClipped; + public Builder setNativeExceededMaxNumTokens(boolean nativeExceededMaxNumTokens) { + mNativeExceededMaxNumTokens = nativeExceededMaxNumTokens; return this; } diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index 68531b6ee1e5..09522152cefb 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -I1926fb1d13628607f7a513c8149b65dd86c98dd6 +I723a9d7b5e64329ab25b6d7627f3b2d222c31ac7 diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 2f3ac225a190..784c63a6cf1f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -32,7 +32,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; -import android.app.ActivityManagerInternal; import android.app.AlarmManager; import android.app.AppGlobals; import android.app.IUidObserver; @@ -348,12 +347,20 @@ public final class QuotaController extends StateController { private final SparseBooleanArray mTempAllowlistCache = new SparseBooleanArray(); /** - * Mapping of app IDs to the when their temp allowlist grace period ends (in the elapsed + * Mapping of UIDs to the when their temp allowlist grace period ends (in the elapsed * realtime timebase). */ private final SparseLongArray mTempAllowlistGraceCache = new SparseLongArray(); - private final ActivityManagerInternal mActivityManagerInternal; + /** Current set of UIDs in the {@link ActivityManager#PROCESS_STATE_TOP} state. */ + private final SparseBooleanArray mTopAppCache = new SparseBooleanArray(); + + /** + * Mapping of UIDs to the when their top app grace period ends (in the elapsed realtime + * timebase). + */ + private final SparseLongArray mTopAppGraceCache = new SparseLongArray(); + private final AlarmManager mAlarmManager; private final ChargingTracker mChargeTracker; private final QcHandler mHandler; @@ -412,7 +419,7 @@ public final class QuotaController extends StateController { } }; - private final IUidObserver mUidObserver = new IUidObserver.Stub() { + private class QcUidObserver extends IUidObserver.Stub { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { mHandler.obtainMessage(MSG_UID_PROCESS_STATE_CHANGED, uid, procState).sendToTarget(); @@ -433,7 +440,7 @@ public final class QuotaController extends StateController { @Override public void onUidCachedChanged(int uid, boolean cached) { } - }; + } private final BroadcastReceiver mPackageAddedReceiver = new BroadcastReceiver() { @Override @@ -548,8 +555,10 @@ public final class QuotaController extends StateController { */ private long mEJRewardNotificationSeenMs = QcConstants.DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS; - private long mEJTempAllowlistGracePeriodMs = - QcConstants.DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS; + private long mEJGracePeriodTempAllowlistMs = + QcConstants.DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS; + + private long mEJGracePeriodTopAppMs = QcConstants.DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS; /** The package verifier app. */ @Nullable @@ -586,7 +595,6 @@ public final class QuotaController extends StateController { mHandler = new QcHandler(mContext.getMainLooper()); mChargeTracker = new ChargingTracker(); mChargeTracker.startTracking(); - mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mQcConstants = new QcConstants(); mBackgroundJobsController = backgroundJobsController; @@ -606,9 +614,12 @@ public final class QuotaController extends StateController { pai.registerTempAllowlistChangeListener(new TempAllowlistTracker()); try { - ActivityManager.getService().registerUidObserver(mUidObserver, + ActivityManager.getService().registerUidObserver(new QcUidObserver(), ActivityManager.UID_OBSERVER_PROCSTATE, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, null); + ActivityManager.getService().registerUidObserver(new QcUidObserver(), + ActivityManager.UID_OBSERVER_PROCSTATE, + ActivityManager.PROCESS_STATE_TOP, null); } catch (RemoteException e) { // ignored; both services live in system_server } @@ -658,7 +669,7 @@ public final class QuotaController extends StateController { } final int uid = jobStatus.getSourceUid(); - if (mActivityManagerInternal.getUidProcessState(uid) <= ActivityManager.PROCESS_STATE_TOP) { + if (mTopAppCache.get(uid)) { if (DEBUG) { Slog.d(TAG, jobStatus.toShortString() + " is top started job"); } @@ -715,6 +726,8 @@ public final class QuotaController extends StateController { mUidToPackageCache.remove(uid); mTempAllowlistCache.delete(uid); mTempAllowlistGraceCache.delete(uid); + mTopAppCache.delete(uid); + mTopAppGraceCache.delete(uid); } @Override @@ -770,14 +783,10 @@ public final class QuotaController extends StateController { /** Returns the maximum amount of time this job could run for. */ public long getMaxJobExecutionTimeMsLocked(@NonNull final JobStatus jobStatus) { - // Need to look at current proc state as well in the case where the job hasn't started yet. - final boolean isTop = mActivityManagerInternal - .getUidProcessState(jobStatus.getSourceUid()) <= ActivityManager.PROCESS_STATE_TOP; - if (!jobStatus.shouldTreatAsExpeditedJob()) { // If quota is currently "free", then the job can run for the full amount of time. if (mChargeTracker.isCharging() - || isTop + || mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) { return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; @@ -790,7 +799,7 @@ public final class QuotaController extends StateController { if (mChargeTracker.isCharging()) { return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; } - if (isTop || isTopStartedJobLocked(jobStatus)) { + if (mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus)) { return Math.max(mEJLimitsMs[ACTIVE_INDEX] / 2, getTimeUntilEJQuotaConsumedLocked( jobStatus.getSourceUserId(), jobStatus.getSourcePackageName())); @@ -817,14 +826,23 @@ public final class QuotaController extends StateController { if (isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) { return true; } + + final long nowElapsed = sElapsedRealtimeClock.millis(); final long tempAllowlistGracePeriodEndElapsed = mTempAllowlistGraceCache.get(jobStatus.getSourceUid()); final boolean hasTempAllowlistExemption = mTempAllowlistCache.get(jobStatus.getSourceUid()) - || sElapsedRealtimeClock.millis() < tempAllowlistGracePeriodEndElapsed; + || nowElapsed < tempAllowlistGracePeriodEndElapsed; if (hasTempAllowlistExemption) { return true; } + final long topAppGracePeriodEndElapsed = mTopAppGraceCache.get(jobStatus.getSourceUid()); + final boolean hasTopAppExemption = mTopAppCache.get(jobStatus.getSourceUid()) + || nowElapsed < topAppGracePeriodEndElapsed; + if (hasTopAppExemption) { + return true; + } + Timer ejTimer = mEJPkgTimers.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()); // Any already executing expedited jobs should be allowed to finish. @@ -2101,8 +2119,12 @@ public final class QuotaController extends StateController { final boolean hasTempAllowlistExemption = !mRegularJobTimer && (mTempAllowlistCache.get(mUid) || nowElapsed < tempAllowlistGracePeriodEndElapsed); + final long topAppGracePeriodEndElapsed = mTopAppGraceCache.get(mUid); + final boolean hasTopAppExemption = !mRegularJobTimer + && (mTopAppCache.get(mUid) || nowElapsed < topAppGracePeriodEndElapsed); return (standbyBucket == RESTRICTED_INDEX || !mChargeTracker.isCharging()) - && !mForegroundUids.get(mUid) && !hasTempAllowlistExemption; + && !mForegroundUids.get(mUid) && !hasTempAllowlistExemption + && !hasTopAppExemption; } void onStateChangedLocked(long nowElapsed, boolean isQuotaFree) { @@ -2421,11 +2443,11 @@ public final class QuotaController extends StateController { public void onAppRemoved(int uid) { synchronized (mLock) { final long nowElapsed = sElapsedRealtimeClock.millis(); - final long endElapsed = nowElapsed + mEJTempAllowlistGracePeriodMs; + final long endElapsed = nowElapsed + mEJGracePeriodTempAllowlistMs; mTempAllowlistCache.delete(uid); mTempAllowlistGraceCache.put(uid, endElapsed); Message msg = mHandler.obtainMessage(MSG_END_GRACE_PERIOD, uid, 0); - mHandler.sendMessageDelayed(msg, mEJTempAllowlistGracePeriodMs); + mHandler.sendMessageDelayed(msg, mEJGracePeriodTempAllowlistMs); } } } @@ -2571,12 +2593,37 @@ public final class QuotaController extends StateController { synchronized (mLock) { boolean isQuotaFree; - if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { + if (procState <= ActivityManager.PROCESS_STATE_TOP) { + mTopAppCache.put(uid, true); + mTopAppGraceCache.delete(uid); + if (mForegroundUids.get(uid)) { + // Went from FGS to TOP. We don't need to reprocess timers or + // jobs. + break; + } mForegroundUids.put(uid, true); isQuotaFree = true; } else { - mForegroundUids.delete(uid); - isQuotaFree = false; + final boolean reprocess; + if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { + reprocess = !mForegroundUids.get(uid); + mForegroundUids.put(uid, true); + isQuotaFree = true; + } else { + reprocess = true; + mForegroundUids.delete(uid); + isQuotaFree = false; + } + if (mTopAppCache.get(uid)) { + final long endElapsed = nowElapsed + mEJGracePeriodTopAppMs; + mTopAppCache.delete(uid); + mTopAppGraceCache.put(uid, endElapsed); + sendMessageDelayed(obtainMessage(MSG_END_GRACE_PERIOD, uid, 0), + mEJGracePeriodTopAppMs); + } + if (!reprocess) { + break; + } } // Update Timers first. if (mPkgTimers.indexOfKey(userId) >= 0 @@ -2644,20 +2691,31 @@ public final class QuotaController extends StateController { case MSG_END_GRACE_PERIOD: { final int uid = msg.arg1; synchronized (mLock) { - if (mTempAllowlistCache.get(uid)) { - // App added back to the temp allowlist during the grace period. + if (mTempAllowlistCache.get(uid) || mTopAppCache.get(uid)) { + // App added back to the temp allowlist or became top again + // during the grace period. if (DEBUG) { Slog.d(TAG, uid + " is still allowed"); } break; } + final long nowElapsed = sElapsedRealtimeClock.millis(); + if (nowElapsed < mTempAllowlistGraceCache.get(uid) + || nowElapsed < mTopAppGraceCache.get(uid)) { + // One of the grace periods is still in effect. + if (DEBUG) { + Slog.d(TAG, uid + " is still in grace period"); + } + break; + } if (DEBUG) { Slog.d(TAG, uid + " is now out of grace period"); } + mTempAllowlistGraceCache.delete(uid); + mTopAppGraceCache.delete(uid); final ArraySet<String> packages = getPackagesForUidLocked(uid); if (packages != null) { final int userId = UserHandle.getUserId(uid); - final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = packages.size() - 1; i >= 0; --i) { Timer t = mEJPkgTimers.get(userId, packages.valueAt(i)); if (t != null) { @@ -2989,8 +3047,11 @@ public final class QuotaController extends StateController { static final String KEY_EJ_REWARD_NOTIFICATION_SEEN_MS = QC_CONSTANT_PREFIX + "ej_reward_notification_seen_ms"; @VisibleForTesting - static final String KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS = - QC_CONSTANT_PREFIX + "ej_temp_allowlist_grace_period_ms"; + static final String KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = + QC_CONSTANT_PREFIX + "ej_grace_period_temp_allowlist_ms"; + @VisibleForTesting + static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS = + QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms"; private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS = 10 * 60 * 1000L; // 10 minutes @@ -3043,7 +3104,8 @@ public final class QuotaController extends StateController { private static final long DEFAULT_EJ_REWARD_TOP_APP_MS = 10 * SECOND_IN_MILLIS; private static final long DEFAULT_EJ_REWARD_INTERACTION_MS = 15 * SECOND_IN_MILLIS; private static final long DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS = 0; - private static final long DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS = 3 * MINUTE_IN_MILLIS; + private static final long DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = 3 * MINUTE_IN_MILLIS; + private static final long DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS = 1 * MINUTE_IN_MILLIS; /** How much time each app will have to run jobs within their standby bucket window. */ public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS; @@ -3275,7 +3337,12 @@ public final class QuotaController extends StateController { * How much additional grace period to add to the end of an app's temp allowlist * duration. */ - public long EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS = DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS; + public long EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS; + + /** + * How much additional grace period to give an app when it leaves the TOP state. + */ + public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS; public void processConstantLocked(@NonNull DeviceConfig.Properties properties, @NonNull String key) { @@ -3470,13 +3537,21 @@ public final class QuotaController extends StateController { mEJRewardNotificationSeenMs = Math.min(5 * MINUTE_IN_MILLIS, Math.max(0, EJ_REWARD_NOTIFICATION_SEEN_MS)); break; - case KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS: + case KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS: // We don't need to re-evaluate execution stats or constraint status for this. - EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS = - properties.getLong(key, DEFAULT_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS); + EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = + properties.getLong(key, DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS); // Limit grace period to be in the range [0 minutes, 1 hour]. - mEJTempAllowlistGracePeriodMs = Math.min(HOUR_IN_MILLIS, - Math.max(0, EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS)); + mEJGracePeriodTempAllowlistMs = Math.min(HOUR_IN_MILLIS, + Math.max(0, EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS)); + break; + case KEY_EJ_GRACE_PERIOD_TOP_APP_MS: + // We don't need to re-evaluate execution stats or constraint status for this. + EJ_GRACE_PERIOD_TOP_APP_MS = + properties.getLong(key, DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS); + // Limit grace period to be in the range [0 minutes, 1 hour]. + mEJGracePeriodTopAppMs = Math.min(HOUR_IN_MILLIS, + Math.max(0, EJ_GRACE_PERIOD_TOP_APP_MS)); break; } } @@ -3739,8 +3814,9 @@ public final class QuotaController extends StateController { pw.print(KEY_EJ_REWARD_TOP_APP_MS, EJ_REWARD_TOP_APP_MS).println(); pw.print(KEY_EJ_REWARD_INTERACTION_MS, EJ_REWARD_INTERACTION_MS).println(); pw.print(KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, EJ_REWARD_NOTIFICATION_SEEN_MS).println(); - pw.print(KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, - EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS).println(); + pw.print(KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, + EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS).println(); + pw.print(KEY_EJ_GRACE_PERIOD_TOP_APP_MS, EJ_GRACE_PERIOD_TOP_APP_MS).println(); pw.decreaseIndent(); } @@ -3853,6 +3929,16 @@ public final class QuotaController extends StateController { } @VisibleForTesting + long getEJGracePeriodTempAllowlistMs() { + return mEJGracePeriodTempAllowlistMs; + } + + @VisibleForTesting + long getEJGracePeriodTopAppMs() { + return mEJGracePeriodTopAppMs; + } + + @VisibleForTesting @NonNull long[] getEJLimitsMs() { return mEJLimitsMs; @@ -3888,11 +3974,6 @@ public final class QuotaController extends StateController { } @VisibleForTesting - long getEJTempAllowlistGracePeriodMs() { - return mEJTempAllowlistGracePeriodMs; - } - - @VisibleForTesting @Nullable List<TimingSession> getEJTimingSessions(int userId, String packageName) { return mEJTimingSessions.get(userId, packageName); @@ -3964,6 +4045,17 @@ public final class QuotaController extends StateController { pw.println(mForegroundUids.toString()); pw.println(); + pw.print("Cached top apps: "); + pw.println(mTopAppCache.toString()); + pw.print("Cached top app grace period: "); + pw.println(mTopAppGraceCache.toString()); + + pw.print("Cached temp allowlist: "); + pw.println(mTempAllowlistCache.toString()); + pw.print("Cached temp allowlist grace period: "); + pw.println(mTempAllowlistGraceCache.toString()); + pw.println(); + pw.println("Cached UID->package map:"); pw.increaseIndent(); for (int i = 0; i < mUidToPackageCache.size(); ++i) { @@ -3975,12 +4067,6 @@ public final class QuotaController extends StateController { pw.decreaseIndent(); pw.println(); - pw.print("Cached temp allowlist: "); - pw.println(mTempAllowlistCache.toString()); - pw.print("Cached temp allowlist grace period: "); - pw.println(mTempAllowlistGraceCache.toString()); - - pw.println(); mTrackedJobs.forEach((jobs) -> { for (int j = 0; j < jobs.size(); j++) { final JobStatus js = jobs.valueAt(j); diff --git a/core/api/current.txt b/core/api/current.txt index 18c049fe2141..8bc603d8513c 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1680,7 +1680,7 @@ package android { field public static final int windowSplashScreenAnimationDuration = 16844334; // 0x101062e field public static final int windowSplashScreenBackground = 16844332; // 0x101062c field public static final int windowSplashScreenBrandingImage = 16844335; // 0x101062f - field public static final int windowSplashscreenContent = 16844132; // 0x1010564 + field @Deprecated public static final int windowSplashscreenContent = 16844132; // 0x1010564 field @Deprecated public static final int windowSwipeToDismiss = 16843763; // 0x10103f3 field public static final int windowTitleBackgroundStyle = 16842844; // 0x101005c field public static final int windowTitleSize = 16842842; // 0x101005a @@ -6137,13 +6137,13 @@ package android.app { method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR; field public static final String DEFAULT_CHANNEL_ID = "miscellaneous"; - field public static final String EDIT_CONVERSATION = "convo"; + field public static final String EDIT_CONVERSATION = "conversation"; field public static final String EDIT_IMPORTANCE = "importance"; field public static final String EDIT_LAUNCHER = "launcher"; field public static final String EDIT_LOCKED_DEVICE = "locked"; field public static final String EDIT_SOUND = "sound"; field public static final String EDIT_VIBRATION = "vibration"; - field public static final String EDIT_ZEN = "dnd"; + field public static final String EDIT_ZEN = "zen"; } public final class NotificationChannelGroup implements android.os.Parcelable { @@ -18438,12 +18438,12 @@ package android.hardware.camera2 { } public class MultiResolutionImageReader implements java.lang.AutoCloseable { + ctor public MultiResolutionImageReader(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int, @IntRange(from=1) int); 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); } @@ -18554,10 +18554,10 @@ package android.hardware.camera2.params { } public class MultiResolutionStreamInfo { - ctor public MultiResolutionStreamInfo(int, int, @NonNull String); - method public int getHeight(); + ctor public MultiResolutionStreamInfo(@IntRange(from=1) int, @IntRange(from=1) int, @NonNull String); + method @IntRange(from=1) public int getHeight(); method @NonNull public String getPhysicalCameraId(); - method public int getWidth(); + method @IntRange(from=1) public int getWidth(); } public final class OisSample { @@ -26824,17 +26824,14 @@ package android.net.vcn { public final class VcnGatewayConnectionConfig { method @NonNull public int[] getExposedCapabilities(); method @IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) public int getMaxMtu(); - method @NonNull public int[] getRequiredUnderlyingCapabilities(); method @NonNull public long[] getRetryInterval(); } public static final class VcnGatewayConnectionConfig.Builder { ctor public VcnGatewayConnectionConfig.Builder(@NonNull android.net.vcn.VcnControlPlaneConfig); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int); - method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addRequiredUnderlyingCapability(int); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build(); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int); - method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeRequiredUnderlyingCapability(int); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) int); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryInterval(@NonNull long[]); } @@ -37401,12 +37398,15 @@ package android.service.autofill { method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender); method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String); method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation); + method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation); method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue); method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews); method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern); method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews); method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation); + method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation); method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation); + method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation); } public final class DateTransformation implements android.os.Parcelable android.service.autofill.Transformation { @@ -37507,6 +37507,7 @@ package android.service.autofill { method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long); method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews); method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation); + method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation, @Nullable android.service.autofill.InlinePresentation); method @NonNull public android.service.autofill.FillResponse.Builder setClientState(@Nullable android.os.Bundle); method @NonNull public android.service.autofill.FillResponse.Builder setFieldClassificationIds(@NonNull android.view.autofill.AutofillId...); method @NonNull public android.service.autofill.FillResponse.Builder setFlags(int); @@ -37534,6 +37535,7 @@ package android.service.autofill { public final class InlinePresentation implements android.os.Parcelable { ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.widget.inline.InlinePresentationSpec, boolean); + method @NonNull public static android.service.autofill.InlinePresentation createTooltipPresentation(@NonNull android.app.slice.Slice, @NonNull android.widget.inline.InlinePresentationSpec); method public int describeContents(); method @NonNull public android.widget.inline.InlinePresentationSpec getInlinePresentationSpec(); method @NonNull public android.app.slice.Slice getSlice(); @@ -51336,6 +51338,7 @@ package android.view.inputmethod { method @NonNull public android.os.Bundle getExtras(); method @NonNull public String getHostPackageName(); method @NonNull public java.util.List<android.widget.inline.InlinePresentationSpec> getInlinePresentationSpecs(); + method @Nullable public android.widget.inline.InlinePresentationSpec getInlineTooltipPresentationSpec(); method public int getMaxSuggestionCount(); method @NonNull public android.os.LocaleList getSupportedLocales(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -51347,9 +51350,12 @@ package android.view.inputmethod { ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addInlinePresentationSpecs(@NonNull android.widget.inline.InlinePresentationSpec); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build(); + method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setClientSupported(boolean); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>); + method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlineTooltipPresentationSpec(@NonNull android.widget.inline.InlinePresentationSpec); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int); + method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setServiceSupported(boolean); method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList); } @@ -52306,10 +52312,10 @@ package android.view.textservice { public final class TextServicesManager { method @Nullable public android.view.textservice.SpellCheckerInfo getCurrentSpellCheckerInfo(); - method @Nullable public java.util.List<android.view.textservice.SpellCheckerInfo> getEnabledSpellCheckerInfos(); + method @NonNull public java.util.List<android.view.textservice.SpellCheckerInfo> getEnabledSpellCheckerInfos(); method public boolean isSpellCheckerEnabled(); method @Nullable public android.view.textservice.SpellCheckerSession newSpellCheckerSession(@Nullable android.os.Bundle, @Nullable java.util.Locale, @NonNull android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean); - method @Nullable public android.view.textservice.SpellCheckerSession newSpellCheckerSession(@Nullable android.os.Bundle, @Nullable java.util.Locale, @NonNull android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean, int); + method @Nullable public android.view.textservice.SpellCheckerSession newSpellCheckerSession(@Nullable java.util.Locale, boolean, int, @Nullable android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener); } } diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 51b6967609af..e3b7c8898039 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -61,6 +61,7 @@ package android.content { public abstract class Context { method @NonNull public android.os.UserHandle getUser(); + field public static final String TEST_NETWORK_SERVICE = "test_network"; } public class Intent implements java.lang.Cloneable android.os.Parcelable { @@ -191,6 +192,29 @@ package android.net { method public int getResourceId(); } + public class NetworkPolicyManager { + method @NonNull public static String blockedReasonsToString(int); + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getMultipathPreference(@NonNull android.net.Network); + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getRestrictBackgroundStatus(int); + method public static boolean isUidBlocked(int, boolean); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); + field public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 262144; // 0x40000 + field public static final int BLOCKED_METERED_REASON_DATA_SAVER = 65536; // 0x10000 + field public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 131072; // 0x20000 + field public static final int BLOCKED_REASON_APP_STANDBY = 4; // 0x4 + field public static final int BLOCKED_REASON_BATTERY_SAVER = 1; // 0x1 + field public static final int BLOCKED_REASON_DOZE = 2; // 0x2 + field public static final int BLOCKED_REASON_NONE = 0; // 0x0 + field public static final int BLOCKED_REASON_RESTRICTED_MODE = 8; // 0x8 + } + + public static interface NetworkPolicyManager.NetworkPolicyCallback { + method public default void onUidBlockedReasonChanged(int, int); + } + public final class NetworkStateSnapshot implements android.os.Parcelable { ctor public NetworkStateSnapshot(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @Nullable String, int); method public int describeContents(); @@ -234,6 +258,7 @@ package android.net { public class VpnManager { field @Deprecated public static final int TYPE_VPN_LEGACY = 3; // 0x3 field public static final int TYPE_VPN_NONE = -1; // 0xffffffff + field public static final int TYPE_VPN_OEM = 4; // 0x4 field public static final int TYPE_VPN_PLATFORM = 2; // 0x2 field public static final int TYPE_VPN_SERVICE = 1; // 0x1 } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2a99aaa94d2a..fe8566f50b29 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -136,6 +136,7 @@ package android { field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS"; field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING"; field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION"; + field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION"; field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS"; field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION"; field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS"; @@ -407,7 +408,7 @@ package android.app { method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public boolean startProfile(@NonNull android.os.UserHandle); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public boolean stopProfile(@NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean switchUser(@NonNull android.os.UserHandle); } public static interface ActivityManager.OnUidImportanceListener { @@ -1695,6 +1696,13 @@ package android.app.smartspace { package android.app.time { + public final class Capabilities { + field public static final int CAPABILITY_NOT_ALLOWED = 20; // 0x14 + field public static final int CAPABILITY_NOT_APPLICABLE = 30; // 0x1e + field public static final int CAPABILITY_NOT_SUPPORTED = 10; // 0xa + field public static final int CAPABILITY_POSSESSED = 40; // 0x28 + } + public final class TimeManager { method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void addTimeZoneDetectorListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.time.TimeManager.TimeZoneDetectorListener); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public android.app.time.TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig(); @@ -1711,10 +1719,6 @@ package android.app.time { method public int getConfigureAutoDetectionEnabledCapability(); method public int getConfigureGeoDetectionEnabledCapability(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field public static final int CAPABILITY_NOT_ALLOWED = 20; // 0x14 - field public static final int CAPABILITY_NOT_APPLICABLE = 30; // 0x1e - field public static final int CAPABILITY_NOT_SUPPORTED = 10; // 0xa - field public static final int CAPABILITY_POSSESSED = 40; // 0x28 field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneCapabilities> CREATOR; } @@ -3656,7 +3660,6 @@ package android.hardware.location { field public static final int RESULT_FAILED_BAD_PARAMS = 2; // 0x2 field public static final int RESULT_FAILED_BUSY = 4; // 0x4 field public static final int RESULT_FAILED_HAL_UNAVAILABLE = 8; // 0x8 - field public static final int RESULT_FAILED_PERMISSION_DENIED = 9; // 0x9 field public static final int RESULT_FAILED_SERVICE_INTERNAL_FAILURE = 7; // 0x7 field public static final int RESULT_FAILED_TIMEOUT = 6; // 0x6 field public static final int RESULT_FAILED_UNINITIALIZED = 3; // 0x3 @@ -5058,8 +5061,8 @@ package android.media { method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups(); - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes); - method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes); + method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes); method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); @@ -10355,7 +10358,7 @@ package android.service.voice { public class VoiceInteractionService extends android.app.Service { method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback); - method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, @Nullable android.os.Bundle, @Nullable android.os.SharedMemory, android.service.voice.AlwaysOnHotwordDetector.Callback); + method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, @Nullable android.os.Bundle, @Nullable android.os.SharedMemory, android.service.voice.AlwaysOnHotwordDetector.Callback); method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager(); } @@ -12041,7 +12044,7 @@ package android.telephony.data { method public int getPduSessionId(); method public int getProtocolType(); method public long getRetryDurationMillis(); - method @Nullable public android.telephony.data.SliceInfo getSliceInfo(); + method @Nullable public android.telephony.data.NetworkSliceInfo getSliceInfo(); method @Deprecated public int getSuggestedRetryTime(); method @NonNull public java.util.List<android.telephony.data.TrafficDescriptor> getTrafficDescriptors(); method public void writeToParcel(android.os.Parcel, int); @@ -12077,7 +12080,7 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int); method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long); - method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo); + method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.NetworkSliceInfo); method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setTrafficDescriptors(@NonNull java.util.List<android.telephony.data.TrafficDescriptor>); } @@ -12150,7 +12153,7 @@ package android.telephony.data { method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback); method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback); method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback); - method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @Nullable android.telephony.data.TrafficDescriptor, boolean, @NonNull android.telephony.data.DataServiceCallback); + method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.NetworkSliceInfo, @Nullable android.telephony.data.TrafficDescriptor, boolean, @NonNull android.telephony.data.DataServiceCallback); } public class DataServiceCallback { @@ -12180,28 +12183,14 @@ package android.telephony.data { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.EpsBearerQosSessionAttributes> CREATOR; } - public abstract class QualifiedNetworksService extends android.app.Service { - ctor public QualifiedNetworksService(); - method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int); - field public static final String QUALIFIED_NETWORKS_SERVICE_INTERFACE = "android.telephony.data.QualifiedNetworksService"; - } - - public abstract class QualifiedNetworksService.NetworkAvailabilityProvider implements java.lang.AutoCloseable { - ctor public QualifiedNetworksService.NetworkAvailabilityProvider(int); - method public abstract void close(); - method public final int getSlotIndex(); - method public void reportThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ThrottleStatus>); - method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>); - } - - public final class SliceInfo implements android.os.Parcelable { + public final class NetworkSliceInfo implements android.os.Parcelable { method public int describeContents(); - method @IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getMappedHplmnSliceDifferentiator(); + method @IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getMappedHplmnSliceDifferentiator(); method public int getMappedHplmnSliceServiceType(); - method @IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getSliceDifferentiator(); + method @IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getSliceDifferentiator(); method public int getSliceServiceType(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.SliceInfo> CREATOR; + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.NetworkSliceInfo> CREATOR; field public static final int MAX_SLICE_DIFFERENTIATOR = 16777214; // 0xfffffe field public static final int MIN_SLICE_DIFFERENTIATOR = -1; // 0xffffffff field public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; // 0xffffffff @@ -12211,13 +12200,27 @@ package android.telephony.data { field public static final int SLICE_SERVICE_TYPE_URLLC = 2; // 0x2 } - public static final class SliceInfo.Builder { - ctor public SliceInfo.Builder(); - method @NonNull public android.telephony.data.SliceInfo build(); - method @NonNull public android.telephony.data.SliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) int); - method @NonNull public android.telephony.data.SliceInfo.Builder setMappedHplmnSliceServiceType(int); - method @NonNull public android.telephony.data.SliceInfo.Builder setSliceDifferentiator(@IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) int); - method @NonNull public android.telephony.data.SliceInfo.Builder setSliceServiceType(int); + public static final class NetworkSliceInfo.Builder { + ctor public NetworkSliceInfo.Builder(); + method @NonNull public android.telephony.data.NetworkSliceInfo build(); + method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) int); + method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceServiceType(int); + method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceDifferentiator(@IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) int); + method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceServiceType(int); + } + + public abstract class QualifiedNetworksService extends android.app.Service { + ctor public QualifiedNetworksService(); + method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int); + field public static final String QUALIFIED_NETWORKS_SERVICE_INTERFACE = "android.telephony.data.QualifiedNetworksService"; + } + + public abstract class QualifiedNetworksService.NetworkAvailabilityProvider implements java.lang.AutoCloseable { + ctor public QualifiedNetworksService.NetworkAvailabilityProvider(int); + method public abstract void close(); + method public final int getSlotIndex(); + method public void reportThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ThrottleStatus>); + method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>); } public final class ThrottleStatus implements android.os.Parcelable { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index e486fa2c2e29..8497427b68a1 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -104,7 +104,9 @@ package android.app { method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors(); method public static void resumeAppSwitches() throws android.os.RemoteException; method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int); + method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean stopUser(int, boolean); method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String); + method @RequiresPermission(android.Manifest.permission.DUMP) public void waitForBroadcastIdle(); field public static final long DROP_CLOSE_SYSTEM_DIALOGS = 174664120L; // 0xa6929b8L field public static final long LOCK_DOWN_CLOSE_SYSTEM_DIALOGS = 174664365L; // 0xa692aadL field public static final int PROCESS_CAPABILITY_ALL = 15; // 0xf @@ -427,6 +429,7 @@ package android.app.admin { method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int); method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int); field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED"; + field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED"; field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6 field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd @@ -709,6 +712,10 @@ package android.content { method public int getDisplayId(); } + public class Intent implements java.lang.Cloneable android.os.Parcelable { + field public static final String ACTION_USER_STOPPED = "android.intent.action.USER_STOPPED"; + } + public class SyncAdapterType implements android.os.Parcelable { method @Nullable public String getPackageName(); } @@ -1329,6 +1336,7 @@ package android.media { public class AudioManager { method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int); method public boolean hasRegisteredDynamicPolicy(); + method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice(); } public static final class AudioRecord.MetricsConstants { @@ -1480,6 +1488,8 @@ package android.net { public class NetworkPolicyManager { method public boolean getRestrictBackground(); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int); method @NonNull public static String resolveNetworkId(@NonNull android.net.wifi.WifiConfiguration); method public void setRestrictBackground(boolean); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f5f0b422a69d..86e2723a4dc8 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1628,6 +1628,18 @@ public class Activity extends ContextThemeWrapper } /** + * Clear the splash screen view if exist. + * @hide + */ + public void detachSplashScreenView() { + synchronized (this) { + if (mSplashScreenView != null) { + mSplashScreenView = null; + } + } + } + + /** * Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with * the attribute {@link android.R.attr#persistableMode} set to * <code>persistAcrossReboots</code>. diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index f905ec86aab7..3fedda36de2d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -31,6 +31,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.annotation.UserIdInt; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; @@ -4048,7 +4049,8 @@ public class ActivityManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS}) public boolean switchUser(@NonNull UserHandle user) { if (user == null) { throw new IllegalArgumentException("UserHandle cannot be null."); @@ -4144,6 +4146,25 @@ public class ActivityManager { } } + /** + * Stops the given {@code userId}. + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + public boolean stopUser(@UserIdInt int userId, boolean force) { + if (userId == UserHandle.USER_SYSTEM) { + return false; + } + try { + return USER_OP_SUCCESS == getService().stopUser( + userId, force, /* callback= */ null); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** {@hide} */ public static final int FLAG_OR_STOPPED = 1 << 0; /** {@hide} */ @@ -4811,6 +4832,21 @@ public class ActivityManager { } /** + * Blocks until all broadcast queues become idle. + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.DUMP) + public void waitForBroadcastIdle() { + try { + getService().waitForBroadcastIdle(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** * A subset of immutable pending intent information suitable for caching on the client side. * * @hide diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 837154fd6080..c1d8541311a2 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -292,7 +292,6 @@ public final class ActivityThread extends ClientTransactionHandler /** Use background GC policy and default JIT threshold. */ private static final int VM_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1; - private static final int REMOVE_SPLASH_SCREEN_VIEW_TIMEOUT = 5000; /** * Denotes an invalid sequence number corresponding to a process state change. */ @@ -1956,8 +1955,6 @@ public final class ActivityThread extends ClientTransactionHandler public static final int INSTRUMENT_WITHOUT_RESTART = 170; public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171; - public static final int REMOVE_SPLASH_SCREEN_VIEW = 172; - String codeToString(int code) { if (DEBUG_MESSAGES) { switch (code) { @@ -2006,8 +2003,6 @@ public final class ActivityThread extends ClientTransactionHandler case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART"; case FINISH_INSTRUMENTATION_WITHOUT_RESTART: return "FINISH_INSTRUMENTATION_WITHOUT_RESTART"; - case REMOVE_SPLASH_SCREEN_VIEW: - return "REMOVE_SPLASH_SCREEN_VIEW"; } } return Integer.toString(code); @@ -2204,9 +2199,6 @@ public final class ActivityThread extends ClientTransactionHandler case FINISH_INSTRUMENTATION_WITHOUT_RESTART: handleFinishInstrumentationWithoutRestart(); break; - case REMOVE_SPLASH_SCREEN_VIEW: - handleRemoveSplashScreenView((ActivityClientRecord) msg.obj); - break; } Object obj = msg.obj; if (obj instanceof SomeArgs) { @@ -4020,6 +4012,7 @@ public final class ActivityThread extends ClientTransactionHandler view.cacheRootWindow(r.window); view.makeSystemUIColorsTransparent(); r.activity.mSplashScreenView = view; + view.attachHostActivity(r.activity); view.requestLayout(); // Ensure splash screen view is shown before remove the splash screen window. final ViewRootImpl impl = decorView.getViewRootImpl(); @@ -4062,8 +4055,6 @@ public final class ActivityThread extends ClientTransactionHandler @Override public void handOverSplashScreenView(@NonNull ActivityClientRecord r) { if (r.activity.mSplashScreenView != null) { - Message msg = mH.obtainMessage(H.REMOVE_SPLASH_SCREEN_VIEW, r); - mH.sendMessageDelayed(msg, REMOVE_SPLASH_SCREEN_VIEW_TIMEOUT); synchronized (this) { if (mSplashScreenGlobal != null) { mSplashScreenGlobal.dispatchOnExitAnimation(r.token, @@ -4074,16 +4065,6 @@ public final class ActivityThread extends ClientTransactionHandler } /** - * Force remove splash screen view. - */ - private void handleRemoveSplashScreenView(@NonNull ActivityClientRecord r) { - if (r.activity.mSplashScreenView != null) { - r.activity.mSplashScreenView.remove(); - r.activity.mSplashScreenView = null; - } - } - - /** * Cycle activity through onPause and onUserLeaveHint so that PIP is entered if supported, then * return to its previous state. This allows activities that rely on onUserLeaveHint instead of * onPictureInPictureRequested to enter picture-in-picture. diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index ef0dcabbe111..4c2433c04771 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -709,4 +709,7 @@ interface IActivityManager { ParceledListSlice queryIntentComponentsForIntentSender(in IIntentSender sender, int matchFlags); int getUidProcessCapabilities(int uid, in String callingPackage); + + /** Blocks until all broadcast queues become idle. */ + void waitForBroadcastIdle(); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a3a8a5e6c651..9dcb23711a52 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -823,6 +823,17 @@ public class Notification implements Parcelable public static final int VISIBILITY_SECRET = -1; /** + * @hide + */ + @IntDef(prefix = "VISIBILITY_", value = { + VISIBILITY_PUBLIC, + VISIBILITY_PRIVATE, + VISIBILITY_SECRET, + NotificationManager.VISIBILITY_NO_OVERRIDE + }) + public @interface NotificationVisibilityOverride{}; + + /** * Notification category: incoming call (voice or video) or similar synchronous communication request. */ public static final String CATEGORY_CALL = "call"; diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 685c222539c6..6553b61ebbc2 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -108,13 +108,13 @@ public final class NotificationChannel implements Parcelable { * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields * that have to do with editing do not disturb bypass {(@link #setBypassDnd(boolean)}) . */ - public static final String EDIT_ZEN = "dnd"; + public static final String EDIT_ZEN = "zen"; /** * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields * that have to do with editing conversation settings (demoting or restoring a channel to * be a Conversation, changing bubble behavior, or setting the priority of a conversation). */ - public static final String EDIT_CONVERSATION = "convo"; + public static final String EDIT_CONVERSATION = "conversation"; /** * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields * that have to do with editing launcher behavior (showing badges)}. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 56aa9a2b580c..d4d3321d37a4 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1766,6 +1766,16 @@ public class DevicePolicyManager { "android.app.action.DATA_SHARING_RESTRICTION_APPLIED"; /** + * Broadcast action: notify that a value of {@link Settings.Global#DEVICE_POLICY_CONSTANTS} + * has been changed. + * @hide + */ + @TestApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = + "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED"; + + /** * Permission policy to prompt user for new permission requests for runtime permissions. * Already granted or denied permissions are not affected by this. */ @@ -2426,7 +2436,7 @@ public class DevicePolicyManager { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, prefix = {"PRIVATE_DNS_MODE_"}, value = { + @IntDef(prefix = {"PRIVATE_DNS_MODE_"}, value = { PRIVATE_DNS_MODE_UNKNOWN, PRIVATE_DNS_MODE_OFF, PRIVATE_DNS_MODE_OPPORTUNISTIC, @@ -6159,13 +6169,22 @@ public class DevicePolicyManager { // STOPSHIP(b/174298501): clarify the expected return value following generateKeyPair call. /** - * Called by a device or profile owner, or delegated certificate installer, to query whether a - * certificate and private key are installed under a given alias. + * This API can be called by the following to query whether a certificate and private key are + * installed under a given alias: + * <ul> + * <li>Device owner</li> + * <li>Profile owner</li> + * <li>Delegated certificate installer</li> + * <li>Credential management app</li> + * </ul> + * + * If called by the credential management app, the alias must exist in the credential + * management app's {@link android.security.AppUriAuthenticationPolicy}. * * @param alias The alias under which the key pair is installed. * @return {@code true} if a key pair with this alias exists, {@code false} otherwise. - * @throws SecurityException if the caller is not a device or profile owner or a delegated - * certificate installer. + * @throws SecurityException if the caller is not a device or profile owner, a delegated + * certificate installer or the credential management app. * @see #setDelegatedScopes * @see #DELEGATION_CERT_INSTALL */ diff --git a/core/java/android/app/time/Capabilities.java b/core/java/android/app/time/Capabilities.java new file mode 100644 index 000000000000..33db7211f3b4 --- /dev/null +++ b/core/java/android/app/time/Capabilities.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +import android.annotation.IntDef; +import android.annotation.SystemApi; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A capability is the ability for the user to configure something or perform an action. This + * information is exposed so that system apps like SettingsUI can be dynamic, rather than + * hard-coding knowledge of when configuration or actions are applicable / available to the user. + * + * <p>Capabilities have states that users cannot change directly. They may influence some + * capabilities indirectly by agreeing to certain device-wide behaviors such as location sharing, or + * by changing the configuration. See the {@code CAPABILITY_} constants for details. + * + * <p>Actions have associated methods, see the documentation for each action for details. + * + * <p>Note: Capabilities are independent of app permissions required to call the associated APIs. + * + * @hide + */ +@SystemApi +public final class Capabilities { + + /** @hide */ + @IntDef({ CAPABILITY_NOT_SUPPORTED, CAPABILITY_NOT_ALLOWED, CAPABILITY_NOT_APPLICABLE, + CAPABILITY_POSSESSED }) + @Retention(RetentionPolicy.SOURCE) + public @interface CapabilityState {} + + /** + * Indicates that a capability is not supported on this device, e.g. because of form factor or + * hardware. The associated UI should usually not be shown to the user. + */ + public static final int CAPABILITY_NOT_SUPPORTED = 10; + + /** + * Indicates that a capability is supported on this device, but not allowed for the user, e.g. + * if the capability relates to the ability to modify settings the user is not able to. + * This could be because of the user's type (e.g. maybe it applies to the primary user only) or + * device policy. Depending on the capability, this could mean the associated UI + * should be hidden, or displayed but disabled. + */ + public static final int CAPABILITY_NOT_ALLOWED = 20; + + /** + * Indicates that a capability is possessed but not currently applicable, e.g. if the + * capability relates to the ability to modify settings, the user has the ability to modify + * it, but it is currently rendered irrelevant by other settings or other device state (flags, + * resource config, etc.). The associated UI may be hidden, disabled, or left visible (but + * ineffective) depending on requirements. + */ + public static final int CAPABILITY_NOT_APPLICABLE = 30; + + /** Indicates that a capability is possessed by the user. */ + public static final int CAPABILITY_POSSESSED = 40; + + private Capabilities() {} + +} diff --git a/core/java/android/app/time/TimeCapabilities.aidl b/core/java/android/app/time/TimeCapabilities.aidl new file mode 100644 index 000000000000..f44b791e6ce7 --- /dev/null +++ b/core/java/android/app/time/TimeCapabilities.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +parcelable TimeCapabilities; diff --git a/core/java/android/app/time/TimeCapabilities.java b/core/java/android/app/time/TimeCapabilities.java new file mode 100644 index 000000000000..fff36c4a7096 --- /dev/null +++ b/core/java/android/app/time/TimeCapabilities.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +import android.annotation.NonNull; +import android.app.time.Capabilities.CapabilityState; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; + +import java.util.Objects; + +/** + * Time-relate capabilities for a user. + * + * <p>For configuration settings capabilities, the associated settings value can be found via + * {@link TimeManager#getTimeCapabilitiesAndConfig()} and may be changed using {@link + * TimeManager#updateTimeConfiguration(TimeConfiguration)} (if the user's capabilities + * allow). + * + * @hide + */ +public final class TimeCapabilities implements Parcelable { + + public static final @NonNull Creator<TimeCapabilities> CREATOR = + new Creator<TimeCapabilities>() { + public TimeCapabilities createFromParcel(Parcel in) { + return TimeCapabilities.createFromParcel(in); + } + + public TimeCapabilities[] newArray(int size) { + return new TimeCapabilities[size]; + } + }; + + + /** + * The user the capabilities are for. This is used for object equality and debugging but there + * is no accessor. + */ + @NonNull + private final UserHandle mUserHandle; + private final @CapabilityState int mConfigureAutoTimeDetectionEnabledCapability; + private final @CapabilityState int mSuggestTimeManuallyCapability; + + private TimeCapabilities(@NonNull Builder builder) { + this.mUserHandle = Objects.requireNonNull(builder.mUserHandle); + this.mConfigureAutoTimeDetectionEnabledCapability = + builder.mConfigureAutoDetectionEnabledCapability; + this.mSuggestTimeManuallyCapability = + builder.mSuggestTimeManuallyCapability; + } + + @NonNull + private static TimeCapabilities createFromParcel(Parcel in) { + UserHandle userHandle = UserHandle.readFromParcel(in); + return new TimeCapabilities.Builder(userHandle) + .setConfigureAutoTimeDetectionEnabledCapability(in.readInt()) + .setSuggestTimeManuallyCapability(in.readInt()) + .build(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + UserHandle.writeToParcel(mUserHandle, dest); + dest.writeInt(mConfigureAutoTimeDetectionEnabledCapability); + dest.writeInt(mSuggestTimeManuallyCapability); + } + + /** + * Returns the capability state associated with the user's ability to modify the automatic time + * detection setting. + */ + @CapabilityState + public int getConfigureAutoTimeDetectionEnabledCapability() { + return mConfigureAutoTimeDetectionEnabledCapability; + } + + /** + * Returns the capability state associated with the user's ability to manually set time on a + * device. + */ + @CapabilityState + public int getSuggestTimeManuallyCapability() { + return mSuggestTimeManuallyCapability; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TimeCapabilities that = (TimeCapabilities) o; + return mConfigureAutoTimeDetectionEnabledCapability + == that.mConfigureAutoTimeDetectionEnabledCapability + && mSuggestTimeManuallyCapability == that.mSuggestTimeManuallyCapability + && mUserHandle.equals(that.mUserHandle); + } + + @Override + public int hashCode() { + return Objects.hash(mUserHandle, mConfigureAutoTimeDetectionEnabledCapability, + mSuggestTimeManuallyCapability); + } + + @Override + public String toString() { + return "TimeCapabilities{" + + "mUserHandle=" + mUserHandle + + ", mConfigureAutoTimeDetectionEnabledCapability=" + + mConfigureAutoTimeDetectionEnabledCapability + + ", mSuggestTimeManuallyCapability=" + mSuggestTimeManuallyCapability + + '}'; + } + + /** + * A builder of {@link TimeCapabilities} objects. + * + * @hide + */ + public static class Builder { + @NonNull private final UserHandle mUserHandle; + private @CapabilityState int mConfigureAutoDetectionEnabledCapability; + private @CapabilityState int mSuggestTimeManuallyCapability; + + public Builder(@NonNull TimeCapabilities timeCapabilities) { + Objects.requireNonNull(timeCapabilities); + this.mUserHandle = timeCapabilities.mUserHandle; + this.mConfigureAutoDetectionEnabledCapability = + timeCapabilities.mConfigureAutoTimeDetectionEnabledCapability; + this.mSuggestTimeManuallyCapability = + timeCapabilities.mSuggestTimeManuallyCapability; + } + + public Builder(@NonNull UserHandle userHandle) { + this.mUserHandle = Objects.requireNonNull(userHandle); + } + + /** Sets the state for automatic time detection config. */ + public Builder setConfigureAutoTimeDetectionEnabledCapability( + @CapabilityState int setConfigureAutoTimeDetectionEnabledCapability) { + this.mConfigureAutoDetectionEnabledCapability = + setConfigureAutoTimeDetectionEnabledCapability; + return this; + } + + /** Sets the state for manual time change. */ + public Builder setSuggestTimeManuallyCapability( + @CapabilityState int suggestTimeManuallyCapability) { + this.mSuggestTimeManuallyCapability = suggestTimeManuallyCapability; + return this; + } + + /** Returns the {@link TimeCapabilities}. */ + public TimeCapabilities build() { + verifyCapabilitySet(mConfigureAutoDetectionEnabledCapability, + "configureAutoDetectionEnabledCapability"); + verifyCapabilitySet(mSuggestTimeManuallyCapability, "suggestTimeManuallyCapability"); + return new TimeCapabilities(this); + } + + private void verifyCapabilitySet(int value, String name) { + if (value == 0) { + throw new IllegalStateException(name + " was not set"); + } + } + } +} diff --git a/core/java/android/app/time/TimeCapabilitiesAndConfig.aidl b/core/java/android/app/time/TimeCapabilitiesAndConfig.aidl new file mode 100644 index 000000000000..183dcaf6e2e6 --- /dev/null +++ b/core/java/android/app/time/TimeCapabilitiesAndConfig.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +parcelable TimeCapabilitiesAndConfig;
\ No newline at end of file diff --git a/core/java/android/app/time/TimeCapabilitiesAndConfig.java b/core/java/android/app/time/TimeCapabilitiesAndConfig.java new file mode 100644 index 000000000000..4a1044760064 --- /dev/null +++ b/core/java/android/app/time/TimeCapabilitiesAndConfig.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * A pair containing a user's {@link TimeCapabilities} and {@link TimeConfiguration}. + * + * @hide + */ +public final class TimeCapabilitiesAndConfig implements Parcelable { + + public static final @NonNull Creator<TimeCapabilitiesAndConfig> CREATOR = + new Creator<TimeCapabilitiesAndConfig>() { + @Override + public TimeCapabilitiesAndConfig createFromParcel(Parcel source) { + return TimeCapabilitiesAndConfig.readFromParcel(source); + } + + @Override + public TimeCapabilitiesAndConfig[] newArray(int size) { + return new TimeCapabilitiesAndConfig[size]; + } + }; + + @NonNull + private final TimeCapabilities mTimeCapabilities; + + @NonNull + private final TimeConfiguration mTimeConfiguration; + + /** + * @hide + */ + public TimeCapabilitiesAndConfig(@NonNull TimeCapabilities timeCapabilities, + @NonNull TimeConfiguration timeConfiguration) { + mTimeCapabilities = Objects.requireNonNull(timeCapabilities); + mTimeConfiguration = Objects.requireNonNull(timeConfiguration); + } + + @NonNull + private static TimeCapabilitiesAndConfig readFromParcel(Parcel in) { + TimeCapabilities capabilities = in.readParcelable(null); + TimeConfiguration configuration = in.readParcelable(null); + return new TimeCapabilitiesAndConfig(capabilities, configuration); + } + + /** + * Returns the user's time behaviour capabilities. + * + * @hide + */ + @NonNull + public TimeCapabilities getTimeCapabilities() { + return mTimeCapabilities; + } + + /** + * Returns the user's time behaviour configuration. + * + * @hide + */ + @NonNull + public TimeConfiguration getTimeConfiguration() { + return mTimeConfiguration; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(mTimeCapabilities, flags); + dest.writeParcelable(mTimeConfiguration, flags); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TimeCapabilitiesAndConfig that = (TimeCapabilitiesAndConfig) o; + return mTimeCapabilities.equals(that.mTimeCapabilities) + && mTimeConfiguration.equals(that.mTimeConfiguration); + } + + @Override + public int hashCode() { + return Objects.hash(mTimeCapabilities, mTimeConfiguration); + } + + @Override + public String toString() { + return "TimeCapabilitiesAndConfig{" + + "mTimeCapabilities=" + mTimeCapabilities + + ", mTimeConfiguration=" + mTimeConfiguration + + '}'; + } +} diff --git a/core/java/android/app/time/TimeConfiguration.aidl b/core/java/android/app/time/TimeConfiguration.aidl new file mode 100644 index 000000000000..eb5bfd6d2272 --- /dev/null +++ b/core/java/android/app/time/TimeConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +parcelable TimeConfiguration; diff --git a/core/java/android/app/time/TimeConfiguration.java b/core/java/android/app/time/TimeConfiguration.java new file mode 100644 index 000000000000..70aede034d27 --- /dev/null +++ b/core/java/android/app/time/TimeConfiguration.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +import android.annotation.NonNull; +import android.annotation.StringDef; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * User visible settings that control the behavior of the time zone detector / manual time zone + * entry. + * + * @hide + */ +public final class TimeConfiguration implements Parcelable { + + public static final @NonNull Creator<TimeConfiguration> CREATOR = + new Creator<TimeConfiguration>() { + @Override + public TimeConfiguration createFromParcel(Parcel source) { + return TimeConfiguration.readFromParcel(source); + } + + @Override + public TimeConfiguration[] newArray(int size) { + return new TimeConfiguration[size]; + } + }; + + @StringDef(SETTING_AUTO_DETECTION_ENABLED) + @Retention(RetentionPolicy.SOURCE) + @interface Setting {} + + @Setting + private static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled"; + + @NonNull + private final Bundle mBundle; + + private TimeConfiguration(Builder builder) { + this.mBundle = builder.mBundle; + } + + /** + * Returns the value of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting. This + * controls whether a device will attempt to determine the time automatically using + * contextual information if the device supports auto detection. + */ + public boolean isAutoDetectionEnabled() { + return mBundle.getBoolean(SETTING_AUTO_DETECTION_ENABLED); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeBundle(mBundle); + } + + private static TimeConfiguration readFromParcel(Parcel in) { + return new TimeConfiguration.Builder() + .merge(in.readBundle()) + .build(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TimeConfiguration that = (TimeConfiguration) o; + return mBundle.kindofEquals(that.mBundle); + } + + @Override + public int hashCode() { + return Objects.hash(mBundle); + } + + @Override + public String toString() { + return "TimeConfiguration{" + + "mBundle=" + mBundle + + '}'; + } + + /** + * A builder for {@link TimeConfiguration} objects. + * + * @hide + */ + public static final class Builder { + private final Bundle mBundle = new Bundle(); + + public Builder() {} + + public Builder(@NonNull TimeConfiguration configuration) { + mBundle.putAll(configuration.mBundle); + } + + /** Sets whether auto detection is enabled or not. */ + @NonNull + public Builder setAutoDetectionEnabled(boolean enabled) { + mBundle.putBoolean(SETTING_AUTO_DETECTION_ENABLED, enabled); + return this; + } + + Builder merge(@NonNull Bundle bundle) { + mBundle.putAll(bundle); + return this; + } + + /** Returns {@link TimeConfiguration} object. */ + @NonNull + public TimeConfiguration build() { + return new TimeConfiguration(this); + } + } +} diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java index 430960fb11a8..c8fa5c8f28e2 100644 --- a/core/java/android/app/time/TimeManager.java +++ b/core/java/android/app/time/TimeManager.java @@ -75,7 +75,7 @@ public final class TimeManager { @NonNull public TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig() { if (DEBUG) { - Log.d(TAG, "getTimeZoneCapabilities called"); + Log.d(TAG, "getTimeZoneCapabilitiesAndConfig called"); } try { return mITimeZoneDetectorService.getCapabilitiesAndConfig(); @@ -85,6 +85,44 @@ public final class TimeManager { } /** + * Returns the calling user's time capabilities and configuration. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + @NonNull + public TimeCapabilitiesAndConfig getTimeCapabilitiesAndConfig() { + if (DEBUG) { + Log.d(TAG, "getTimeCapabilitiesAndConfig called"); + } + try { + return mITimeDetectorService.getCapabilitiesAndConfig(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Modifies the time detection configuration. + * + * @return {@code true} if all the configuration settings specified have been set to the + * new values, {@code false} if none have + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + public boolean updateTimeConfiguration(@NonNull TimeConfiguration configuration) { + if (DEBUG) { + Log.d(TAG, "updateTimeConfiguration called: " + configuration); + } + try { + return mITimeDetectorService.updateConfiguration(configuration); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Modifies the time zone detection configuration. * * <p>Configuration settings vary in scope: some may be global (affect all users), others may be @@ -97,11 +135,11 @@ public final class TimeManager { * capabilities. * * <p>Attempts to modify configuration settings with capabilities that are {@link - * TimeZoneCapabilities#CAPABILITY_NOT_SUPPORTED} or {@link - * TimeZoneCapabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false} + * Capabilities#CAPABILITY_NOT_SUPPORTED} or {@link + * Capabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false} * will be returned. Modifying configuration settings with capabilities that are {@link - * TimeZoneCapabilities#CAPABILITY_NOT_APPLICABLE} or {@link - * TimeZoneCapabilities#CAPABILITY_POSSESSED} will succeed. See {@link + * Capabilities#CAPABILITY_NOT_APPLICABLE} or {@link + * Capabilities#CAPABILITY_POSSESSED} will succeed. See {@link * TimeZoneCapabilities} for further details. * * <p>If the supplied configuration only has some values set, then only the specified settings diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java index a27be96e6e69..433b4200eece 100644 --- a/core/java/android/app/time/TimeZoneCapabilities.java +++ b/core/java/android/app/time/TimeZoneCapabilities.java @@ -16,77 +16,33 @@ package android.app.time; -import android.annotation.IntDef; +import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.app.time.Capabilities.CapabilityState; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TimeZoneDetector; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** - * Time zone-related capabilities for a user. A capability is the ability for the user to configure - * something or perform an action. This information is exposed so that system apps like SettingsUI - * can be dynamic, rather than hard-coding knowledge of when configuration or actions are applicable - * / available to the user. - * - * <p>Capabilities have states that users cannot change directly. They may influence some - * capabilities indirectly by agreeing to certain device-wide behaviors such as location sharing, or - * by changing the configuration. See the {@code CAPABILITY_} constants for details. - * - * <p>Actions have associated methods, see the documentation for each action for details. + * Time zone-related capabilities for a user. * * <p>For configuration settings capabilities, the associated settings value can be found via * {@link TimeManager#getTimeZoneCapabilitiesAndConfig()} and may be changed using {@link * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)} (if the user's capabilities * allow). * - * <p>Note: Capabilities are independent of app permissions required to call the associated APIs. - * * @hide */ @SystemApi public final class TimeZoneCapabilities implements Parcelable { - /** @hide */ - @IntDef({ CAPABILITY_NOT_SUPPORTED, CAPABILITY_NOT_ALLOWED, CAPABILITY_NOT_APPLICABLE, - CAPABILITY_POSSESSED }) - @Retention(RetentionPolicy.SOURCE) - public @interface CapabilityState {} - - /** - * Indicates that a capability is not supported on this device, e.g. because of form factor or - * hardware. The associated UI should usually not be shown to the user. - */ - public static final int CAPABILITY_NOT_SUPPORTED = 10; - - /** - * Indicates that a capability is supported on this device, but not allowed for the user, e.g. - * if the capability relates to the ability to modify settings the user is not able to. - * This could be because of the user's type (e.g. maybe it applies to the primary user only) or - * device policy. Depending on the capability, this could mean the associated UI - * should be hidden, or displayed but disabled. - */ - public static final int CAPABILITY_NOT_ALLOWED = 20; - - /** - * Indicates that a capability is possessed but not currently applicable, e.g. if the - * capability relates to the ability to modify settings, the user has the ability to modify - * it, but it is currently rendered irrelevant by other settings or other device state (flags, - * resource config, etc.). The associated UI may be hidden, disabled, or left visible (but - * ineffective) depending on requirements. - */ - public static final int CAPABILITY_NOT_APPLICABLE = 30; - - /** Indicates that a capability is possessed by the user. */ - public static final int CAPABILITY_POSSESSED = 40; - public static final @NonNull Creator<TimeZoneCapabilities> CREATOR = new Creator<TimeZoneCapabilities>() { public TimeZoneCapabilities createFromParcel(Parcel in) { @@ -159,7 +115,8 @@ public final class TimeZoneCapabilities implements Parcelable { * on a device via {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}. * * <p>The suggestion will be ignored in all cases unless the value is {@link - * #CAPABILITY_POSSESSED}. See also {@link TimeZoneConfiguration#isAutoDetectionEnabled()}. + * Capabilities#CAPABILITY_POSSESSED}. See also + * {@link TimeZoneConfiguration#isAutoDetectionEnabled()}. * * @hide */ diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java index f9a0c74312fc..a9ea76f77958 100644 --- a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java +++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java @@ -113,7 +113,7 @@ public final class TimeZoneCapabilitiesAndConfig implements Parcelable { @Override public String toString() { - return "TimeZoneDetectorCapabilitiesAndConfig{" + return "TimeZoneCapabilitiesAndConfig{" + "mCapabilities=" + mCapabilities + ", mConfiguration=" + mConfiguration + '}'; diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl index c4546be10601..9a6c33589123 100644 --- a/core/java/android/app/timedetector/ITimeDetectorService.aidl +++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl @@ -17,6 +17,8 @@ package android.app.timedetector; import android.app.time.ExternalTimeSuggestion; +import android.app.time.TimeCapabilitiesAndConfig; +import android.app.time.TimeConfiguration; import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; @@ -36,6 +38,9 @@ import android.app.timedetector.TelephonyTimeSuggestion; * {@hide} */ interface ITimeDetectorService { + TimeCapabilitiesAndConfig getCapabilitiesAndConfig(); + boolean updateConfiguration(in TimeConfiguration timeConfiguration); + void suggestExternalTime( in ExternalTimeSuggestion timeSuggestion); void suggestGnssTime(in GnssTimeSuggestion timeSuggestion); boolean suggestManualTime(in ManualTimeSuggestion timeSuggestion); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 190ef1adeb85..64ca92fa8132 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4219,7 +4219,8 @@ public abstract class Context { * @see #getSystemService(String) * @hide */ - @TestApi public static final String TEST_NETWORK_SERVICE = "test_network"; + @TestApi @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final String TEST_NETWORK_SERVICE = "test_network"; /** * Use with {@link #getSystemService(String)} to retrieve a {@link diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index adf9ff32c4ec..96b8fbe293f5 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -28,6 +28,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.AppGlobals; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo; @@ -3771,6 +3772,7 @@ public class Intent implements Parcelable, Cloneable { * has just been stopped (which is no longer running). * @hide */ + @TestApi public static final String ACTION_USER_STOPPED = "android.intent.action.USER_STOPPED"; diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index d7225ccd9ca6..439c6396f1d0 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ApplicationInfo; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; @@ -28,6 +29,7 @@ import android.os.Build.VERSION_CODES; import android.os.Parcel; import android.os.Parcelable; import android.util.DisplayMetrics; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.MotionEvent; import android.view.WindowManager; @@ -423,13 +425,38 @@ public class CompatibilityInfo implements Parcelable { } /** - * Translate an InsetsState in screen coordinates into the app window's coordinates. + * Translate an {@link InsetsState} in screen coordinates into the app window's coordinates. */ public void translateInsetsStateInScreenToAppWindow(InsetsState state) { state.scale(applicationInvertedScale); } /** + * Translate {@link InsetsSourceControl}s in screen coordinates into the app window's + * coordinates. + */ + public void translateSourceControlsInScreenToAppWindow(InsetsSourceControl[] controls) { + if (controls == null) { + return; + } + final float scale = applicationInvertedScale; + if (scale == 1f) { + return; + } + for (InsetsSourceControl control : controls) { + if (control == null) { + continue; + } + final Insets hint = control.getInsetsHint(); + control.setInsetsHint( + (int) (scale * hint.left), + (int) (scale * hint.top), + (int) (scale * hint.right), + (int) (scale * hint.bottom)); + } + } + + /** * Translate a Point in screen coordinates into the app window's coordinates. */ public void translatePointInScreenToAppWindow(PointF point) { diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index 1fdce5e773b1..acfad1354ccf 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -29,6 +29,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; +import android.os.IBinder; import android.os.RemoteException; import android.security.keystore.KeyProperties; import android.util.Slog; @@ -410,6 +411,36 @@ public class BiometricManager { } /** + * Requests all other biometric sensors to resetLockout. Note that this is a "time bound" + * See the {@link android.hardware.biometrics.fingerprint.ISession#resetLockout(int, + * HardwareAuthToken)} and {@link android.hardware.biometrics.face.ISession#resetLockout(int, + * HardwareAuthToken)} documentation for complete details. + * + * @param token A binder from the caller, for the service to linkToDeath + * @param opPackageName Caller's package name + * @param fromSensorId The originating sensor that just authenticated. Note that this MUST + * be a sensor that meets {@link Authenticators#BIOMETRIC_STRONG} strength. + * The strength will also be enforced on the BiometricService side. + * @param userId The user that authentication succeeded for, and also the user that resetLockout + * should be applied to. + * @param hardwareAuthToken A valid HAT generated upon successful biometric authentication. Note + * that it is not necessary for the HAT to contain a challenge. + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, + int userId, byte[] hardwareAuthToken) { + if (mService != null) { + try { + mService.resetLockoutTimeBound(token, opPackageName, fromSensorId, userId, + hardwareAuthToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** * Provides a localized string that may be used as the label for a button that invokes * {@link BiometricPrompt}. * diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl index 1472bb940be5..86df0994a222 100644 --- a/core/java/android/hardware/biometrics/IAuthService.aidl +++ b/core/java/android/hardware/biometrics/IAuthService.aidl @@ -69,6 +69,10 @@ interface IAuthService { // land as SIDs, and are used during key generation. long[] getAuthenticatorIds(); + // See documentation in BiometricManager. + void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, int userId, + in byte[] hardwareAuthToken); + // Provides a localized string that may be used as the label for a button that invokes // BiometricPrompt. CharSequence getButtonLabel(int userId, String opPackageName, int authenticators); diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl index 7639c5dd4d16..059bf2622b00 100644 --- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl +++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl @@ -70,4 +70,8 @@ interface IBiometricAuthenticator { // Gets the authenticator ID representing the current set of enrolled templates long getAuthenticatorId(int callingUserId); + + // Requests the sensor to reset its lockout state + void resetLockout(IBinder token, String opPackageName, int userId, + in byte[] hardwareAuthToken); } diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index 6d8bf0fb5543..64b51183a170 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -74,6 +74,10 @@ interface IBiometricService { // land as SIDs, and are used during key generation. long[] getAuthenticatorIds(int callingUserId); + // See documentation in BiometricManager. + void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, int userId, + in byte[] hardwareAuthToken); + int getCurrentStrength(int sensorId); // Returns a bit field of the modality (or modalities) that are will be used for authentication. diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java index c592f19bc45c..bb3d91dbc65c 100644 --- a/core/java/android/hardware/camera2/MultiResolutionImageReader.java +++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java @@ -131,18 +131,10 @@ public class MultiResolutionImageReader implements AutoCloseable { * @see * android.hardware.camera2.params.MultiResolutionStreamConfigurationMap */ - public static @NonNull MultiResolutionImageReader newInstance( + public MultiResolutionImageReader( @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; diff --git a/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java index aa1d1d4aaa18..e2e61ad4d31a 100644 --- a/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java +++ b/core/java/android/hardware/camera2/params/MultiResolutionStreamInfo.java @@ -17,6 +17,7 @@ package android.hardware.camera2.params; import android.annotation.NonNull; +import android.annotation.IntRange; import java.util.Objects; @@ -50,9 +51,22 @@ public class MultiResolutionStreamInfo { * MultiResolutionStreamConfigurationMap#getOutputInfo} or {@link * MultiResolutionStreamConfigurationMap#getInputInfo} to obtain them for a particular format * instead.</p> + * + * @param streamWidth The width in pixels of the camera stream + * @param streamHeight The height in pixels of the camera stream + * @param physicalCameraId The physical camera Id the camera stream is associated with + * @throws IllegalArgumentException if the streamWidth or streamHeight is invalid (either zero + * or negative). */ - public MultiResolutionStreamInfo(int streamWidth, int streamHeight, + public MultiResolutionStreamInfo(@IntRange(from = 1) int streamWidth, + @IntRange(from = 1) int streamHeight, @NonNull String physicalCameraId) { + if (streamWidth <= 0) { + throw new IllegalArgumentException("Invalid stream width " + streamWidth); + } + if (streamHeight <= 0) { + throw new IllegalArgumentException("Invalid stream height " + streamHeight); + } mStreamWidth = streamWidth; mStreamHeight = streamHeight; mPhysicalCameraId = physicalCameraId; @@ -61,14 +75,14 @@ public class MultiResolutionStreamInfo { /** * The width of this particular image buffer stream in pixels. */ - public int getWidth() { + public @IntRange(from = 1) int getWidth() { return mStreamWidth; } /** * The height of this particular image buffer stream in pixels. */ - public int getHeight() { + public @IntRange(from = 1) int getHeight() { return mStreamHeight; } diff --git a/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl new file mode 100644 index 000000000000..b79d6e0f9dfe --- /dev/null +++ b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl @@ -0,0 +1,47 @@ +/* + * 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.fingerprint; + +/** + * A listener for the high-brightness mode (HBM) transitions. This allows other components to + * perform certain actions when the HBM is toggled on or off. For example, a display manager + * implementation can subscribe to these events from UdfpsController and adjust the display's + * refresh rate when the HBM is enabled. + * + * @hide + */ +oneway interface IUdfpsHbmListener { + /** + * UdfpsController will call this method when the HBM is enabled. + * + * @param hbmType The type of HBM that was enabled. See + * {@link com.android.systemui.biometrics.HbmTypes}. + * @param displayId The displayId for which the HBM is enabled. See + * {@link android.view.Display#getDisplayId()}. + */ + void onHbmEnabled(int hbmType, int displayId); + + /** + * UdfpsController will call this method when the HBM is disabled. + * + * @param hbmType The type of HBM that was disabled. See + * {@link com.android.systemui.biometrics.HbmTypes}. + * @param displayId The displayId for which the HBM is disabled. See + * {@link android.view.Display#getDisplayId()}. + */ + void onHbmDisabled(int hbmType, int displayId); +} + diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java index 49beeb3a2e96..21ac71cb9163 100644 --- a/core/java/android/hardware/location/ContextHubClient.java +++ b/core/java/android/hardware/location/ContextHubClient.java @@ -146,6 +146,8 @@ public class ContextHubClient implements Closeable { * @return the result of sending the message defined as in ContextHubTransaction.Result * * @throws NullPointerException if NanoAppMessage is null + * @throws SecurityException if this client doesn't have permissions to send a message to the + * nanoapp. * * @see NanoAppMessage * @see ContextHubTransaction.Result diff --git a/core/java/android/hardware/location/ContextHubClientCallback.java b/core/java/android/hardware/location/ContextHubClientCallback.java index 7e484dda283c..35d00f03de67 100644 --- a/core/java/android/hardware/location/ContextHubClientCallback.java +++ b/core/java/android/hardware/location/ContextHubClientCallback.java @@ -117,11 +117,10 @@ public class ContextHubClientCallback { * 4) {@link ContextHubClient} performs any cleanup required with the nanoapp * 5) Callback invoked with the nanoapp ID and {@link ContextHubManager#AUTHORIZATION_DENIED}. * At this point, any further attempts of communication between the nanoapp and the - * {@link ContextHubClient} will be dropped by the contexthub and a return value of - * {@link ContextHubTransaction#RESULT_FAILED_PERMISSION_DENIED} will be used when calling - * {@link ContextHubClient#sendMessageToNanoApp}. The {@link ContextHubClient} should assume - * no communciation can happen again until {@link ContextHubManager#AUTHORIZATION_GRANTED} is - * received. + * {@link ContextHubClient} will be dropped by the contexthub and a security exception will + * be thrown when calling {@link ContextHubClient#sendMessageToNanoApp}. The + * {@link ContextHubClient} should assume no communciation can happen again until + * {@link ContextHubManager#AUTHORIZATION_GRANTED} is received. * * @param client the client that is associated with this callback * @param nanoAppId the ID of the nanoapp associated with the new diff --git a/core/java/android/hardware/location/ContextHubTransaction.java b/core/java/android/hardware/location/ContextHubTransaction.java index 86f77c0bf138..d11e0a9b6081 100644 --- a/core/java/android/hardware/location/ContextHubTransaction.java +++ b/core/java/android/hardware/location/ContextHubTransaction.java @@ -81,8 +81,7 @@ public class ContextHubTransaction<T> { RESULT_FAILED_AT_HUB, RESULT_FAILED_TIMEOUT, RESULT_FAILED_SERVICE_INTERNAL_FAILURE, - RESULT_FAILED_HAL_UNAVAILABLE, - RESULT_FAILED_PERMISSION_DENIED + RESULT_FAILED_HAL_UNAVAILABLE }) public @interface Result {} public static final int RESULT_SUCCESS = 0; @@ -118,11 +117,6 @@ public class ContextHubTransaction<T> { * Failure mode when the Context Hub HAL was not available. */ public static final int RESULT_FAILED_HAL_UNAVAILABLE = 8; - /** - * Failure mode when the user of the API doesn't have the required permissions to perform the - * operation. - */ - public static final int RESULT_FAILED_PERMISSION_DENIED = 9; /** * A class describing the response for a ContextHubTransaction. diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl index dfb1e996c55a..00c691379187 100644 --- a/core/java/android/net/INetworkPolicyListener.aidl +++ b/core/java/android/net/INetworkPolicyListener.aidl @@ -25,4 +25,5 @@ oneway interface INetworkPolicyListener { void onUidPoliciesChanged(int uid, int uidPolicies); void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes); void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans); + void onBlockedReasonChanged(int uid, int oldBlockedReason, int newBlockedReason); } diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 9bf791ba33e0..171c6a2c6a19 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -62,6 +62,7 @@ interface INetworkPolicyManager { 3 - enabled */ int getRestrictBackgroundByCaller(); + int getRestrictBackgroundStatus(int uid); void setDeviceIdleMode(boolean enabled); void setWifiMeteredOverride(String networkId, int meteredOverride); diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 664120698971..3da1227f1bac 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -23,6 +23,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.app.ActivityManager; @@ -44,6 +45,8 @@ import android.util.DebugUtils; import android.util.Pair; import android.util.Range; +import com.android.internal.util.function.pooled.PooledLambda; + import com.google.android.collect.Sets; import java.lang.annotation.Retention; @@ -53,6 +56,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; /** * Manager for creating and modifying network policy rules. @@ -60,6 +64,7 @@ import java.util.concurrent.ConcurrentHashMap; * @hide */ @TestApi +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @SystemService(Context.NETWORK_POLICY_SERVICE) public class NetworkPolicyManager { @@ -198,12 +203,157 @@ public class NetworkPolicyManager { }) public @interface SubscriptionOverrideMask {} + /** + * Flag to indicate that an app is not subject to any restrictions that could result in its + * network access blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_REASON_NONE = 0; + + /** + * Flag to indicate that an app is subject to Battery saver restrictions that would + * result in its network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_REASON_BATTERY_SAVER = 1 << 0; + + /** + * Flag to indicate that an app is subject to Doze restrictions that would + * result in its network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_REASON_DOZE = 1 << 1; + + /** + * Flag to indicate that an app is subject to App Standby restrictions that would + * result in its network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_REASON_APP_STANDBY = 1 << 2; + + /** + * Flag to indicate that an app is subject to Restricted mode restrictions that would + * result in its network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_REASON_RESTRICTED_MODE = 1 << 3; + + /** + * Flag to indicate that an app is subject to Data saver restrictions that would + * result in its metered network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_METERED_REASON_DATA_SAVER = 1 << 16; + + /** + * Flag to indicate that an app is subject to user restrictions that would + * result in its metered network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 1 << 17; + + /** + * Flag to indicate that an app is subject to Device admin restrictions that would + * result in its metered network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 1 << 18; + + /** @hide */ + public static final int BLOCKED_METERED_REASON_MASK = 0xffff0000; + + /** + * Flag to indicate that app is not exempt from any network restrictions. + * + * @hide + */ + public static final int ALLOWED_REASON_NONE = 0; + /** + * Flag to indicate that app is exempt from certain network restrictions because of it being a + * system component. + * + * @hide + */ + public static final int ALLOWED_REASON_SYSTEM = 1 << 0; + /** + * Flag to indicate that app is exempt from certain network restrictions because of it being + * in the foreground. + * + * @hide + */ + public static final int ALLOWED_REASON_FOREGROUND = 1 << 1; + /** + * Flag to indicate that app is exempt from certain network restrictions because of it being + * in the {@code allow-in-power-save} list. + * + * @hide + */ + public static final int ALLOWED_REASON_POWER_SAVE_ALLOWLIST = 1 << 2; + /** + * Flag to indicate that app is exempt from certain network restrictions because of it being + * in the {@code allow-in-power-save-except-idle} list. + * + * @hide + */ + public static final int ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST = 1 << 3; + /** + * Flag to indicate that app is exempt from certain network restrictions because of it holding + * certain privileged permissions. + * + * @hide + */ + public static final int ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS = 1 << 4; + /** + * Flag to indicate that app is exempt from certain metered network restrictions because user + * explicitly exempted it. + * + * @hide + */ + public static final int ALLOWED_METERED_REASON_USER_EXEMPTED = 1 << 16; + + /** @hide */ + public static final int ALLOWED_METERED_REASON_MASK = 0xffff0000; + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = {"BLOCKED_"}, value = { + BLOCKED_REASON_NONE, + BLOCKED_REASON_BATTERY_SAVER, + BLOCKED_REASON_DOZE, + BLOCKED_REASON_APP_STANDBY, + BLOCKED_REASON_RESTRICTED_MODE, + BLOCKED_METERED_REASON_DATA_SAVER, + BLOCKED_METERED_REASON_USER_RESTRICTED, + BLOCKED_METERED_REASON_ADMIN_DISABLED, + }) + public @interface BlockedReason {} + private final Context mContext; @UnsupportedAppUsage private INetworkPolicyManager mService; private final Map<SubscriptionCallback, SubscriptionCallbackProxy> - mCallbackMap = new ConcurrentHashMap<>(); + mSubscriptionCallbackMap = new ConcurrentHashMap<>(); + private final Map<NetworkPolicyCallback, NetworkPolicyCallbackProxy> + mNetworkPolicyCallbackMap = new ConcurrentHashMap<>(); /** @hide */ public NetworkPolicyManager(Context context, INetworkPolicyManager service) { @@ -318,7 +468,7 @@ public class NetworkPolicyManager { } final SubscriptionCallbackProxy callbackProxy = new SubscriptionCallbackProxy(callback); - if (null != mCallbackMap.putIfAbsent(callback, callbackProxy)) { + if (null != mSubscriptionCallbackMap.putIfAbsent(callback, callbackProxy)) { throw new IllegalArgumentException("Callback is already registered."); } registerListener(callbackProxy); @@ -331,7 +481,7 @@ public class NetworkPolicyManager { throw new NullPointerException("Callback cannot be null."); } - final SubscriptionCallbackProxy callbackProxy = mCallbackMap.remove(callback); + final SubscriptionCallbackProxy callbackProxy = mSubscriptionCallbackMap.remove(callback); if (callbackProxy == null) return; unregisterListener(callbackProxy); @@ -379,6 +529,26 @@ public class NetworkPolicyManager { } /** + * Determines if an UID is subject to metered network restrictions while running in background. + * + * @param uid The UID whose status needs to be checked. + * @return {@link ConnectivityManager#RESTRICT_BACKGROUND_STATUS_DISABLED}, + * {@link ConnectivityManager##RESTRICT_BACKGROUND_STATUS_ENABLED}, + * or {@link ConnectivityManager##RESTRICT_BACKGROUND_STATUS_WHITELISTED} to denote + * the current status of the UID. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) + public int getRestrictBackgroundStatus(int uid) { + try { + return mService.getRestrictBackgroundStatus(uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Override connections to be temporarily marked as either unmetered or congested, * along with automatic timeouts if desired. * @@ -460,9 +630,8 @@ public class NetworkPolicyManager { * @param meteredNetwork True if the network is metered. * @return true if networking is blocked for the given uid according to current networking * policies. - * - * @hide */ + @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork) { try { return mService.isUidNetworkingBlocked(uid, meteredNetwork); @@ -501,9 +670,8 @@ public class NetworkPolicyManager { * * @param uid The target uid. * @return true if the given uid is restricted from doing networking on metered networks. - * - * @hide */ + @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int uid) { try { return mService.isUidRestrictedOnMeteredNetworks(uid); @@ -513,11 +681,15 @@ public class NetworkPolicyManager { } /** - * Get multipath preference for the given network. + * Gets a hint on whether it is desirable to use multipath data transfer on the given network. + * + * @return One of the ConnectivityManager.MULTIPATH_PREFERENCE_* constants. * * @hide */ - public int getMultipathPreference(Network network) { + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) + public int getMultipathPreference(@NonNull Network network) { try { return mService.getMultipathPreference(network); } catch (RemoteException e) { @@ -689,6 +861,142 @@ public class NetworkPolicyManager { return WifiInfo.sanitizeSsid(ssid); } + /** + * Returns whether network access of an UID is blocked or not based on {@code blockedReasons} + * corresponding to it. + * + * {@code blockedReasons} would be a bitwise {@code OR} combination of the + * {@code BLOCKED_REASON_*} and/or {@code BLOCKED_METERED_REASON_*} constants. + * + * @param blockedReasons Value indicating the reasons for why the network access of an UID is + * blocked. If the value is equal to {@link #BLOCKED_REASON_NONE}, then + * it indicates that an app's network access is not blocked. + * @param meteredNetwork Value indicating whether the network is metered or not. + * @return Whether network access is blocked or not. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static boolean isUidBlocked(@BlockedReason int blockedReasons, boolean meteredNetwork) { + if (blockedReasons == BLOCKED_REASON_NONE) { + return false; + } + final int blockedOnAllNetworksReason = (blockedReasons & ~BLOCKED_METERED_REASON_MASK); + if (blockedOnAllNetworksReason != BLOCKED_REASON_NONE) { + return true; + } + if (meteredNetwork) { + return blockedReasons != BLOCKED_REASON_NONE; + } + return false; + } + + /** + * Returns the {@code string} representation of {@code blockedReasons} argument. + * + * @param blockedReasons Value indicating the reasons for why the network access of an UID is + * blocked. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @NonNull + public static String blockedReasonsToString(@BlockedReason int blockedReasons) { + return DebugUtils.flagsToString(NetworkPolicyManager.class, "BLOCKED_", blockedReasons); + } + + /** + * Register a {@link NetworkPolicyCallback} to listen for changes to network blocked status + * of apps. + * + * Note that when a caller tries to register a new callback, it might replace a previously + * registered callback if it is considered equal to the new one, based on the + * {@link Object#equals(Object)} check. + * + * @param executor The {@link Executor} to run the callback on. + * @param callback The {@link NetworkPolicyCallback} to be registered. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) + public void registerNetworkPolicyCallback(@Nullable Executor executor, + @NonNull NetworkPolicyCallback callback) { + if (callback == null) { + throw new NullPointerException("Callback cannot be null."); + } + + final NetworkPolicyCallbackProxy callbackProxy = new NetworkPolicyCallbackProxy( + executor, callback); + registerListener(callbackProxy); + mNetworkPolicyCallbackMap.put(callback, callbackProxy); + } + + /** + * Unregister a previously registered {@link NetworkPolicyCallback}. + * + * @param callback The {@link NetworkPolicyCallback} to be unregistered. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) + public void unregisterNetworkPolicyCallback(@NonNull NetworkPolicyCallback callback) { + if (callback == null) { + throw new NullPointerException("Callback cannot be null."); + } + + final NetworkPolicyCallbackProxy callbackProxy = mNetworkPolicyCallbackMap.remove(callback); + if (callbackProxy == null) return; + unregisterListener(callbackProxy); + } + + /** + * Interface for the callback to listen for changes to network blocked status of apps. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public interface NetworkPolicyCallback { + /** + * Called when the reason for why the network access of an UID is blocked changes. + * + * @param uid The UID for which the blocked status changed. + * @param blockedReasons Value indicating the reasons for why the network access of an + * UID is blocked. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + default void onUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {} + } + + /** @hide */ + public static class NetworkPolicyCallbackProxy extends Listener { + private final Executor mExecutor; + private final NetworkPolicyCallback mCallback; + + NetworkPolicyCallbackProxy(@Nullable Executor executor, + @NonNull NetworkPolicyCallback callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void onBlockedReasonChanged(int uid, @BlockedReason int oldBlockedReasons, + @BlockedReason int newBlockedReasons) { + if (oldBlockedReasons != newBlockedReasons) { + dispatchOnUidBlockedReasonChanged(mExecutor, mCallback, uid, newBlockedReasons); + } + } + } + + private static void dispatchOnUidBlockedReasonChanged(@Nullable Executor executor, + @NonNull NetworkPolicyCallback callback, int uid, @BlockedReason int blockedReasons) { + if (executor == null) { + callback.onUidBlockedReasonChanged(uid, blockedReasons); + } else { + executor.execute(PooledLambda.obtainRunnable( + NetworkPolicyCallback::onUidBlockedReasonChanged, + callback, uid, blockedReasons).recycleOnUse()); + } + } + /** @hide */ public static class SubscriptionCallback { /** @@ -743,5 +1051,7 @@ public class NetworkPolicyManager { @Override public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, int[] networkTypes) { } @Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { } + @Override public void onBlockedReasonChanged(int uid, + int oldBlockedReasons, int newBlockedReasons) { } } } diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java index 77754d1256a7..5f65d46f3b1e 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -86,13 +86,21 @@ public class VpnManager { public static final int TYPE_VPN_LEGACY = 3; /** + * An VPN created by OEM code through other means than {@link VpnService} or {@link VpnManager}. + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public static final int TYPE_VPN_OEM = 4; + + /** * Channel for VPN notifications. * @hide */ public static final String NOTIFICATION_CHANNEL_VPN = "VPN"; /** @hide */ - @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY}) + @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY, + TYPE_VPN_OEM}) @Retention(RetentionPolicy.SOURCE) public @interface VpnType {} diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index 9f83b21f0d0c..d4e8e2dca296 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -52,13 +52,12 @@ import java.util.concurrent.TimeUnit; * Network}s. * * <p>A VCN connection based on this configuration will be brought up dynamically based on device - * settings, and filed NetworkRequests. Underlying networks will be selected based on the services - * required by this configuration (as represented by network capabilities), and must be part of the - * subscription group under which this configuration is registered (see {@link + * settings, and filed NetworkRequests. Underlying Networks must provide INTERNET connectivity, and + * must be part of the subscription group under which this configuration is registered (see {@link * VcnManager#setVcnConfig}). * - * <p>As an abstraction of a cellular network, services that can be provided by a VCN network, or - * required for underlying networks are limited to services provided by cellular networks: + * <p>As an abstraction of a cellular network, services that can be provided by a VCN network are + * limited to services provided by cellular networks: * * <ul> * <li>{@link NetworkCapabilities#NET_CAPABILITY_MMS} @@ -214,13 +213,6 @@ public final class VcnGatewayConnectionConfig { checkValidCapability(cap); } - Preconditions.checkArgument( - mUnderlyingCapabilities != null && !mUnderlyingCapabilities.isEmpty(), - "underlyingCapabilities was null or empty"); - for (Integer cap : getAllUnderlyingCapabilities()) { - checkValidCapability(cap); - } - Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null"); validateRetryInterval(mRetryIntervalsMs); @@ -295,7 +287,9 @@ public final class VcnGatewayConnectionConfig { * * @see Builder#addRequiredUnderlyingCapability(int) * @see Builder#removeRequiredUnderlyingCapability(int) + * @hide */ + // TODO(b/182219992): Remove, and add when per-transport capabilities are supported @NonNull public int[] getRequiredUnderlyingCapabilities() { // Sorted set guarantees ordering @@ -470,7 +464,9 @@ public final class VcnGatewayConnectionConfig { * @return this {@link Builder} instance, for chaining * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying * networks + * @hide */ + // TODO(b/182219992): Remove, and add when per-transport capabilities are supported @NonNull public Builder addRequiredUnderlyingCapability( @VcnSupportedCapability int underlyingCapability) { @@ -492,7 +488,9 @@ public final class VcnGatewayConnectionConfig { * @return this {@link Builder} instance, for chaining * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying * networks + * @hide */ + // TODO(b/182219992): Remove, and add when per-transport capabilities are supported @NonNull @SuppressLint("BuilderSetStyle") // For consistency with NetCaps.Builder add/removeCap public Builder removeRequiredUnderlyingCapability( diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 062438c6e5db..b73fdbff8ef3 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -448,7 +448,7 @@ public class VcnManager { * @param networkCapabilities an array of NetworkCapabilities.NET_CAPABILITY_* capabilities * for the Gateway Connection that encountered the error, for identification purposes. * These will be a sorted list with no duplicates and will match {@link - * VcnGatewayConnectionConfig#getRequiredUnderlyingCapabilities()} for one of the {@link + * VcnGatewayConnectionConfig#getExposedCapabilities()} for one of the {@link * VcnGatewayConnectionConfig}s set in the {@link VcnConfig} for this subscription * group. * @param errorCode the code to indicate the error that occurred diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index ab1f688d60ca..bf859d45a440 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -229,6 +229,12 @@ public class Process { public static final int FSVERITY_CERT_UID = 1075; /** + * GID that gives access to USB OTG (unreliable) volumes on /mnt/media_rw/<vol name> + * @hide + */ + public static final int EXTERNAL_STORAGE_GID = 1077; + + /** * GID that gives write access to app-private data directories on external * storage (used on devices without sdcardfs only). * @hide diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java index 82c4c715f4b0..54905ec6eaeb 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -39,6 +39,17 @@ public abstract class StorageManagerInternal { public abstract int getExternalStorageMountMode(int uid, String packageName); /** + * Checks whether the {@code packageName} with {@code uid} has full external storage access via + * the {@link MANAGE_EXTERNAL_STORAGE} permission. + * + * @param uid the UID for which to check access. + * @param packageName the package in the UID for making the call. + * @return whether the {@code packageName} has full external storage access. + * Returns {@code true} if it has access, {@code false} otherwise. + */ + public abstract boolean hasExternalStorageAccess(int uid, String packageName); + + /** * A listener for reset events in the StorageManagerService. */ public interface ResetListener { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 5d139d971328..13f397912642 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4126,7 +4126,6 @@ public final class Settings { * unset or a match is not made, only the standard color modes will be restored. * @hide */ - @Readable public static final String DISPLAY_COLOR_MODE_VENDOR_HINT = "display_color_mode_vendor_hint"; @@ -6163,7 +6162,6 @@ public final class Settings { * * @hide */ - @Readable public static final String CAMERA_AUTOROTATE = "camera_autorotate"; /** @@ -6724,7 +6722,6 @@ public final class Settings { * android.app.timezonedetector.TimeZoneDetector#updateConfiguration} to update. * @hide */ - @Readable public static final String LOCATION_TIME_ZONE_DETECTION_ENABLED = "location_time_zone_detection_enabled"; @@ -7040,6 +7037,15 @@ public final class Settings { "enabled_accessibility_services"; /** + * List of the notified non-accessibility category accessibility services. + * + * @hide + */ + @Readable + public static final String NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES = + "notified_non_accessibility_category_services"; + + /** * List of the accessibility services to which the user has granted * permission to put the device into touch exploration mode. * @@ -7515,7 +7521,6 @@ public final class Settings { * * @hide */ - @Readable public static final String REDUCE_BRIGHT_COLORS_ACTIVATED = "reduce_bright_colors_activated"; @@ -7525,7 +7530,6 @@ public final class Settings { * * @hide */ - @Readable public static final String REDUCE_BRIGHT_COLORS_LEVEL = "reduce_bright_colors_level"; @@ -7534,7 +7538,6 @@ public final class Settings { * * @hide */ - @Readable public static final String REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS = "reduce_bright_colors_persist_across_reboots"; @@ -8245,7 +8248,6 @@ public final class Settings { * @see #MATCH_CONTENT_FRAMERATE_ALWAYS * @hide */ - @Readable public static final String MATCH_CONTENT_FRAME_RATE = "match_content_frame_rate"; @@ -8373,7 +8375,6 @@ public final class Settings { * {@link Display.STATE_OFF} and {@link Display.STATE_DOZE}. * @hide */ - @Readable public static final String DOZE_QUICK_PICKUP_GESTURE = "doze_quick_pickup_gesture"; /** @@ -8479,7 +8480,6 @@ public final class Settings { * For user preference if swipe bottom to expand notification gesture enabled. * @hide */ - @Readable public static final String SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED = "swipe_bottom_to_notification_enabled"; @@ -8487,28 +8487,24 @@ public final class Settings { * For user preference if One-Handed Mode enabled. * @hide */ - @Readable public static final String ONE_HANDED_MODE_ENABLED = "one_handed_mode_enabled"; /** * For user preference if One-Handed Mode timeout. * @hide */ - @Readable public static final String ONE_HANDED_MODE_TIMEOUT = "one_handed_mode_timeout"; /** * For user taps app to exit One-Handed Mode. * @hide */ - @Readable public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit"; /** * Internal use, one handed mode tutorial showed times. * @hide */ - @Readable public static final String ONE_HANDED_TUTORIAL_SHOW_COUNT = "one_handed_tutorial_show_count"; @@ -8534,7 +8530,6 @@ public final class Settings { * The last computed night mode bool the last time the phone was on * @hide */ - @Readable public static final String UI_NIGHT_MODE_LAST_COMPUTED = "ui_night_mode_last_computed"; /** @@ -8953,7 +8948,6 @@ public final class Settings { * * @hide */ - @Readable public static final String EMERGENCY_GESTURE_ENABLED = "emergency_gesture_enabled"; /** @@ -8961,7 +8955,6 @@ public final class Settings { * * @hide */ - @Readable public static final String EMERGENCY_GESTURE_SOUND_ENABLED = "emergency_gesture_sound_enabled"; @@ -9694,7 +9687,6 @@ public final class Settings { * @see Settings.Secure#MEDIA_CONTROLS_RESUME * @hide */ - @Readable public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked"; /** @@ -9742,7 +9734,6 @@ public final class Settings { * @hide */ @TestApi - @Readable public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY = "accessibility_magnification_capability"; @@ -9752,7 +9743,6 @@ public final class Settings { * * @hide */ - @Readable public static final String ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT = "accessibility_show_window_magnification_prompt"; @@ -9769,7 +9759,6 @@ public final class Settings { * @see #ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU * @hide */ - @Readable public static final String ACCESSIBILITY_BUTTON_MODE = "accessibility_button_mode"; @@ -9798,7 +9787,6 @@ public final class Settings { * * @hide */ - @Readable public static final String ACCESSIBILITY_FLOATING_MENU_SIZE = "accessibility_floating_menu_size"; @@ -9811,7 +9799,6 @@ public final class Settings { * * @hide */ - @Readable public static final String ACCESSIBILITY_FLOATING_MENU_ICON_TYPE = "accessibility_floating_menu_icon_type"; @@ -9820,7 +9807,6 @@ public final class Settings { * * @hide */ - @Readable public static final String ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED = "accessibility_floating_menu_fade_enabled"; @@ -9830,7 +9816,6 @@ public final class Settings { * * @hide */ - @Readable public static final String ACCESSIBILITY_FLOATING_MENU_OPACITY = "accessibility_floating_menu_opacity"; @@ -9839,7 +9824,6 @@ public final class Settings { * * @hide */ - @Readable public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled"; /** @@ -9863,7 +9847,6 @@ public final class Settings { * * @hide */ - @Readable public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS = "reminder_exp_learning_time_elapsed"; @@ -9872,7 +9855,6 @@ public final class Settings { * * @hide */ - @Readable public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT = "reminder_exp_learning_event_count"; @@ -10581,7 +10563,6 @@ public final class Settings { * * @hide */ - @Readable public static final String DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH = "wm_display_settings_path"; @@ -10761,7 +10742,6 @@ public final class Settings { * * @hide */ - @Readable public static final String HDMI_CONTROL_SEND_STANDBY_ON_SLEEP = "hdmi_control_send_standby_on_sleep"; @@ -14439,7 +14419,6 @@ public final class Settings { * * @hide */ - @Readable public static final String EUICC_SWITCH_SLOT_TIMEOUT_MILLIS = "euicc_switch_slot_timeout_millis"; @@ -14449,7 +14428,6 @@ public final class Settings { * * @hide */ - @Readable public static final String ENABLE_MULTI_SLOT_TIMEOUT_MILLIS = "enable_multi_slot_timeout_millis"; @@ -14573,7 +14551,6 @@ public final class Settings { * * @hide */ - @Readable public static final String FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT = "force_non_debuggable_final_build_for_compat"; @@ -14653,7 +14630,6 @@ public final class Settings { * * @hide */ - @Readable public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side"; /** @@ -15336,7 +15312,6 @@ public final class Settings { * The value 1 - enable, 0 - disable * @hide */ - @Readable public static final String NOTIFICATION_FEEDBACK_ENABLED = "notification_feedback_enabled"; /** @@ -15579,7 +15554,6 @@ public final class Settings { * * @hide */ - @Readable public static final String GNSS_SATELLITE_BLOCKLIST = "gnss_satellite_blocklist"; /** @@ -15784,7 +15758,6 @@ public final class Settings { * 1: Enabled * @hide */ - @Readable public static final String SHOW_PEOPLE_SPACE = "show_people_space"; /** @@ -15795,7 +15768,6 @@ public final class Settings { * 2: All conversations * @hide */ - @Readable public static final String PEOPLE_SPACE_CONVERSATION_TYPE = "people_space_conversation_type"; @@ -15806,7 +15778,6 @@ public final class Settings { * 1: Enabled * @hide */ - @Readable public static final String SHOW_NEW_NOTIF_DISMISS = "show_new_notif_dismiss"; /** @@ -15821,7 +15792,6 @@ public final class Settings { * 1: Enabled (All apps will receive the new rules) * @hide */ - @Readable public static final String BACKPORT_S_NOTIF_RULES = "backport_s_notif_rules"; /** @@ -15836,7 +15806,6 @@ public final class Settings { * <p>See {@link android.app.Notification.DevFlags} for more details. * @hide */ - @Readable public static final String FULLY_CUSTOM_VIEW_NOTIF_DECORATION = "fully_custom_view_notif_decoration"; @@ -15850,7 +15819,6 @@ public final class Settings { * <p>See {@link android.app.Notification.DevFlags} for more details. * @hide */ - @Readable public static final String DECORATED_CUSTOM_VIEW_NOTIF_DECORATION = "decorated_custom_view_notif_decoration"; @@ -15906,7 +15874,6 @@ public final class Settings { * 1: enabled * @hide */ - @Readable public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode"; } diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index e3d0741b4603..6147c58867a6 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -107,10 +107,12 @@ public final class Dataset implements Parcelable { private final ArrayList<AutofillValue> mFieldValues; private final ArrayList<RemoteViews> mFieldPresentations; private final ArrayList<InlinePresentation> mFieldInlinePresentations; + private final ArrayList<InlinePresentation> mFieldInlineTooltipPresentations; private final ArrayList<DatasetFieldFilter> mFieldFilters; @Nullable private final ClipData mFieldContent; private final RemoteViews mPresentation; @Nullable private final InlinePresentation mInlinePresentation; + @Nullable private final InlinePresentation mInlineTooltipPresentation; private final IntentSender mAuthentication; @Nullable String mId; @@ -119,10 +121,12 @@ public final class Dataset implements Parcelable { mFieldValues = builder.mFieldValues; mFieldPresentations = builder.mFieldPresentations; mFieldInlinePresentations = builder.mFieldInlinePresentations; + mFieldInlineTooltipPresentations = builder.mFieldInlineTooltipPresentations; mFieldFilters = builder.mFieldFilters; mFieldContent = builder.mFieldContent; mPresentation = builder.mPresentation; mInlinePresentation = builder.mInlinePresentation; + mInlineTooltipPresentation = builder.mInlineTooltipPresentation; mAuthentication = builder.mAuthentication; mId = builder.mId; } @@ -154,6 +158,14 @@ public final class Dataset implements Parcelable { } /** @hide */ + public @Nullable InlinePresentation getFieldInlineTooltipPresentation(int index) { + final InlinePresentation inlineTooltipPresentation = + mFieldInlineTooltipPresentations.get(index); + return inlineTooltipPresentation != null + ? inlineTooltipPresentation : mInlineTooltipPresentation; + } + + /** @hide */ public @Nullable DatasetFieldFilter getFilter(int index) { return mFieldFilters.get(index); } @@ -209,6 +221,10 @@ public final class Dataset implements Parcelable { if (mFieldInlinePresentations != null) { builder.append(", fieldInlinePresentations=").append(mFieldInlinePresentations.size()); } + if (mFieldInlineTooltipPresentations != null) { + builder.append(", fieldInlineTooltipInlinePresentations=").append( + mFieldInlineTooltipPresentations.size()); + } if (mFieldFilters != null) { builder.append(", fieldFilters=").append(mFieldFilters.size()); } @@ -218,6 +234,9 @@ public final class Dataset implements Parcelable { if (mInlinePresentation != null) { builder.append(", hasInlinePresentation"); } + if (mInlineTooltipPresentation != null) { + builder.append(", hasInlineTooltipPresentation"); + } if (mAuthentication != null) { builder.append(", hasAuthentication"); } @@ -245,10 +264,12 @@ public final class Dataset implements Parcelable { private ArrayList<AutofillValue> mFieldValues; private ArrayList<RemoteViews> mFieldPresentations; private ArrayList<InlinePresentation> mFieldInlinePresentations; + private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations; private ArrayList<DatasetFieldFilter> mFieldFilters; @Nullable private ClipData mFieldContent; private RemoteViews mPresentation; @Nullable private InlinePresentation mInlinePresentation; + @Nullable private InlinePresentation mInlineTooltipPresentation; private IntentSender mAuthentication; private boolean mDestroyed; @Nullable private String mId; @@ -306,6 +327,31 @@ public final class Dataset implements Parcelable { } /** + * Visualizes this dataset as inline suggestions. + * + * @param inlinePresentation the {@link InlinePresentation} used to visualize this + * dataset as inline suggestions. If the dataset supports inline suggestions this + * should not be null. + * @param inlineTooltipPresentation the {@link InlinePresentation} used to show + * the tooltip for the {@code inlinePresentation}. + * + * @throws IllegalStateException if {@link #build()} was already called. + * + * @return this builder. + */ + public @NonNull Builder setInlinePresentation( + @NonNull InlinePresentation inlinePresentation, + @NonNull InlinePresentation inlineTooltipPresentation) { + throwIfDestroyed(); + Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null"); + Preconditions.checkNotNull(inlineTooltipPresentation, + "inlineTooltipPresentation must be non-null"); + mInlinePresentation = inlinePresentation; + mInlineTooltipPresentation = inlineTooltipPresentation; + return this; + } + + /** * Triggers a custom UI before before autofilling the screen with the contents of this * dataset. * @@ -598,6 +644,41 @@ public final class Dataset implements Parcelable { /** * Sets the value of a field, using a custom {@link RemoteViews presentation} to + * visualize it and an {@link InlinePresentation} to visualize it as an inline suggestion. + * + * @see #setValue(AutofillId, AutofillValue, RemoteViews, InlinePresentation) + * + * @param id id returned by {@link + * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. + * @param value the value to be autofilled. Pass {@code null} if you do not have the value + * but the target view is a logical part of the dataset. For example, if + * the dataset needs authentication and you have no access to the value. + * @param presentation the presentation used to visualize this field. + * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset + * as inline suggestions. If the dataset supports inline suggestions, + * this should not be null. + * @param inlineTooltipPresentation The {@link InlinePresentation} used to show + * the tooltip for the {@code inlinePresentation}. + * + * @throws IllegalStateException if {@link #build()} was already called. + * + * @return this builder. + */ + public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value, + @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation, + @NonNull InlinePresentation inlineTooltipPresentation) { + throwIfDestroyed(); + Preconditions.checkNotNull(presentation, "presentation cannot be null"); + Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null"); + Preconditions.checkNotNull(inlineTooltipPresentation, + "inlineTooltipPresentation cannot be null"); + setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, + inlineTooltipPresentation, null); + return this; + } + + /** + * Sets the value of a field, using a custom {@link RemoteViews presentation} to * visualize it and a <a href="#Filtering">explicit filter</a>, and an * {@link InlinePresentation} to visualize it as an inline suggestion. * @@ -641,6 +722,47 @@ public final class Dataset implements Parcelable { } /** + * Sets the value of a field, using a custom {@link RemoteViews presentation} to + * visualize it and a <a href="#Filtering">explicit filter</a>, and an + * {@link InlinePresentation} to visualize it as an inline suggestion. + * + * @see #setValue(AutofillId, AutofillValue, Pattern, RemoteViews, InlinePresentation) + * + * @param id id returned by {@link + * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. + * @param value the value to be autofilled. Pass {@code null} if you do not have the value + * but the target view is a logical part of the dataset. For example, if + * the dataset needs authentication and you have no access to the value. + * @param filter regex used to determine if the dataset should be shown in the autofill UI; + * when {@code null}, it disables filtering on that dataset (this is the recommended + * approach when {@code value} is not {@code null} and field contains sensitive data + * such as passwords). + * @param presentation the presentation used to visualize this field. + * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset + * as inline suggestions. If the dataset supports inline suggestions, this + * should not be null. + * @param inlineTooltipPresentation The {@link InlinePresentation} used to show + * the tooltip for the {@code inlinePresentation}. + * + * @throws IllegalStateException if {@link #build()} was already called. + * + * @return this builder. + */ + public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value, + @Nullable Pattern filter, @NonNull RemoteViews presentation, + @NonNull InlinePresentation inlinePresentation, + @NonNull InlinePresentation inlineTooltipPresentation) { + throwIfDestroyed(); + Preconditions.checkNotNull(presentation, "presentation cannot be null"); + Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null"); + Preconditions.checkNotNull(inlineTooltipPresentation, + "inlineTooltipPresentation cannot be null"); + setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, + inlineTooltipPresentation, new DatasetFieldFilter(filter)); + return this; + } + + /** * Sets the value of a field with an <a href="#Filtering">explicit filter</a>, and using an * {@link InlinePresentation} to visualize it as an inline suggestion. * @@ -680,6 +802,15 @@ public final class Dataset implements Parcelable { @Nullable AutofillValue value, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable DatasetFieldFilter filter) { + setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null, + filter); + } + + private void setLifeTheUniverseAndEverything(@NonNull AutofillId id, + @Nullable AutofillValue value, @Nullable RemoteViews presentation, + @Nullable InlinePresentation inlinePresentation, + @Nullable InlinePresentation tooltip, + @Nullable DatasetFieldFilter filter) { Preconditions.checkNotNull(id, "id cannot be null"); if (mFieldIds != null) { final int existingIdx = mFieldIds.indexOf(id); @@ -687,6 +818,7 @@ public final class Dataset implements Parcelable { mFieldValues.set(existingIdx, value); mFieldPresentations.set(existingIdx, presentation); mFieldInlinePresentations.set(existingIdx, inlinePresentation); + mFieldInlineTooltipPresentations.set(existingIdx, tooltip); mFieldFilters.set(existingIdx, filter); return; } @@ -695,12 +827,14 @@ public final class Dataset implements Parcelable { mFieldValues = new ArrayList<>(); mFieldPresentations = new ArrayList<>(); mFieldInlinePresentations = new ArrayList<>(); + mFieldInlineTooltipPresentations = new ArrayList<>(); mFieldFilters = new ArrayList<>(); } mFieldIds.add(id); mFieldValues.add(value); mFieldPresentations.add(presentation); mFieldInlinePresentations.add(inlinePresentation); + mFieldInlineTooltipPresentations.add(tooltip); mFieldFilters.add(filter); } @@ -755,10 +889,12 @@ public final class Dataset implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeParcelable(mPresentation, flags); parcel.writeParcelable(mInlinePresentation, flags); + parcel.writeParcelable(mInlineTooltipPresentation, flags); parcel.writeTypedList(mFieldIds, flags); parcel.writeTypedList(mFieldValues, flags); parcel.writeTypedList(mFieldPresentations, flags); parcel.writeTypedList(mFieldInlinePresentations, flags); + parcel.writeTypedList(mFieldInlineTooltipPresentations, flags); parcel.writeTypedList(mFieldFilters, flags); parcel.writeParcelable(mFieldContent, flags); parcel.writeParcelable(mAuthentication, flags); @@ -770,6 +906,8 @@ public final class Dataset implements Parcelable { public Dataset createFromParcel(Parcel parcel) { final RemoteViews presentation = parcel.readParcelable(null); final InlinePresentation inlinePresentation = parcel.readParcelable(null); + final InlinePresentation inlineTooltipPresentation = + parcel.readParcelable(null); final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR); final ArrayList<AutofillValue> values = @@ -778,6 +916,8 @@ public final class Dataset implements Parcelable { parcel.createTypedArrayList(RemoteViews.CREATOR); final ArrayList<InlinePresentation> inlinePresentations = parcel.createTypedArrayList(InlinePresentation.CREATOR); + final ArrayList<InlinePresentation> inlineTooltipPresentations = + parcel.createTypedArrayList(InlinePresentation.CREATOR); final ArrayList<DatasetFieldFilter> filters = parcel.createTypedArrayList(DatasetFieldFilter.CREATOR); final ClipData fieldContent = parcel.readParcelable(null); @@ -790,8 +930,13 @@ public final class Dataset implements Parcelable { final Builder builder = (presentation != null) ? new Builder(presentation) : new Builder(); if (inlinePresentation != null) { - builder.setInlinePresentation(inlinePresentation); + if (inlineTooltipPresentation != null) { + builder.setInlinePresentation(inlinePresentation, inlineTooltipPresentation); + } else { + builder.setInlinePresentation(inlinePresentation); + } } + if (fieldContent != null) { builder.setContent(ids.get(0), fieldContent); } @@ -802,9 +947,11 @@ public final class Dataset implements Parcelable { final RemoteViews fieldPresentation = presentations.get(i); final InlinePresentation fieldInlinePresentation = i < inlinePresentationsSize ? inlinePresentations.get(i) : null; + final InlinePresentation fieldInlineTooltipPresentation = + i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null; final DatasetFieldFilter filter = filters.get(i); builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, - fieldInlinePresentation, filter); + fieldInlinePresentation, fieldInlineTooltipPresentation, filter); } builder.setAuthentication(authentication); builder.setId(datasetId); diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index b1107a8c2efb..740ae260999f 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -23,6 +23,7 @@ import static android.view.autofill.Helper.sDebug; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.TestApi; import android.app.Activity; import android.content.IntentSender; @@ -76,6 +77,7 @@ public final class FillResponse implements Parcelable { private final @Nullable Bundle mClientState; private final @Nullable RemoteViews mPresentation; private final @Nullable InlinePresentation mInlinePresentation; + private final @Nullable InlinePresentation mInlineTooltipPresentation; private final @Nullable RemoteViews mHeader; private final @Nullable RemoteViews mFooter; private final @Nullable IntentSender mAuthentication; @@ -95,6 +97,7 @@ public final class FillResponse implements Parcelable { mClientState = builder.mClientState; mPresentation = builder.mPresentation; mInlinePresentation = builder.mInlinePresentation; + mInlineTooltipPresentation = builder.mInlineTooltipPresentation; mHeader = builder.mHeader; mFooter = builder.mFooter; mAuthentication = builder.mAuthentication; @@ -135,6 +138,11 @@ public final class FillResponse implements Parcelable { } /** @hide */ + public @Nullable InlinePresentation getInlineTooltipPresentation() { + return mInlineTooltipPresentation; + } + + /** @hide */ public @Nullable RemoteViews getHeader() { return mHeader; } @@ -219,6 +227,7 @@ public final class FillResponse implements Parcelable { private Bundle mClientState; private RemoteViews mPresentation; private InlinePresentation mInlinePresentation; + private InlinePresentation mInlineTooltipPresentation; private RemoteViews mHeader; private RemoteViews mFooter; private IntentSender mAuthentication; @@ -360,6 +369,22 @@ public final class FillResponse implements Parcelable { public Builder setAuthentication(@NonNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation) { + return setAuthentication(ids, authentication, presentation, inlinePresentation, null); + } + + /** + * Triggers a custom UI before before autofilling the screen with any data set in this + * response. + * + * <p>This method like + * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews, InlinePresentation)} + * but allows setting an {@link InlinePresentation} for the inline suggestion tooltip. + */ + @NonNull + public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids, + @Nullable IntentSender authentication, @Nullable RemoteViews presentation, + @Nullable InlinePresentation inlinePresentation, + @Nullable InlinePresentation inlineTooltipPresentation) { throwIfDestroyed(); throwIfDisableAutofillCalled(); if (mHeader != null || mFooter != null) { @@ -373,6 +398,7 @@ public final class FillResponse implements Parcelable { mAuthentication = authentication; mPresentation = presentation; mInlinePresentation = inlinePresentation; + mInlineTooltipPresentation = inlineTooltipPresentation; mAuthenticationIds = assertValid(ids); return this; } @@ -737,6 +763,9 @@ public final class FillResponse implements Parcelable { if (mInlinePresentation != null) { builder.append(", hasInlinePresentation"); } + if (mInlineTooltipPresentation != null) { + builder.append(", hasInlineTooltipPresentation"); + } if (mHeader != null) { builder.append(", hasHeader"); } @@ -784,6 +813,7 @@ public final class FillResponse implements Parcelable { parcel.writeParcelable(mAuthentication, flags); parcel.writeParcelable(mPresentation, flags); parcel.writeParcelable(mInlinePresentation, flags); + parcel.writeParcelable(mInlineTooltipPresentation, flags); parcel.writeParcelable(mHeader, flags); parcel.writeParcelable(mFooter, flags); parcel.writeParcelable(mUserData, flags); @@ -818,9 +848,10 @@ public final class FillResponse implements Parcelable { final IntentSender authentication = parcel.readParcelable(null); final RemoteViews presentation = parcel.readParcelable(null); final InlinePresentation inlinePresentation = parcel.readParcelable(null); + final InlinePresentation inlineTooltipPresentation = parcel.readParcelable(null); if (authenticationIds != null) { builder.setAuthentication(authenticationIds, authentication, presentation, - inlinePresentation); + inlinePresentation, inlineTooltipPresentation); } final RemoteViews header = parcel.readParcelable(null); if (header != null) { diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java index fbf23b69addf..40349576c460 100644 --- a/core/java/android/service/autofill/InlinePresentation.java +++ b/core/java/android/service/autofill/InlinePresentation.java @@ -75,9 +75,23 @@ public final class InlinePresentation implements Parcelable { return hints.toArray(new String[hints.size()]); } + /** + * Creates a presentation for the inline suggestion tooltip + * + * @param slice Represents the UI content and the action for the inline suggestion tooltip. + * @param spec Specifies the UI specification for the inline suggestion tooltip. + * @return An {@link InlinePresentation} for the inline suggestion tooltip + */ + @NonNull + public static InlinePresentation createTooltipPresentation(@NonNull Slice slice, + @NonNull InlinePresentationSpec spec) { + return new InlinePresentation(slice, spec, /* pinned */ false); + + } + - // Code below generated by codegen v1.0.20. + // Code below generated by codegen v1.0.22. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -259,10 +273,10 @@ public final class InlinePresentation implements Parcelable { }; @DataClass.Generated( - time = 1604456277638L, - codegenVersion = "1.0.20", + time = 1615968415006L, + codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java", - inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)") + inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size java.lang.String[] getAutofillHints()\npublic static @android.annotation.NonNull android.service.autofill.InlinePresentation createTooltipPresentation(android.app.slice.Slice,android.widget.inline.InlinePresentationSpec)\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 7aa5bbc930fb..bf5f24d17f8a 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1786,7 +1786,8 @@ public abstract class NotificationListenerService extends Service { * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if * no such preference has been expressed. */ - public int getLockscreenVisibilityOverride() { + public @Notification.NotificationVisibilityOverride + int getLockscreenVisibilityOverride() { return mVisibilityOverride; } diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index 048d9f57aded..9ba39a1b37f7 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -353,6 +353,7 @@ public class VoiceInteractionService extends Service { * @hide */ @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION) @NonNull public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector( @SuppressLint("MissingNullability") String keyphrase, // TODO: nullability properly diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index a89c540eedaf..7a61df8178a2 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -29,6 +29,7 @@ import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static android.view.InsetsController.AnimationType; import static android.view.InsetsController.DEBUG; import static android.view.InsetsState.ISIDE_BOTTOM; +import static android.view.InsetsState.ISIDE_FLOATING; import static android.view.InsetsState.ISIDE_LEFT; import static android.view.InsetsState.ISIDE_RIGHT; import static android.view.InsetsState.ISIDE_TOP; @@ -74,8 +75,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll private final WindowInsetsAnimationControlListener mListener; private final SparseArray<InsetsSourceControl> mControls; - private final SparseIntArray mTypeSideMap = new SparseIntArray(); - private final SparseSetArray<InsetsSourceControl> mSideSourceMap = new SparseSetArray<>(); + private final SparseSetArray<InsetsSourceControl> mSideControlsMap = new SparseSetArray<>(); /** @see WindowInsetsAnimationController#getHiddenStateInsets */ private final Insets mHiddenInsets; @@ -104,8 +104,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll private Boolean mPerceptible; @VisibleForTesting - public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame, - InsetsState state, WindowInsetsAnimationControlListener listener, + public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, + @Nullable Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener, @InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator, @AnimationType int animationType, CompatibilityInfo.Translator translator) { @@ -114,19 +114,30 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll mTypes = types; mController = controller; mInitialInsetsState = new InsetsState(state, true /* copySources */); - mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */); - mPendingInsets = mCurrentInsets; - mHiddenInsets = calculateInsets(mInitialInsetsState, frame, controls, false /* shown */, - null /* typeSideMap */); - mShownInsets = calculateInsets(mInitialInsetsState, frame, controls, true /* shown */, - mTypeSideMap); - mHasZeroInsetsIme = mShownInsets.bottom == 0 && controlsInternalType(ITYPE_IME); - if (mHasZeroInsetsIme) { - // IME has shownInsets of ZERO, and can't map to a side by default. - // Map zero insets IME to bottom, making it a special case of bottom insets. - mTypeSideMap.put(ITYPE_IME, ISIDE_BOTTOM); + if (frame != null) { + final SparseIntArray typeSideMap = new SparseIntArray(); + mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */); + mHiddenInsets = calculateInsets(mInitialInsetsState, frame, controls, false /* shown */, + null /* typeSideMap */); + mShownInsets = calculateInsets(mInitialInsetsState, frame, controls, true /* shown */, + typeSideMap); + mHasZeroInsetsIme = mShownInsets.bottom == 0 && controlsInternalType(ITYPE_IME); + if (mHasZeroInsetsIme) { + // IME has shownInsets of ZERO, and can't map to a side by default. + // Map zero insets IME to bottom, making it a special case of bottom insets. + typeSideMap.put(ITYPE_IME, ISIDE_BOTTOM); + } + buildSideControlsMap(typeSideMap, mSideControlsMap, controls); + } else { + // Passing a null frame indicates the caller wants to play the insets animation anyway, + // no matter the source provides insets to the frame or not. + mCurrentInsets = calculateInsets(mInitialInsetsState, controls, true /* shown */); + mHiddenInsets = calculateInsets(null, controls, false /* shown */); + mShownInsets = calculateInsets(null, controls, true /* shown */); + mHasZeroInsetsIme = mShownInsets.bottom == 0 && controlsInternalType(ITYPE_IME); + buildSideControlsMap(mSideControlsMap, controls); } - buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls); + mPendingInsets = mCurrentInsets; mAnimation = new WindowInsetsAnimation(mTypes, interpolator, durationMs); @@ -312,32 +323,52 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll proto.end(token); } - WindowInsetsAnimationControlListener getListener() { - return mListener; - } - SparseArray<InsetsSourceControl> getControls() { return mControls; } + private Insets getInsetsFromState(InsetsState state, Rect frame, + @Nullable @InternalInsetsSide SparseIntArray typeSideMap) { + return state.calculateInsets(frame, null /* ignoringVisibilityState */, + false /* isScreenRound */, false /* alwaysConsumeSystemBars */, + LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/, + 0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, TYPE_APPLICATION, + WINDOWING_MODE_UNDEFINED, typeSideMap).getInsets(mTypes); + } + + /** Computes the insets relative to the given frame. */ private Insets calculateInsets(InsetsState state, Rect frame, SparseArray<InsetsSourceControl> controls, boolean shown, @Nullable @InternalInsetsSide SparseIntArray typeSideMap) { for (int i = controls.size() - 1; i >= 0; i--) { - // control may be null if it got revoked. - if (controls.valueAt(i) == null) continue; - state.getSource(controls.valueAt(i).getType()).setVisible(shown); + final InsetsSourceControl control = controls.valueAt(i); + if (control == null) { + // control may be null if it got revoked. + continue; + } + state.getSource(control.getType()).setVisible(shown); } return getInsetsFromState(state, frame, typeSideMap); } - private Insets getInsetsFromState(InsetsState state, Rect frame, - @Nullable @InternalInsetsSide SparseIntArray typeSideMap) { - return state.calculateInsets(frame, null /* ignoringVisibilityState */, - false /* isScreenRound */, false /* alwaysConsumeSystemBars */, - LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/, - 0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, TYPE_APPLICATION, - WINDOWING_MODE_UNDEFINED, typeSideMap).getInsets(mTypes); + /** Computes the insets from the insets hints of controls. */ + private Insets calculateInsets(InsetsState state, SparseArray<InsetsSourceControl> controls, + boolean shownOrCurrent) { + Insets insets = Insets.NONE; + if (!shownOrCurrent) { + return insets; + } + for (int i = controls.size() - 1; i >= 0; i--) { + final InsetsSourceControl control = controls.valueAt(i); + if (control == null) { + // control may be null if it got revoked. + continue; + } + if (state == null || state.getSource(control.getType()).isVisible()) { + insets = Insets.max(insets, control.getInsetsHint()); + } + } + return insets; } private Insets sanitize(Insets insets) { @@ -356,13 +387,13 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll private void updateLeashesForSide(@InternalInsetsSide int side, int offset, int inset, ArrayList<SurfaceParams> surfaceParams, @Nullable InsetsState outState, float alpha) { - ArraySet<InsetsSourceControl> items = mSideSourceMap.get(side); - if (items == null) { + final ArraySet<InsetsSourceControl> controls = mSideControlsMap.get(side); + if (controls == null) { return; } // TODO: Implement behavior when inset spans over multiple types - for (int i = items.size() - 1; i >= 0; i--) { - final InsetsSourceControl control = items.valueAt(i); + for (int i = controls.size() - 1; i >= 0; i--) { + final InsetsSourceControl control = controls.valueAt(i); final InsetsSource source = mInitialInsetsState.getSource(control.getType()); final SurfaceControl leash = control.getLeash(); @@ -371,7 +402,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame); final boolean visible = mHasZeroInsetsIme && side == ISIDE_BOTTOM - ? (mAnimationType == ANIMATION_TYPE_SHOW ? true : !mFinished) + ? (mAnimationType == ANIMATION_TYPE_SHOW || !mFinished) : inset != 0; if (outState != null) { @@ -391,32 +422,32 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll } } - private void addTranslationToMatrix(@InternalInsetsSide int side, int inset, Matrix m, + private void addTranslationToMatrix(@InternalInsetsSide int side, int offset, Matrix m, Rect frame) { final float surfaceOffset = mTranslator != null - ? mTranslator.translateLengthInAppWindowToScreen(inset) : inset; + ? mTranslator.translateLengthInAppWindowToScreen(offset) : offset; switch (side) { case ISIDE_LEFT: m.postTranslate(-surfaceOffset, 0); - frame.offset(-inset, 0); + frame.offset(-offset, 0); break; case ISIDE_TOP: m.postTranslate(0, -surfaceOffset); - frame.offset(0, -inset); + frame.offset(0, -offset); break; case ISIDE_RIGHT: m.postTranslate(surfaceOffset, 0); - frame.offset(inset, 0); + frame.offset(offset, 0); break; case ISIDE_BOTTOM: m.postTranslate(0, surfaceOffset); - frame.offset(0, inset); + frame.offset(0, offset); break; } } - private static void buildTypeSourcesMap(SparseIntArray typeSideMap, - SparseSetArray<InsetsSourceControl> sideSourcesMap, + private static void buildSideControlsMap(SparseIntArray typeSideMap, + SparseSetArray<InsetsSourceControl> sideControlsMap, SparseArray<InsetsSourceControl> controls) { for (int i = typeSideMap.size() - 1; i >= 0; i--) { final int type = typeSideMap.keyAt(i); @@ -427,7 +458,24 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll // there can be some null controllers. continue; } - sideSourcesMap.add(side, control); + sideControlsMap.add(side, control); + } + } + + private static void buildSideControlsMap( + SparseSetArray<InsetsSourceControl> sideControlsMap, + SparseArray<InsetsSourceControl> controls) { + for (int i = controls.size() - 1; i >= 0; i--) { + final InsetsSourceControl control = controls.valueAt(i); + if (control == null) { + // control may be null if it got revoked. + continue; + } + @InternalInsetsSide int side = InsetsState.getInsetSide(control.getInsetsHint()); + if (side == ISIDE_FLOATING && control.getType() == ITYPE_IME) { + side = ISIDE_BOTTOM; + } + sideControlsMap.add(side, control); } } } diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java index 6a34a1520fa3..6122c90ce06e 100644 --- a/core/java/android/view/InsetsAnimationThreadControlRunner.java +++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java @@ -19,6 +19,7 @@ package android.view; import static android.view.InsetsController.DEBUG; import static android.view.SyncRtSurfaceTransactionApplier.applyParams; +import android.annotation.Nullable; import android.annotation.UiThread; import android.content.res.CompatibilityInfo; import android.graphics.Rect; @@ -100,8 +101,8 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro }; @UiThread - public InsetsAnimationThreadControlRunner(SparseArray<InsetsSourceControl> controls, Rect frame, - InsetsState state, WindowInsetsAnimationControlListener listener, + public InsetsAnimationThreadControlRunner(SparseArray<InsetsSourceControl> controls, + @Nullable Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener, @InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator, @AnimationType int animationType, CompatibilityInfo.Translator translator, diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index e681c0e3580e..8bf78db354fe 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -837,9 +837,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation PendingControlRequest pendingRequest = mPendingImeControlRequest; mPendingImeControlRequest = null; mHandler.removeCallbacks(mPendingControlTimeout); + + // We are about to playing the default animation. Passing a null frame indicates the + // controlled types should be animated regardless of the frame. controlAnimationUnchecked( pendingRequest.types, pendingRequest.cancellationSignal, - pendingRequest.listener, mFrame, + pendingRequest.listener, null /* frame */, true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator, pendingRequest.animationType, pendingRequest.layoutInsetsDuringAnimation, @@ -934,7 +937,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void controlAnimationUnchecked(@InsetsType int types, @Nullable CancellationSignal cancellationSignal, - WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, + WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme, long durationMs, Interpolator interpolator, @AnimationType int animationType, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, @@ -1358,10 +1361,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation show, hasAnimationCallbacks, types, skipAnim || mAnimationsDisabled, mHost.dipToPx(InternalAnimationControlListener.FLOATING_IME_BOTTOM_INSET)); - // Show/hide animations always need to be relative to the display frame, in order that shown - // and hidden state insets are correct. + // We are about to playing the default animation (show/hide). Passing a null frame indicates + // the controlled types should be animated regardless of the frame. controlAnimationUnchecked( - types, null /* cancellationSignal */, listener, mState.getDisplayFrame(), fromIme, + types, null /* cancellationSignal */, listener, null /* frame */, fromIme, listener.getDurationMs(), listener.getInterpolator(), show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index 1d4b4111f884..9256beff0e4f 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -23,6 +23,7 @@ import static android.view.InsetsSourceControlProto.POSITION; import static android.view.InsetsSourceControlProto.TYPE; import android.annotation.Nullable; +import android.graphics.Insets; import android.graphics.Point; import android.os.Parcel; import android.os.Parcelable; @@ -41,13 +42,19 @@ public class InsetsSourceControl implements Parcelable { private final @InternalInsetsType int mType; private final @Nullable SurfaceControl mLeash; private final Point mSurfacePosition; + + // This is used while playing an insets animation regardless of the relative frame. This would + // be the insets received by the bounds of its source window. + private Insets mInsetsHint; + private boolean mSkipAnimationOnce; public InsetsSourceControl(@InternalInsetsType int type, @Nullable SurfaceControl leash, - Point surfacePosition) { + Point surfacePosition, Insets insetsHint) { mType = type; mLeash = leash; mSurfacePosition = surfacePosition; + mInsetsHint = insetsHint; } public InsetsSourceControl(InsetsSourceControl other) { @@ -58,9 +65,18 @@ public class InsetsSourceControl implements Parcelable { mLeash = null; } mSurfacePosition = new Point(other.mSurfacePosition); + mInsetsHint = other.mInsetsHint; mSkipAnimationOnce = other.getAndClearSkipAnimationOnce(); } + public InsetsSourceControl(Parcel in) { + mType = in.readInt(); + mLeash = in.readTypedObject(SurfaceControl.CREATOR); + mSurfacePosition = in.readTypedObject(Point.CREATOR); + mInsetsHint = in.readTypedObject(Insets.CREATOR); + mSkipAnimationOnce = in.readBoolean(); + } + public int getType() { return mType; } @@ -75,13 +91,6 @@ public class InsetsSourceControl implements Parcelable { return mLeash; } - public InsetsSourceControl(Parcel in) { - mType = in.readInt(); - mLeash = in.readTypedObject(SurfaceControl.CREATOR); - mSurfacePosition = in.readTypedObject(Point.CREATOR); - mSkipAnimationOnce = in.readBoolean(); - } - public boolean setSurfacePosition(int left, int top) { if (mSurfacePosition.equals(left, top)) { return false; @@ -90,14 +99,26 @@ public class InsetsSourceControl implements Parcelable { return true; } - public void setSkipAnimationOnce(boolean skipAnimation) { - mSkipAnimationOnce = skipAnimation; - } - public Point getSurfacePosition() { return mSurfacePosition; } + public void setInsetsHint(Insets insets) { + mInsetsHint = insets; + } + + public void setInsetsHint(int left, int top, int right, int bottom) { + mInsetsHint = Insets.of(left, top, right, bottom); + } + + public Insets getInsetsHint() { + return mInsetsHint; + } + + public void setSkipAnimationOnce(boolean skipAnimation) { + mSkipAnimationOnce = skipAnimation; + } + /** * Get the state whether the current control needs to skip animation or not. * @@ -121,6 +142,7 @@ public class InsetsSourceControl implements Parcelable { dest.writeInt(mType); dest.writeTypedObject(mLeash, 0 /* parcelableFlags */); dest.writeTypedObject(mSurfacePosition, 0 /* parcelableFlags */); + dest.writeTypedObject(mInsetsHint, 0 /* parcelableFlags */); dest.writeBoolean(mSkipAnimationOnce); } @@ -135,6 +157,7 @@ public class InsetsSourceControl implements Parcelable { pw.print("InsetsSourceControl type="); pw.print(InsetsState.typeToString(mType)); pw.print(" mLeash="); pw.print(mLeash); pw.print(" mSurfacePosition="); pw.print(mSurfacePosition); + pw.print(" mInsetsHint="); pw.print(mInsetsHint); pw.print(" mSkipAnimationOnce="); pw.print(mSkipAnimationOnce); pw.println(); } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index fce1952de44b..3d1c8ff515c7 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -400,7 +400,7 @@ public class InsetsState implements Parcelable { * Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b * is set in order that this method returns a meaningful result. */ - private @InternalInsetsSide int getInsetSide(Insets insets) { + static @InternalInsetsSide int getInsetSide(Insets insets) { if (Insets.NONE.equals(insets)) { return ISIDE_FLOATING; } diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java index b1b670f5e0c9..14dcdad8e2b5 100644 --- a/core/java/android/view/RemoteAnimationTarget.java +++ b/core/java/android/view/RemoteAnimationTarget.java @@ -31,6 +31,7 @@ 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; @@ -195,12 +196,30 @@ 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; @@ -217,6 +236,7 @@ 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) { @@ -236,6 +256,7 @@ public class RemoteAnimationTarget implements Parcelable { startLeash = in.readTypedObject(SurfaceControl.CREATOR); startBounds = in.readTypedObject(Rect.CREATOR); pictureInPictureParams = in.readTypedObject(PictureInPictureParams.CREATOR); + windowType = in.readInt(); } @Override @@ -261,6 +282,7 @@ 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) { @@ -278,6 +300,7 @@ public class RemoteAnimationTarget implements Parcelable { pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration); pw.print(prefix); pw.print("leash="); pw.println(leash); pw.print(prefix); pw.print("pictureInPictureParams="); pw.println(pictureInPictureParams); + pw.print(prefix); pw.print("windowType="); pw.print(windowType); } public void dumpDebug(ProtoOutputStream proto, long fieldId) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d462f5844a70..e4fb61107c4a 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1100,6 +1100,7 @@ public final class ViewRootImpl implements ViewParent, mTempControls); if (mTranslator != null) { mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets); + mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls); } } catch (RemoteException e) { mAdded = false; @@ -7699,6 +7700,7 @@ public final class ViewRootImpl implements ViewParent, if (mTranslator != null) { mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame); mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets); + mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls); } setFrame(mTmpFrames.frame); mInsetsController.onStateChanged(mTempInsets); @@ -8158,6 +8160,7 @@ public final class ViewRootImpl implements ViewParent, } if (mTranslator != null) { mTranslator.translateInsetsStateInScreenToAppWindow(insetsState); + mTranslator.translateSourceControlsInScreenToAppWindow(activeControls); } if (insetsState != null && insetsState.getSource(ITYPE_IME).isVisible()) { ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchInsetsControlChanged", diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java index b8893cee834d..27c637bb59dd 100644 --- a/core/java/android/view/inputmethod/InlineSuggestion.java +++ b/core/java/android/view/inputmethod/InlineSuggestion.java @@ -31,6 +31,7 @@ import android.os.RemoteException; import android.util.Size; import android.util.Slog; import android.view.SurfaceControlViewHost; +import android.view.View; import android.view.ViewGroup; import android.widget.inline.InlineContentView; @@ -38,6 +39,7 @@ import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; import com.android.internal.view.inline.IInlineContentCallback; import com.android.internal.view.inline.IInlineContentProvider; +import com.android.internal.view.inline.InlineTooltipUi; import java.lang.ref.WeakReference; import java.util.concurrent.Executor; @@ -75,6 +77,15 @@ public final class InlineSuggestion implements Parcelable { private InlineContentCallbackImpl mInlineContentCallback; /** + * Used to show up the inline suggestion tooltip. + * + * @hide + */ + @Nullable + @DataClass.ParcelWith(InlineTooltipUiParceling.class) + private InlineTooltipUi mInlineTooltipUi; + + /** * Creates a new {@link InlineSuggestion}, for testing purpose. * * @hide @@ -82,7 +93,8 @@ public final class InlineSuggestion implements Parcelable { @TestApi @NonNull public static InlineSuggestion newInlineSuggestion(@NonNull InlineSuggestionInfo info) { - return new InlineSuggestion(info, null, /* inlineContentCallback */ null); + return new InlineSuggestion(info, null, /* inlineContentCallback */ null, + /* inlineTooltipUi */ null); } /** @@ -92,7 +104,7 @@ public final class InlineSuggestion implements Parcelable { */ public InlineSuggestion(@NonNull InlineSuggestionInfo info, @Nullable IInlineContentProvider contentProvider) { - this(info, contentProvider, /* inlineContentCallback */ null); + this(info, contentProvider, /* inlineContentCallback */ null, /* inlineTooltipUi */ null); } /** @@ -136,9 +148,21 @@ public final class InlineSuggestion implements Parcelable { "size is neither between min:" + minSize + " and max:" + maxSize + ", nor wrap_content"); } - mInlineContentCallback = getInlineContentCallback(context, callbackExecutor, callback); + + InlineSuggestion toolTip = mInfo.getTooltip(); + if (toolTip != null) { + if (mInlineTooltipUi == null) { + mInlineTooltipUi = new InlineTooltipUi(context); + } + } else { + mInlineTooltipUi = null; + } + + mInlineContentCallback = getInlineContentCallback(context, callbackExecutor, callback, + mInlineTooltipUi); if (mContentProvider == null) { callbackExecutor.execute(() -> callback.accept(/* view */ null)); + mInlineTooltipUi = null; return; } try { @@ -148,6 +172,13 @@ public final class InlineSuggestion implements Parcelable { Slog.w(TAG, "Error creating suggestion content surface: " + e); callbackExecutor.execute(() -> callback.accept(/* view */ null)); } + if (toolTip == null) return; + + final Size tooltipSize = new Size(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + mInfo.getTooltip().inflate(context, tooltipSize, callbackExecutor, view -> { + Handler.getMain().post(() -> mInlineTooltipUi.setTooltipView(view)); + }); } /** @@ -162,12 +193,13 @@ public final class InlineSuggestion implements Parcelable { } private synchronized InlineContentCallbackImpl getInlineContentCallback(Context context, - Executor callbackExecutor, Consumer<InlineContentView> callback) { + Executor callbackExecutor, Consumer<InlineContentView> callback, + InlineTooltipUi inlineTooltipUi) { if (mInlineContentCallback != null) { throw new IllegalStateException("Already called #inflate()"); } return new InlineContentCallbackImpl(context, mContentProvider, callbackExecutor, - callback); + callback, inlineTooltipUi); } /** @@ -267,14 +299,19 @@ public final class InlineSuggestion implements Parcelable { @Nullable private Consumer<SurfaceControlViewHost.SurfacePackage> mSurfacePackageConsumer; + @Nullable + private InlineTooltipUi mInlineTooltipUi; + InlineContentCallbackImpl(@NonNull Context context, @Nullable IInlineContentProvider inlineContentProvider, @NonNull @CallbackExecutor Executor callbackExecutor, - @NonNull Consumer<InlineContentView> callback) { + @NonNull Consumer<InlineContentView> callback, + @Nullable InlineTooltipUi inlineTooltipUi) { mContext = context; mInlineContentProvider = inlineContentProvider; mCallbackExecutor = callbackExecutor; mCallback = callback; + mInlineTooltipUi = inlineTooltipUi; } @BinderThread @@ -305,6 +342,17 @@ public final class InlineSuggestion implements Parcelable { mCallbackExecutor.execute(() -> mCallback.accept(/* view */null)); } else { mView = new InlineContentView(mContext); + if (mInlineTooltipUi != null) { + mView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, + int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + if (mInlineTooltipUi != null) { + mInlineTooltipUi.update(mView); + } + } + }); + } mView.setLayoutParams(new ViewGroup.LayoutParams(width, height)); mView.setChildSurfacePackageUpdater(getSurfacePackageUpdater()); mCallbackExecutor.execute(() -> mCallback.accept(mView)); @@ -425,10 +473,25 @@ public final class InlineSuggestion implements Parcelable { } } + /** + * This class used to provide parcelling logic for InlineContentCallbackImpl. It's intended to + * make this parcelling a no-op, since it can't be parceled and we don't need to parcel it. + */ + private static class InlineTooltipUiParceling implements + Parcelling<InlineTooltipUi> { + @Override + public void parcel(InlineTooltipUi item, Parcel dest, int parcelFlags) { + } + + @Override + public InlineTooltipUi unparcel(Parcel source) { + return null; + } + } - // Code below generated by codegen v1.0.15. + // Code below generated by codegen v1.0.22. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -446,18 +509,22 @@ public final class InlineSuggestion implements Parcelable { * * @param inlineContentCallback * Used to keep a strong reference to the callback so it doesn't get garbage collected. + * @param inlineTooltipUi + * Used to show up the inline suggestion tooltip. * @hide */ @DataClass.Generated.Member public InlineSuggestion( @NonNull InlineSuggestionInfo info, @Nullable IInlineContentProvider contentProvider, - @Nullable InlineContentCallbackImpl inlineContentCallback) { + @Nullable InlineContentCallbackImpl inlineContentCallback, + @Nullable InlineTooltipUi inlineTooltipUi) { this.mInfo = info; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mInfo); this.mContentProvider = contentProvider; this.mInlineContentCallback = inlineContentCallback; + this.mInlineTooltipUi = inlineTooltipUi; // onConstructed(); // You can define this method to get a callback } @@ -485,6 +552,16 @@ public final class InlineSuggestion implements Parcelable { return mInlineContentCallback; } + /** + * Used to show up the inline suggestion tooltip. + * + * @hide + */ + @DataClass.Generated.Member + public @Nullable InlineTooltipUi getInlineTooltipUi() { + return mInlineTooltipUi; + } + @Override @DataClass.Generated.Member public String toString() { @@ -494,7 +571,8 @@ public final class InlineSuggestion implements Parcelable { return "InlineSuggestion { " + "info = " + mInfo + ", " + "contentProvider = " + mContentProvider + ", " + - "inlineContentCallback = " + mInlineContentCallback + + "inlineContentCallback = " + mInlineContentCallback + ", " + + "inlineTooltipUi = " + mInlineTooltipUi + " }"; } @@ -513,7 +591,8 @@ public final class InlineSuggestion implements Parcelable { return true && java.util.Objects.equals(mInfo, that.mInfo) && java.util.Objects.equals(mContentProvider, that.mContentProvider) - && java.util.Objects.equals(mInlineContentCallback, that.mInlineContentCallback); + && java.util.Objects.equals(mInlineContentCallback, that.mInlineContentCallback) + && java.util.Objects.equals(mInlineTooltipUi, that.mInlineTooltipUi); } @Override @@ -526,6 +605,7 @@ public final class InlineSuggestion implements Parcelable { _hash = 31 * _hash + java.util.Objects.hashCode(mInfo); _hash = 31 * _hash + java.util.Objects.hashCode(mContentProvider); _hash = 31 * _hash + java.util.Objects.hashCode(mInlineContentCallback); + _hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipUi); return _hash; } @@ -540,6 +620,17 @@ public final class InlineSuggestion implements Parcelable { } } + @DataClass.Generated.Member + static Parcelling<InlineTooltipUi> sParcellingForInlineTooltipUi = + Parcelling.Cache.get( + InlineTooltipUiParceling.class); + static { + if (sParcellingForInlineTooltipUi == null) { + sParcellingForInlineTooltipUi = Parcelling.Cache.put( + new InlineTooltipUiParceling()); + } + } + @Override @DataClass.Generated.Member public void writeToParcel(@NonNull Parcel dest, int flags) { @@ -549,10 +640,12 @@ public final class InlineSuggestion implements Parcelable { byte flg = 0; if (mContentProvider != null) flg |= 0x2; if (mInlineContentCallback != null) flg |= 0x4; + if (mInlineTooltipUi != null) flg |= 0x8; dest.writeByte(flg); dest.writeTypedObject(mInfo, flags); if (mContentProvider != null) dest.writeStrongInterface(mContentProvider); sParcellingForInlineContentCallback.parcel(mInlineContentCallback, dest, flags); + sParcellingForInlineTooltipUi.parcel(mInlineTooltipUi, dest, flags); } @Override @@ -570,12 +663,14 @@ public final class InlineSuggestion implements Parcelable { InlineSuggestionInfo info = (InlineSuggestionInfo) in.readTypedObject(InlineSuggestionInfo.CREATOR); IInlineContentProvider contentProvider = (flg & 0x2) == 0 ? null : IInlineContentProvider.Stub.asInterface(in.readStrongBinder()); InlineContentCallbackImpl inlineContentCallback = sParcellingForInlineContentCallback.unparcel(in); + InlineTooltipUi inlineTooltipUi = sParcellingForInlineTooltipUi.unparcel(in); this.mInfo = info; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mInfo); this.mContentProvider = contentProvider; this.mInlineContentCallback = inlineContentCallback; + this.mInlineTooltipUi = inlineTooltipUi; // onConstructed(); // You can define this method to get a callback } @@ -595,10 +690,10 @@ public final class InlineSuggestion implements Parcelable { }; @DataClass.Generated( - time = 1589396017700L, - codegenVersion = "1.0.15", + time = 1615562097666L, + codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java", - inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate static boolean isValid(int,int,int)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)") + inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineTooltipUiParceling.class) com.android.internal.view.inline.InlineTooltipUi mInlineTooltipUi\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate static boolean isValid(int,int,int)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>,com.android.internal.view.inline.InlineTooltipUi)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java index 10fd0e036814..5798614b8c9f 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java +++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java @@ -73,6 +73,11 @@ public final class InlineSuggestionInfo implements Parcelable { private final boolean mPinned; /** + * @hide + */ + private final @Nullable InlineSuggestion mTooltip; + + /** * Creates a new {@link InlineSuggestionInfo}, for testing purpose. * * @hide @@ -84,12 +89,30 @@ public final class InlineSuggestionInfo implements Parcelable { @NonNull @Source String source, @SuppressLint("NullableCollection") @Nullable String[] autofillHints, @NonNull @Type String type, boolean isPinned) { - return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned); + return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned, + null); + } + + /** + * Creates a new {@link InlineSuggestionInfo}, for testing purpose. + * + * @hide + */ + @NonNull + public static InlineSuggestionInfo newInlineSuggestionInfo( + @NonNull InlinePresentationSpec presentationSpec, + @NonNull @Source String source, + @Nullable String[] autofillHints, @NonNull @Type String type, boolean isPinned, + @Nullable InlineSuggestion tooltip) { + return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned, + tooltip); } - // Code below generated by codegen v1.0.20. + + + // Code below generated by codegen v1.0.22. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -141,7 +164,8 @@ public final class InlineSuggestionInfo implements Parcelable { @NonNull @Source String source, @Nullable String[] autofillHints, @NonNull @Type String type, - boolean pinned) { + boolean pinned, + @Nullable InlineSuggestion tooltip) { this.mInlinePresentationSpec = inlinePresentationSpec; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mInlinePresentationSpec); @@ -171,6 +195,7 @@ public final class InlineSuggestionInfo implements Parcelable { com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mType); this.mPinned = pinned; + this.mTooltip = tooltip; // onConstructed(); // You can define this method to get a callback } @@ -215,6 +240,14 @@ public final class InlineSuggestionInfo implements Parcelable { return mPinned; } + /** + * @hide + */ + @DataClass.Generated.Member + public @Nullable InlineSuggestion getTooltip() { + return mTooltip; + } + @Override @DataClass.Generated.Member public String toString() { @@ -226,7 +259,8 @@ public final class InlineSuggestionInfo implements Parcelable { "source = " + mSource + ", " + "autofillHints = " + java.util.Arrays.toString(mAutofillHints) + ", " + "type = " + mType + ", " + - "pinned = " + mPinned + + "pinned = " + mPinned + ", " + + "tooltip = " + mTooltip + " }"; } @@ -247,7 +281,8 @@ public final class InlineSuggestionInfo implements Parcelable { && java.util.Objects.equals(mSource, that.mSource) && java.util.Arrays.equals(mAutofillHints, that.mAutofillHints) && java.util.Objects.equals(mType, that.mType) - && mPinned == that.mPinned; + && mPinned == that.mPinned + && java.util.Objects.equals(mTooltip, that.mTooltip); } @Override @@ -262,6 +297,7 @@ public final class InlineSuggestionInfo implements Parcelable { _hash = 31 * _hash + java.util.Arrays.hashCode(mAutofillHints); _hash = 31 * _hash + java.util.Objects.hashCode(mType); _hash = 31 * _hash + Boolean.hashCode(mPinned); + _hash = 31 * _hash + java.util.Objects.hashCode(mTooltip); return _hash; } @@ -274,11 +310,13 @@ public final class InlineSuggestionInfo implements Parcelable { byte flg = 0; if (mPinned) flg |= 0x10; if (mAutofillHints != null) flg |= 0x4; + if (mTooltip != null) flg |= 0x20; dest.writeByte(flg); dest.writeTypedObject(mInlinePresentationSpec, flags); dest.writeString(mSource); if (mAutofillHints != null) dest.writeStringArray(mAutofillHints); dest.writeString(mType); + if (mTooltip != null) dest.writeTypedObject(mTooltip, flags); } @Override @@ -298,6 +336,7 @@ public final class InlineSuggestionInfo implements Parcelable { String source = in.readString(); String[] autofillHints = (flg & 0x4) == 0 ? null : in.createStringArray(); String type = in.readString(); + InlineSuggestion tooltip = (flg & 0x20) == 0 ? null : (InlineSuggestion) in.readTypedObject(InlineSuggestion.CREATOR); this.mInlinePresentationSpec = inlinePresentationSpec; com.android.internal.util.AnnotationValidations.validate( @@ -328,6 +367,7 @@ public final class InlineSuggestionInfo implements Parcelable { com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mType); this.mPinned = pinned; + this.mTooltip = tooltip; // onConstructed(); // You can define this method to get a callback } @@ -347,10 +387,10 @@ public final class InlineSuggestionInfo implements Parcelable { }; @DataClass.Generated( - time = 1604456249219L, - codegenVersion = "1.0.20", + time = 1614287616672L, + codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java", - inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)") + inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestion mTooltip\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\npublic static @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean,android.view.inputmethod.InlineSuggestion)\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java index 0ab4e05227ba..e1e175512edc 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java @@ -106,6 +106,27 @@ public final class InlineSuggestionsRequest implements Parcelable { private int mHostDisplayId; /** + * Specifies the UI specification for the inline suggestion tooltip in the response. + */ + private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec; + + /** + * Whether the IME supports inline suggestions from the default Autofill service that + * provides the input view. + * + * Note: The default value is {@code true}. + */ + private boolean mServiceSupported; + + /** + * Whether the IME supports inline suggestions from the application that provides the + * input view. + * + * Note: The default value is {@code true}. + */ + private boolean mClientSupported; + + /** * @hide * @see {@link #mHostInputToken}. */ @@ -151,6 +172,10 @@ public final class InlineSuggestionsRequest implements Parcelable { for (int i = 0; i < mInlinePresentationSpecs.size(); i++) { mInlinePresentationSpecs.get(i).filterContentTypes(); } + + if (mInlineTooltipPresentationSpec != null) { + mInlineTooltipPresentationSpec.filterContentTypes(); + } } private static int defaultMaxSuggestionCount() { @@ -161,6 +186,10 @@ public final class InlineSuggestionsRequest implements Parcelable { return ActivityThread.currentPackageName(); } + private static InlinePresentationSpec defaultInlineTooltipPresentationSpec() { + return null; + } + /** * The {@link InlineSuggestionsRequest#getSupportedLocales()} now returns empty locale list when * it's not set, instead of the default system locale. @@ -191,6 +220,14 @@ public final class InlineSuggestionsRequest implements Parcelable { return Bundle.EMPTY; } + private static boolean defaultServiceSupported() { + return true; + } + + private static boolean defaultClientSupported() { + return true; + } + /** @hide */ abstract static class BaseBuilder { abstract Builder setInlinePresentationSpecs( @@ -203,6 +240,16 @@ public final class InlineSuggestionsRequest implements Parcelable { abstract Builder setHostDisplayId(int value); } + /** @hide */ + public boolean isServiceSupported() { + return mServiceSupported; + } + + /** @hide */ + public boolean isClientSupported() { + return mClientSupported; + } + // Code below generated by codegen v1.0.22. @@ -226,7 +273,10 @@ public final class InlineSuggestionsRequest implements Parcelable { @NonNull LocaleList supportedLocales, @NonNull Bundle extras, @Nullable IBinder hostInputToken, - int hostDisplayId) { + int hostDisplayId, + @Nullable InlinePresentationSpec inlineTooltipPresentationSpec, + boolean serviceSupported, + boolean clientSupported) { this.mMaxSuggestionCount = maxSuggestionCount; this.mInlinePresentationSpecs = inlinePresentationSpecs; com.android.internal.util.AnnotationValidations.validate( @@ -242,6 +292,9 @@ public final class InlineSuggestionsRequest implements Parcelable { NonNull.class, null, mExtras); this.mHostInputToken = hostInputToken; this.mHostDisplayId = hostDisplayId; + this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec; + this.mServiceSupported = serviceSupported; + this.mClientSupported = clientSupported; onConstructed(); } @@ -324,6 +377,16 @@ public final class InlineSuggestionsRequest implements Parcelable { return mHostDisplayId; } + /** + * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response. + * + * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec) + */ + @DataClass.Generated.Member + public @Nullable InlinePresentationSpec getInlineTooltipPresentationSpec() { + return mInlineTooltipPresentationSpec; + } + @Override @DataClass.Generated.Member public String toString() { @@ -337,7 +400,10 @@ public final class InlineSuggestionsRequest implements Parcelable { "supportedLocales = " + mSupportedLocales + ", " + "extras = " + mExtras + ", " + "hostInputToken = " + mHostInputToken + ", " + - "hostDisplayId = " + mHostDisplayId + + "hostDisplayId = " + mHostDisplayId + ", " + + "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + ", " + + "serviceSupported = " + mServiceSupported + ", " + + "clientSupported = " + mClientSupported + " }"; } @@ -360,7 +426,10 @@ public final class InlineSuggestionsRequest implements Parcelable { && java.util.Objects.equals(mSupportedLocales, that.mSupportedLocales) && extrasEquals(that.mExtras) && java.util.Objects.equals(mHostInputToken, that.mHostInputToken) - && mHostDisplayId == that.mHostDisplayId; + && mHostDisplayId == that.mHostDisplayId + && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec) + && mServiceSupported == that.mServiceSupported + && mClientSupported == that.mClientSupported; } @Override @@ -377,6 +446,9 @@ public final class InlineSuggestionsRequest implements Parcelable { _hash = 31 * _hash + java.util.Objects.hashCode(mExtras); _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken); _hash = 31 * _hash + mHostDisplayId; + _hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipPresentationSpec); + _hash = 31 * _hash + Boolean.hashCode(mServiceSupported); + _hash = 31 * _hash + Boolean.hashCode(mClientSupported); return _hash; } @@ -386,9 +458,12 @@ public final class InlineSuggestionsRequest implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } - byte flg = 0; + int flg = 0; + if (mServiceSupported) flg |= 0x100; + if (mClientSupported) flg |= 0x200; if (mHostInputToken != null) flg |= 0x20; - dest.writeByte(flg); + if (mInlineTooltipPresentationSpec != null) flg |= 0x80; + dest.writeInt(flg); dest.writeInt(mMaxSuggestionCount); dest.writeParcelableList(mInlinePresentationSpecs, flags); dest.writeString(mHostPackageName); @@ -396,6 +471,7 @@ public final class InlineSuggestionsRequest implements Parcelable { dest.writeBundle(mExtras); parcelHostInputToken(dest, flags); dest.writeInt(mHostDisplayId); + if (mInlineTooltipPresentationSpec != null) dest.writeTypedObject(mInlineTooltipPresentationSpec, flags); } @Override @@ -409,7 +485,9 @@ public final class InlineSuggestionsRequest implements Parcelable { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } - byte flg = in.readByte(); + int flg = in.readInt(); + boolean serviceSupported = (flg & 0x100) != 0; + boolean clientSupported = (flg & 0x200) != 0; int maxSuggestionCount = in.readInt(); List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>(); in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader()); @@ -418,6 +496,7 @@ public final class InlineSuggestionsRequest implements Parcelable { Bundle extras = in.readBundle(); IBinder hostInputToken = unparcelHostInputToken(in); int hostDisplayId = in.readInt(); + InlinePresentationSpec inlineTooltipPresentationSpec = (flg & 0x80) == 0 ? null : (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR); this.mMaxSuggestionCount = maxSuggestionCount; this.mInlinePresentationSpecs = inlinePresentationSpecs; @@ -434,6 +513,9 @@ public final class InlineSuggestionsRequest implements Parcelable { NonNull.class, null, mExtras); this.mHostInputToken = hostInputToken; this.mHostDisplayId = hostDisplayId; + this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec; + this.mServiceSupported = serviceSupported; + this.mClientSupported = clientSupported; onConstructed(); } @@ -466,6 +548,9 @@ public final class InlineSuggestionsRequest implements Parcelable { private @NonNull Bundle mExtras; private @Nullable IBinder mHostInputToken; private int mHostDisplayId; + private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec; + private boolean mServiceSupported; + private boolean mClientSupported; private long mBuilderFieldsSet = 0L; @@ -597,10 +682,51 @@ public final class InlineSuggestionsRequest implements Parcelable { return this; } + /** + * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response. + * + * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)s + */ + @DataClass.Generated.Member + public @NonNull Builder setInlineTooltipPresentationSpec(@NonNull InlinePresentationSpec value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x80; + mInlineTooltipPresentationSpec = value; + return this; + } + + /** + * Whether the IME supports inline suggestions from the default Autofill service that + * provides the input view. + * + * Note: The default value is {@code true}. + */ + @DataClass.Generated.Member + public @NonNull Builder setServiceSupported(boolean value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x100; + mServiceSupported = value; + return this; + } + + /** + * Whether the IME supports inline suggestions from the application that provides the + * input view. + * + * Note: The default value is {@code true}. + */ + @DataClass.Generated.Member + public @NonNull Builder setClientSupported(boolean value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x200; + mClientSupported = value; + return this; + } + /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull InlineSuggestionsRequest build() { checkNotUsed(); - mBuilderFieldsSet |= 0x80; // Mark builder used + mBuilderFieldsSet |= 0x400; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mMaxSuggestionCount = defaultMaxSuggestionCount(); @@ -620,6 +746,15 @@ public final class InlineSuggestionsRequest implements Parcelable { if ((mBuilderFieldsSet & 0x40) == 0) { mHostDisplayId = defaultHostDisplayId(); } + if ((mBuilderFieldsSet & 0x80) == 0) { + mInlineTooltipPresentationSpec = defaultInlineTooltipPresentationSpec(); + } + if ((mBuilderFieldsSet & 0x100) == 0) { + mServiceSupported = defaultServiceSupported(); + } + if ((mBuilderFieldsSet & 0x200) == 0) { + mClientSupported = defaultClientSupported(); + } InlineSuggestionsRequest o = new InlineSuggestionsRequest( mMaxSuggestionCount, mInlinePresentationSpecs, @@ -627,12 +762,15 @@ public final class InlineSuggestionsRequest implements Parcelable { mSupportedLocales, mExtras, mHostInputToken, - mHostDisplayId); + mHostDisplayId, + mInlineTooltipPresentationSpec, + mServiceSupported, + mClientSupported); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x80) != 0) { + if ((mBuilderFieldsSet & 0x400) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -640,10 +778,10 @@ public final class InlineSuggestionsRequest implements Parcelable { } @DataClass.Generated( - time = 1612206506050L, + time = 1615798784918L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java", - inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate boolean mServiceSupported\nprivate boolean mClientSupported\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nprivate static boolean defaultServiceSupported()\nprivate static boolean defaultClientSupported()\npublic boolean isServiceSupported()\npublic boolean isClientSupported()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index ba58b6525a6d..a449cf1876e1 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -16,6 +16,8 @@ package android.view.textservice; +import android.annotation.BinderThread; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Binder; import android.os.Build; @@ -27,6 +29,7 @@ import android.os.RemoteException; import android.util.Log; import android.view.inputmethod.InputMethodManager; +import com.android.internal.annotations.GuardedBy; import com.android.internal.textservice.ISpellCheckerSession; import com.android.internal.textservice.ISpellCheckerSessionListener; import com.android.internal.textservice.ITextServicesSessionListener; @@ -35,6 +38,7 @@ import dalvik.system.CloseGuard; import java.util.LinkedList; import java.util.Queue; +import java.util.concurrent.Executor; /** * The SpellCheckerSession interface provides the per client functionality of SpellCheckerService. @@ -102,38 +106,26 @@ public class SpellCheckerSession { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private final SpellCheckerSessionListener mSpellCheckerSessionListener; private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl; + private final Executor mExecutor; private final CloseGuard mGuard = CloseGuard.get(); - /** Handler that will execute the main tasks */ - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_ON_GET_SUGGESTION_MULTIPLE: - handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj); - break; - case MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE: - handleOnGetSentenceSuggestionsMultiple((SentenceSuggestionsInfo[]) msg.obj); - break; - } - } - }; - /** * Constructor * @hide */ public SpellCheckerSession( - SpellCheckerInfo info, TextServicesManager tsm, SpellCheckerSessionListener listener) { + SpellCheckerInfo info, TextServicesManager tsm, SpellCheckerSessionListener listener, + Executor executor) { if (info == null || listener == null || tsm == null) { throw new NullPointerException(); } mSpellCheckerInfo = info; - mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(mHandler); + mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(this); mInternalListener = new InternalListener(mSpellCheckerSessionListenerImpl); mTextServicesManager = tsm; mSpellCheckerSessionListener = listener; + mExecutor = executor; mGuard.open("finishSession"); } @@ -219,12 +211,13 @@ public class SpellCheckerSession { textInfos, suggestionsLimit, sequentialWords); } - private void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionInfos) { - mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos); + void handleOnGetSuggestionsMultiple(SuggestionsInfo[] suggestionsInfos) { + mExecutor.execute(() -> mSpellCheckerSessionListener.onGetSuggestions(suggestionsInfos)); } - private void handleOnGetSentenceSuggestionsMultiple(SentenceSuggestionsInfo[] suggestionInfos) { - mSpellCheckerSessionListener.onGetSentenceSuggestions(suggestionInfos); + void handleOnGetSentenceSuggestionsMultiple(SentenceSuggestionsInfo[] suggestionsInfos) { + mExecutor.execute(() -> + mSpellCheckerSessionListener.onGetSentenceSuggestions(suggestionsInfos)); } private static final class SpellCheckerSessionListenerImpl @@ -249,7 +242,8 @@ public class SpellCheckerSession { } private final Queue<SpellCheckerParams> mPendingTasks = new LinkedList<>(); - private Handler mHandler; + @GuardedBy("SpellCheckerSessionListenerImpl.this") + private SpellCheckerSession mSpellCheckerSession; private static final int STATE_WAIT_CONNECTION = 0; private static final int STATE_CONNECTED = 1; @@ -270,8 +264,8 @@ public class SpellCheckerSession { private HandlerThread mThread; private Handler mAsyncHandler; - public SpellCheckerSessionListenerImpl(Handler handler) { - mHandler = handler; + SpellCheckerSessionListenerImpl(SpellCheckerSession spellCheckerSession) { + mSpellCheckerSession = spellCheckerSession; } private static class SpellCheckerParams { @@ -349,6 +343,7 @@ public class SpellCheckerSession { } } + @GuardedBy("SpellCheckerSessionListenerImpl.this") private void processCloseLocked() { if (DBG) Log.d(TAG, "entering processCloseLocked:" + " session" + (mISpellCheckerSession != null ? ".hashCode()=#" @@ -358,7 +353,7 @@ public class SpellCheckerSession { if (mThread != null) { mThread.quit(); } - mHandler = null; + mSpellCheckerSession = null; mPendingTasks.clear(); mThread = null; mAsyncHandler = null; @@ -502,23 +497,30 @@ public class SpellCheckerSession { processTask(session, scp, false); } + @BinderThread @Override public void onGetSuggestions(SuggestionsInfo[] results) { - synchronized (this) { - if (mHandler != null) { - mHandler.sendMessage(Message.obtain(mHandler, - MSG_ON_GET_SUGGESTION_MULTIPLE, results)); - } + SpellCheckerSession session = getSpellCheckerSession(); + if (session != null) { + // Lock should not be held when calling callback, in order to avoid deadlock. + session.handleOnGetSuggestionsMultiple(results); } } + @BinderThread @Override public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) { - synchronized (this) { - if (mHandler != null) { - mHandler.sendMessage(Message.obtain(mHandler, - MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results)); - } + SpellCheckerSession session = getSpellCheckerSession(); + if (session != null) { + // Lock should not be held when calling callback, in order to avoid deadlock. + session.handleOnGetSentenceSuggestionsMultiple(results); + } + } + + @Nullable + private SpellCheckerSession getSpellCheckerSession() { + synchronized (SpellCheckerSessionListenerImpl.this) { + return mSpellCheckerSession; } } } diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java index 6fb01a309402..bf91cca85522 100644 --- a/core/java/android/view/textservice/TextServicesManager.java +++ b/core/java/android/view/textservice/TextServicesManager.java @@ -16,6 +16,7 @@ package android.view.textservice; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -25,6 +26,8 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerExecutor; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; @@ -37,8 +40,11 @@ import com.android.internal.textservice.ISpellCheckerSessionListener; import com.android.internal.textservice.ITextServicesManager; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Objects; +import java.util.concurrent.Executor; /** * System API to the overall text services, which arbitrates interaction between applications @@ -160,10 +166,12 @@ public final class TextServicesManager { * {@link SuggestionsInfo#RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS} will be passed to the spell * checker as supported attributes. * - * @see #newSpellCheckerSession(Bundle, Locale, SpellCheckerSessionListener, boolean, int) + * @see #newSpellCheckerSession(Locale, boolean, int, Bundle, Executor, + * SpellCheckerSessionListener) * @param bundle A bundle to pass to the spell checker. * @param locale The locale for the spell checker. * @param listener A spell checker session lister for getting results from the spell checker. + * The listener will be called on the calling thread. * @param referToSpellCheckerLanguageSettings If true, the session for one of enabled * languages in settings will be used. * @return A spell checker session from the spell checker. @@ -173,10 +181,15 @@ public final class TextServicesManager { @Nullable Locale locale, @NonNull SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) { - return newSpellCheckerSession(bundle, locale, listener, referToSpellCheckerLanguageSettings, - SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY - | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO - | SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS); + // Attributes existed before {@link #newSpellCheckerSession(Locale, boolean, int, Bundle, + // Executor, SpellCheckerSessionListener)} was introduced. + int supportedAttributes = SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY + | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO + | SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS; + // Using the implicit looper to preserve the old behavior. + Executor executor = new HandlerExecutor(new Handler()); + return newSpellCheckerSession(locale, referToSpellCheckerLanguageSettings, + supportedAttributes, bundle, executor, listener); } /** @@ -190,25 +203,28 @@ public final class TextServicesManager { * language only (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be * selected. * - * @param bundle A bundle to pass to the spell checker. * @param locale The locale for the spell checker. - * @param listener A spell checker session lister for getting results from a spell checker. * @param referToSpellCheckerLanguageSettings If true, the session for one of enabled * languages in settings will be used. * @param supportedAttributes A union of {@link SuggestionsInfo} attributes that the spell * checker can set in the spell checking results. + * @param bundle A bundle for passing implementation-specific extra parameters for the spell + * checker. You can check the current spell checker package by + * {@link #getCurrentSpellCheckerInfo()}. + * @param executor An executor to call the listener on. + * @param listener A spell checker session lister for getting results from a spell checker. * @return The spell checker session of the spell checker. */ @Nullable public SpellCheckerSession newSpellCheckerSession( - @SuppressLint("NullableCollection") @Nullable Bundle bundle, @SuppressLint("UseIcu") @Nullable Locale locale, - @NonNull SpellCheckerSessionListener listener, - @SuppressLint("ListenerLast") boolean referToSpellCheckerLanguageSettings, - @SuppressLint("ListenerLast") @SuggestionsInfo.ResultAttrs int supportedAttributes) { - if (listener == null) { - throw new NullPointerException(); - } + boolean referToSpellCheckerLanguageSettings, + @SuggestionsInfo.ResultAttrs int supportedAttributes, + @Nullable Bundle bundle, + @NonNull @CallbackExecutor Executor executor, + @NonNull SpellCheckerSessionListener listener) { + Objects.requireNonNull(executor); + Objects.requireNonNull(listener); if (!referToSpellCheckerLanguageSettings && locale == null) { throw new IllegalArgumentException("Locale should not be null if you don't refer" + " settings."); @@ -258,7 +274,7 @@ public final class TextServicesManager { if (subtypeInUse == null) { return null; } - final SpellCheckerSession session = new SpellCheckerSession(sci, this, listener); + final SpellCheckerSession session = new SpellCheckerSession(sci, this, listener, executor); try { mService.getSpellCheckerService(mUserId, sci.getId(), subtypeInUse.getLocale(), session.getTextServicesSessionListener(), @@ -288,15 +304,15 @@ public final class TextServicesManager { } /** - * Retrieve the list of currently enabled spell checkers, or null if there is none. + * Retrieve the list of currently enabled spell checkers. * * @return The list of currently enabled spell checkers. */ - @Nullable - @SuppressLint("NullableCollection") + @NonNull public List<SpellCheckerInfo> getEnabledSpellCheckerInfos() { final SpellCheckerInfo[] enabledSpellCheckers = getEnabledSpellCheckers(); - return enabledSpellCheckers != null ? Arrays.asList(enabledSpellCheckers) : null; + return enabledSpellCheckers != null + ? Arrays.asList(enabledSpellCheckers) : Collections.emptyList(); } /** diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 0cedcea7b4d4..11ac7f2a5167 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -5339,9 +5339,13 @@ public class RemoteViews implements Parcelable, Filter { /** @hide */ public boolean canRecycleView(View v) { + Integer previousLayout = (Integer) v.getTag(R.id.widget_frame); + if (previousLayout == null) { + return false; + } Integer overrideIdTag = (Integer) v.getTag(R.id.remote_views_override_id); int overrideId = overrideIdTag == null ? View.NO_ID : overrideIdTag; - return (Integer) v.getTag(R.id.widget_frame) == getLayoutId() && mViewId == overrideId; + return previousLayout == getLayoutId() && mViewId == overrideId; } // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index 2464b4af23eb..a63305e55e53 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -126,14 +126,14 @@ public class SpellChecker implements SpellCheckerSessionListener { || mTextServicesManager.getCurrentSpellCheckerSubtype(true) == null) { mSpellCheckerSession = null; } else { + int supportedAttributes = SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY + | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO + | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR + | SuggestionsInfo.RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS; mSpellCheckerSession = mTextServicesManager.newSpellCheckerSession( + mCurrentLocale, false, supportedAttributes, null /* Bundle not currently used by the textServicesManager */, - mCurrentLocale, this, - false /* means any available languages from current spell checker */, - SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY - | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO - | SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_GRAMMAR_ERROR - | SuggestionsInfo.RESULT_ATTR_DONT_SHOW_UI_FOR_SUGGESTIONS); + mTextView.getContext().getMainExecutor(), this); } // Restore SpellCheckSpans in pool diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java index da445b8b9f33..ddea64a77f4b 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -23,6 +23,7 @@ import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -46,8 +47,6 @@ import android.widget.FrameLayout; import com.android.internal.R; import com.android.internal.policy.DecorView; -import java.util.function.Consumer; - /** * <p>The view which allows an activity to customize its splash screen exit animation.</p> * @@ -79,8 +78,8 @@ public final class SplashScreenView extends FrameLayout { private Animatable mAnimatableIcon; private ValueAnimator mAnimator; - private Runnable mAnimationFinishListener; - private Consumer<Canvas> mOnDrawCallback; + // The host activity when transfer view to it. + private Activity mHostActivity; // cache original window and status private Window mWindow; private boolean mDrawBarBackground; @@ -334,6 +333,17 @@ public final class SplashScreenView extends FrameLayout { restoreSystemUIColors(); mWindow = null; } + if (mHostActivity != null) { + mHostActivity.detachSplashScreenView(); + } + } + + /** + * Called when this view is attached to an activity. + * @hide + */ + public void attachHostActivity(Activity activity) { + mHostActivity = activity; } /** diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java index 2237efc9e2b6..2f40d3b457c6 100644 --- a/core/java/com/android/internal/notification/SystemNotificationChannels.java +++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java @@ -58,6 +58,7 @@ public class SystemNotificationChannels { public static String SYSTEM_CHANGES = "SYSTEM_CHANGES"; public static String DO_NOT_DISTURB = "DO_NOT_DISTURB"; public static String ACCESSIBILITY_MAGNIFICATION = "ACCESSIBILITY_MAGNIFICATION"; + public static String ACCESSIBILITY_SECURITY_POLICY = "ACCESSIBILITY_SECURITY_POLICY"; public static void createAll(Context context) { final NotificationManager nm = context.getSystemService(NotificationManager.class); @@ -199,6 +200,12 @@ public class SystemNotificationChannels { newFeaturePrompt.setBlockable(true); channelsList.add(newFeaturePrompt); + final NotificationChannel accessibilitySecurityPolicyChannel = new NotificationChannel( + ACCESSIBILITY_SECURITY_POLICY, + context.getString(R.string.notification_channel_accessibility_security_policy), + NotificationManager.IMPORTANCE_LOW); + channelsList.add(accessibilitySecurityPolicyChannel); + nm.createNotificationChannels(channelsList); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 33b55ac2f0a0..7f8788529714 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -11492,10 +11492,10 @@ public class BatteryStatsImpl extends BatteryStats { return; } - final ArrayMap<Uid, Double> uidEstimatedConsumptionMah = + final SparseDoubleArray uidEstimatedConsumptionMah = (mGlobalMeasuredEnergyStats != null && mWifiPowerCalculator != null && consumedChargeUC > 0) ? - new ArrayMap<>() : null; + new SparseDoubleArray() : null; double totalEstimatedConsumptionMah = 0; SparseLongArray rxPackets = new SparseLongArray(); @@ -11583,7 +11583,7 @@ public class BatteryStatsImpl extends BatteryStats { } } - addDoubleToUidMap(uidEstimatedConsumptionMah, u, + uidEstimatedConsumptionMah.add(u.getUid(), mWifiPowerCalculator.calcPowerWithoutControllerDataMah( entry.rxPackets, entry.txPackets, uidRunningMs, uidScanMs, uidBatchScanMs)); @@ -11709,7 +11709,7 @@ public class BatteryStatsImpl extends BatteryStats { if (uidEstimatedConsumptionMah != null) { double uidEstMah = mWifiPowerCalculator.calcPowerFromControllerDataMah( scanRxTimeSinceMarkMs, scanTxTimeSinceMarkMs, myIdleTimeMs); - addDoubleToUidMap(uidEstimatedConsumptionMah, uid, uidEstMah); + uidEstimatedConsumptionMah.add(uid.getUid(), uidEstMah); } } @@ -11731,7 +11731,7 @@ public class BatteryStatsImpl extends BatteryStats { uid.getOrCreateWifiControllerActivityLocked().getTxTimeCounters()[0] .addCountLocked(myTxTimeMs); if (uidEstimatedConsumptionMah != null) { - addDoubleToUidMap(uidEstimatedConsumptionMah, uid, + uidEstimatedConsumptionMah.add(uid.getUid(), mWifiPowerCalculator.calcPowerFromControllerDataMah( 0, myTxTimeMs, 0)); } @@ -11750,7 +11750,7 @@ public class BatteryStatsImpl extends BatteryStats { uid.getOrCreateWifiControllerActivityLocked().getRxTimeCounter() .addCountLocked(myRxTimeMs); if (uidEstimatedConsumptionMah != null) { - addDoubleToUidMap(uidEstimatedConsumptionMah, uid, + uidEstimatedConsumptionMah.add(uid.getUid(), mWifiPowerCalculator.calcPowerFromControllerDataMah( myRxTimeMs, 0, 0)); } @@ -12086,10 +12086,10 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, " Idle Time: " + idleTimeMs + " ms"); } - final ArrayMap<Uid, Double> uidEstimatedConsumptionMah = + final SparseDoubleArray uidEstimatedConsumptionMah = (mGlobalMeasuredEnergyStats != null && mBluetoothPowerCalculator != null && consumedChargeUC > 0) ? - new ArrayMap<>() : null; + new SparseDoubleArray() : null; long totalScanTimeMs = 0; @@ -12150,7 +12150,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.getTxTimeCounters()[0].addCountLocked(scanTimeTxSinceMarkMs); if (uidEstimatedConsumptionMah != null) { - addDoubleToUidMap(uidEstimatedConsumptionMah, u, + uidEstimatedConsumptionMah.add(u.getUid(), mBluetoothPowerCalculator.calculatePowerMah( scanTimeRxSinceMarkMs, scanTimeTxSinceMarkMs, 0)); } @@ -12217,7 +12217,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.getRxTimeCounter().addCountLocked(timeRxMs); if (uidEstimatedConsumptionMah != null) { - addDoubleToUidMap(uidEstimatedConsumptionMah, u, + uidEstimatedConsumptionMah.add(u.getUid(), mBluetoothPowerCalculator.calculatePowerMah(timeRxMs, 0, 0)); } } @@ -12230,7 +12230,7 @@ public class BatteryStatsImpl extends BatteryStats { counter.getTxTimeCounters()[0].addCountLocked(timeTxMs); if (uidEstimatedConsumptionMah != null) { - addDoubleToUidMap(uidEstimatedConsumptionMah, u, + uidEstimatedConsumptionMah.add(u.getUid(), mBluetoothPowerCalculator.calculatePowerMah(0, timeTxMs, 0)); } } @@ -12452,7 +12452,7 @@ public class BatteryStatsImpl extends BatteryStats { // If multidisplay becomes a reality, this is probably more reasonable than pooling. // On the first pass, collect total time since mark so that we can normalize power. - final ArrayMap<Uid, Double> fgTimeUsArray = new ArrayMap<>(); + final SparseDoubleArray fgTimeUsArray = new SparseDoubleArray(); final long elapsedRealtimeUs = elapsedRealtimeMs * 1000; // TODO(b/175726779): Update and optimize the algorithm (e.g. avoid iterating over ALL uids) final int uidStatsSize = mUidStats.size(); @@ -12460,7 +12460,7 @@ public class BatteryStatsImpl extends BatteryStats { final Uid uid = mUidStats.valueAt(i); final long fgTimeUs = uid.markProcessForegroundTimeUs(elapsedRealtimeMs, true); if (fgTimeUs == 0) continue; - fgTimeUsArray.put(uid, (double) fgTimeUs); + fgTimeUsArray.put(uid.getUid(), (double) fgTimeUs); } distributeEnergyToUidsLocked(powerBucket, chargeUC, fgTimeUsArray, 0); } @@ -12523,10 +12523,11 @@ public class BatteryStatsImpl extends BatteryStats { * <p>A consequence of minRatioDenominator is that the sum over all uids might be less than * totalConsumedChargeUC. This is intentional; the remainder is purposefully unnaccounted rather * than incorrectly blamed on uids, and implies unknown (non-uid) sources of drain. + * + * <p>All uids in ratioNumerators must exist in mUidStats already. */ - // TODO(b/182845832): Use some sort of "SparseDoubleArray" instead of ArrayMap<Uid, Double>. private void distributeEnergyToUidsLocked(@StandardPowerBucket int bucket, - long totalConsumedChargeUC, ArrayMap<Uid, Double> ratioNumerators, + long totalConsumedChargeUC, SparseDoubleArray ratioNumerators, double minRatioDenominator) { // If the sum of all app usage was greater than the total, use that instead: @@ -12538,7 +12539,7 @@ public class BatteryStatsImpl extends BatteryStats { if (ratioDenominator <= 0) return; for (int i = ratioNumerators.size() - 1; i >= 0; i--) { - final Uid uid = ratioNumerators.keyAt(i); + final Uid uid = getAvailableUidStatsLocked(ratioNumerators.keyAt(i)); final double ratioNumerator = ratioNumerators.valueAt(i); final long uidActualUC = (long) (totalConsumedChargeUC * ratioNumerator / ratioDenominator + 0.5); @@ -12546,15 +12547,79 @@ public class BatteryStatsImpl extends BatteryStats { } } - /** Adds the summand to the value stored in uidMap for the given uid. */ - // TODO(b/182845832): Use some sort of "SparseDoubleArray" instead of ArrayMap<Uid, Double>. - private static void addDoubleToUidMap(ArrayMap<Uid, Double> uidMap, Uid uid, double summand) { - if (uidMap == null) return; - final Double oldVal = uidMap.get(uid); - if (oldVal != null) { - summand += oldVal; + /** + * SparseDoubleArray map integers to doubles. + * Its implementation is the same as that of {@link SparseLongArray}; see there for details. + * + * @see SparseLongArray + */ + private static class SparseDoubleArray { + /** + * The int->double map, but storing the doubles as longs using + * {@link Double.doubleToRawLongBits(double)}. + */ + private final SparseLongArray mValues = new SparseLongArray(); + + /** + * Gets the double mapped from the specified key, or <code>0</code> + * if no such mapping has been made. + */ + public double get(int key) { + if (mValues.indexOfKey(key) >= 0) { + return Double.longBitsToDouble(mValues.get(key)); + } + return 0; + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(int key, double value) { + mValues.put(key, Double.doubleToRawLongBits(value)); + } + + /** + * Adds a mapping from the specified key to the specified value, + * <b>adding</b> to the previous mapping from the specified key if there + * was one. + */ + public void add(int key, double summand) { + final double oldValue = get(key); + put(key, oldValue + summand); } - uidMap.put(uid, summand); + + /** + * Returns the number of key-value mappings that this SparseDoubleArray + * currently stores. + */ + public int size() { + return mValues.size(); + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the key from the <code>index</code>th key-value mapping that this + * SparseDoubleArray stores. + * + * @see SparseLongArray#keyAt(int) + */ + public int keyAt(int index) { + return mValues.keyAt(index); + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the value from the <code>index</code>th key-value mapping that this + * SparseDoubleArray stores. + * + * @see SparseLongArray#valueAt(int) + */ + public double valueAt(int index) { + return Double.longBitsToDouble(mValues.valueAt(index)); + } + } /** diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 47b0f8c2be0c..2458fe3dfb8c 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -41,7 +41,6 @@ import android.os.UserHandle; import android.os.ZygoteProcess; import android.os.storage.StorageManager; import android.provider.DeviceConfig; -import android.security.keystore.AndroidKeyStoreProvider; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; @@ -74,7 +73,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.security.Provider; import java.security.Security; -import java.util.Optional; /** * Startup class for the zygote process. @@ -227,17 +225,7 @@ public class ZygoteInit { // AndroidKeyStoreProvider.install() manipulates the list of JCA providers to insert // preferred providers. Note this is not done via security.properties as the JCA providers // are not on the classpath in the case of, for example, raw dalvikvm runtimes. - // TODO b/171305684 This code is used to conditionally enable the installation of the - // Keystore 2.0 provider to enable teams adjusting to Keystore 2.0 at their own - // pace. This code will be removed when all calling code was adjusted to - // Keystore 2.0. - Optional<Boolean> keystore2_enabled = - android.sysprop.Keystore2Properties.keystore2_enabled(); - if (keystore2_enabled.isPresent() && keystore2_enabled.get()) { - android.security.keystore2.AndroidKeyStoreProvider.install(); - } else { - AndroidKeyStoreProvider.install(); - } + android.security.keystore2.AndroidKeyStoreProvider.install(); Log.i(TAG, "Installed AndroidKeyStoreProvider in " + (SystemClock.uptimeMillis() - startTime) + "ms."); Trace.traceEnd(Trace.TRACE_TAG_DALVIK); diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index fea07519fb4d..f19a12340873 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.graphics.Rect; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; +import android.hardware.fingerprint.IUdfpsHbmListener; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.service.notification.StatusBarNotification; @@ -156,6 +157,11 @@ oneway interface IStatusBar void hideAuthenticationDialog(); /** + * Sets an instance of IUdfpsHbmListener for UdfpsController. + */ + void setUdfpsHbmListener(in IUdfpsHbmListener listener); + + /** * Notifies System UI that the display is ready to show system decorations. */ void onDisplayReady(int displayId); diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 2e25ea3601da..c8a91d8cee10 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.graphics.Rect; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; +import android.hardware.fingerprint.IUdfpsHbmListener; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; @@ -121,6 +122,11 @@ interface IStatusBarService void hideAuthenticationDialog(); /** + * Sets an instance of IUdfpsHbmListener for UdfpsController. + */ + void setUdfpsHbmListener(in IUdfpsHbmListener listener); + + /** * Show a warning that the device is about to go to sleep due to user inactivity. */ void showInattentiveSleepWarning(); diff --git a/core/java/com/android/internal/view/inline/InlineTooltipUi.java b/core/java/com/android/internal/view/inline/InlineTooltipUi.java new file mode 100644 index 000000000000..5ec8b30d6a7b --- /dev/null +++ b/core/java/com/android/internal/view/inline/InlineTooltipUi.java @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.view.inline; + +import static android.view.autofill.Helper.sVerbose; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.ContextWrapper; +import android.graphics.drawable.Drawable; +import android.transition.Transition; +import android.util.Slog; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.inline.InlineContentView; + +import java.io.PrintWriter; + +/** + * UI container for the inline suggestion tooltip. + */ +public final class InlineTooltipUi extends PopupWindow implements AutoCloseable { + private static final String TAG = "InlineTooltipUi"; + + private final WindowManager mWm; + private final ViewGroup mContentContainer; + + private boolean mShowing; + + private WindowManager.LayoutParams mWindowLayoutParams; + + private final View.OnAttachStateChangeListener mAnchorOnAttachStateChangeListener = + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + /* ignore - handled by the super class */ + } + + @Override + public void onViewDetachedFromWindow(View v) { + dismiss(); + } + }; + + private final View.OnLayoutChangeListener mAnchoredOnLayoutChangeListener = + new View.OnLayoutChangeListener() { + int mHeight; + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + if (mHeight != bottom - top) { + mHeight = bottom - top; + adjustPosition(); + } + } + }; + + public InlineTooltipUi(@NonNull Context context) { + mContentContainer = new LinearLayout(new ContextWrapper(context)); + mWm = context.getSystemService(WindowManager.class); + + setTouchModal(false); + setOutsideTouchable(true); + setInputMethodMode(INPUT_METHOD_NOT_NEEDED); + setFocusable(false); + } + + /** + * Sets the content view for inline suggestions tooltip + * @param v the content view of {@link android.widget.inline.InlineContentView} + */ + public void setTooltipView(@NonNull InlineContentView v) { + mContentContainer.removeAllViews(); + mContentContainer.addView(v); + mContentContainer.setVisibility(View.VISIBLE); + } + + @Override + public void close() { + hide(); + } + + @Override + protected boolean hasContentView() { + return true; + } + + @Override + protected boolean hasDecorView() { + return true; + } + + @Override + protected WindowManager.LayoutParams getDecorViewLayoutParams() { + return mWindowLayoutParams; + } + + /** + * The effective {@code update} method that should be called by its clients. + */ + public void update(View anchor) { + // set to the application type with the highest z-order + setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL); + + // The first time to show up, the height of tooltip is zero, + // so set the offset Y to 2 * anchor height. + final int achoredHeight = mContentContainer.getHeight(); + final int offsetY = (achoredHeight == 0) + ? -anchor.getHeight() << 1 : -anchor.getHeight() - achoredHeight; + if (!isShowing()) { + setWidth(WindowManager.LayoutParams.WRAP_CONTENT); + setHeight(WindowManager.LayoutParams.WRAP_CONTENT); + showAsDropDown(anchor, 0 , offsetY, Gravity.TOP | Gravity.CENTER_HORIZONTAL); + } else { + update(anchor, 0 , offsetY, WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT); + } + } + + @Override + protected void update(View anchor, WindowManager.LayoutParams params) { + // update content view for the anchor is scrolling + if (anchor.isVisibleToUser()) { + show(params); + } else { + hide(); + } + } + + @Override + public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) { + if (isShowing()) { + return; + } + + setShowing(true); + setDropDown(true); + attachToAnchor(anchor, xoff, yoff, gravity); + final WindowManager.LayoutParams p = mWindowLayoutParams = createPopupLayoutParams( + anchor.getWindowToken()); + final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, + p.width, p.height, gravity, getAllowScrollingAnchorParent()); + updateAboveAnchor(aboveAnchor); + p.accessibilityIdOfAnchor = anchor.getAccessibilityViewId(); + p.packageName = anchor.getContext().getPackageName(); + show(p); + } + + @Override + protected void attachToAnchor(View anchor, int xoff, int yoff, int gravity) { + super.attachToAnchor(anchor, xoff, yoff, gravity); + anchor.addOnAttachStateChangeListener(mAnchorOnAttachStateChangeListener); + } + + @Override + protected void detachFromAnchor() { + final View anchor = getAnchor(); + if (anchor != null) { + anchor.removeOnAttachStateChangeListener(mAnchorOnAttachStateChangeListener); + } + super.detachFromAnchor(); + } + + @Override + public void dismiss() { + if (!isShowing() || isTransitioningToDismiss()) { + return; + } + + setShowing(false); + setTransitioningToDismiss(true); + + hide(); + detachFromAnchor(); + if (getOnDismissListener() != null) { + getOnDismissListener().onDismiss(); + } + } + + private void adjustPosition() { + View anchor = getAnchor(); + if (anchor == null) return; + update(anchor); + } + + private void show(WindowManager.LayoutParams params) { + if (sVerbose) { + Slog.v(TAG, "show()"); + } + mWindowLayoutParams = params; + + try { + params.packageName = "android"; + params.setTitle("Autofill Inline Tooltip"); // Title is set for debugging purposes + if (!mShowing) { + params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mContentContainer.addOnLayoutChangeListener(mAnchoredOnLayoutChangeListener); + mWm.addView(mContentContainer, params); + mShowing = true; + } else { + mWm.updateViewLayout(mContentContainer, params); + } + } catch (WindowManager.BadTokenException e) { + Slog.d(TAG, "Failed with token " + params.token + " gone."); + } catch (IllegalStateException e) { + // WM throws an ISE if mContentView was added twice; this should never happen - + // since show() and hide() are always called in the UIThread - but when it does, + // it should not crash the system. + Slog.wtf(TAG, "Exception showing window " + params, e); + } + } + + private void hide() { + if (sVerbose) { + Slog.v(TAG, "hide()"); + } + try { + if (mShowing) { + mContentContainer.removeOnLayoutChangeListener(mAnchoredOnLayoutChangeListener); + mWm.removeView(mContentContainer); + mShowing = false; + } + } catch (IllegalStateException e) { + // WM might thrown an ISE when removing the mContentView; this should never + // happen - since show() and hide() are always called in the UIThread - but if it + // does, it should not crash the system. + Slog.e(TAG, "Exception hiding window ", e); + } + } + + @Override + public int getAnimationStyle() { + throw new IllegalStateException("You can't call this!"); + } + + @Override + public Drawable getBackground() { + throw new IllegalStateException("You can't call this!"); + } + + @Override + public View getContentView() { + throw new IllegalStateException("You can't call this!"); + } + + @Override + public float getElevation() { + throw new IllegalStateException("You can't call this!"); + } + + @Override + public Transition getEnterTransition() { + throw new IllegalStateException("You can't call this!"); + } + + @Override + public Transition getExitTransition() { + throw new IllegalStateException("You can't call this!"); + } + + @Override + public void setBackgroundDrawable(Drawable background) { + throw new IllegalStateException("You can't call this!"); + } + + @Override + public void setContentView(View contentView) { + if (contentView != null) { + throw new IllegalStateException("You can't call this!"); + } + } + + @Override + public void setElevation(float elevation) { + throw new IllegalStateException("You can't call this!"); + } + + @Override + public void setEnterTransition(Transition enterTransition) { + throw new IllegalStateException("You can't call this!"); + } + + @Override + public void setExitTransition(Transition exitTransition) { + throw new IllegalStateException("You can't call this!"); + } + + @Override + public void setTouchInterceptor(View.OnTouchListener l) { + throw new IllegalStateException("You can't call this!"); + } + + /** + * Dumps status + */ + public void dump(@NonNull PrintWriter pw, @Nullable String prefix) { + + pw.print(prefix); + + if (mContentContainer != null) { + pw.print(prefix); pw.print("Window: "); + final String prefix2 = prefix + " "; + pw.println(); + pw.print(prefix2); pw.print("showing: "); pw.println(mShowing); + pw.print(prefix2); pw.print("view: "); pw.println(mContentContainer); + if (mWindowLayoutParams != null) { + pw.print(prefix2); pw.print("params: "); pw.println(mWindowLayoutParams); + } + pw.print(prefix2); pw.print("screen coordinates: "); + if (mContentContainer == null) { + pw.println("N/A"); + } else { + final int[] coordinates = mContentContainer.getLocationOnScreen(); + pw.print(coordinates[0]); pw.print("x"); pw.println(coordinates[1]); + } + } + } +} diff --git a/core/proto/android/net/networkcapabilities.proto b/core/proto/android/net/networkcapabilities.proto new file mode 100644 index 000000000000..edb6c0400062 --- /dev/null +++ b/core/proto/android/net/networkcapabilities.proto @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2017 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. + */ + +syntax = "proto2"; + +package android.net; + +option java_multiple_files = true; + +import "frameworks/base/core/proto/android/privacy.proto"; +import "frameworks/proto_logging/stats/enums/net/enums.proto"; + +/** + * An android.net.NetworkCapabilities object. + */ +message NetworkCapabilitiesProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + repeated Transport transports = 1; + + enum NetCapability { + // Indicates this is a network that has the ability to reach the + // carrier's MMSC for sending and receiving MMS messages. + NET_CAPABILITY_MMS = 0; + // Indicates this is a network that has the ability to reach the + // carrier's SUPL server, used to retrieve GPS information. + NET_CAPABILITY_SUPL = 1; + // Indicates this is a network that has the ability to reach the + // carrier's DUN or tethering gateway. + NET_CAPABILITY_DUN = 2; + // Indicates this is a network that has the ability to reach the + // carrier's FOTA portal, used for over the air updates. + NET_CAPABILITY_FOTA = 3; + // Indicates this is a network that has the ability to reach the + // carrier's IMS servers, used for network registration and signaling. + NET_CAPABILITY_IMS = 4; + // Indicates this is a network that has the ability to reach the + // carrier's CBS servers, used for carrier specific services. + NET_CAPABILITY_CBS = 5; + // Indicates this is a network that has the ability to reach a Wi-Fi + // direct peer. + NET_CAPABILITY_WIFI_P2P = 6; + // Indicates this is a network that has the ability to reach a carrier's + // Initial Attach servers. + NET_CAPABILITY_IA = 7; + // Indicates this is a network that has the ability to reach a carrier's + // RCS servers, used for Rich Communication Services. + NET_CAPABILITY_RCS = 8; + // Indicates this is a network that has the ability to reach a carrier's + // XCAP servers, used for configuration and control. + NET_CAPABILITY_XCAP = 9; + // Indicates this is a network that has the ability to reach a carrier's + // Emergency IMS servers or other services, used for network signaling + // during emergency calls. + NET_CAPABILITY_EIMS = 10; + // Indicates that this network is unmetered. + NET_CAPABILITY_NOT_METERED = 11; + // Indicates that this network should be able to reach the internet. + NET_CAPABILITY_INTERNET = 12; + // Indicates that this network is available for general use. If this is + // not set applications should not attempt to communicate on this + // network. Note that this is simply informative and not enforcement - + // enforcement is handled via other means. Set by default. + NET_CAPABILITY_NOT_RESTRICTED = 13; + // Indicates that the user has indicated implicit trust of this network. + // This generally means it's a sim-selected carrier, a plugged in + // ethernet, a paired BT device or a wifi the user asked to connect to. + // Untrusted networks are probably limited to unknown wifi AP. Set by + // default. + NET_CAPABILITY_TRUSTED = 14; + // Indicates that this network is not a VPN. This capability is set by + // default and should be explicitly cleared for VPN networks. + NET_CAPABILITY_NOT_VPN = 15; + // Indicates that connectivity on this network was successfully + // validated. For example, for a network with NET_CAPABILITY_INTERNET, + // it means that Internet connectivity was successfully detected. + NET_CAPABILITY_VALIDATED = 16; + // Indicates that this network was found to have a captive portal in + // place last time it was probed. + NET_CAPABILITY_CAPTIVE_PORTAL = 17; + // Indicates that this network is not roaming. + NET_CAPABILITY_NOT_ROAMING = 18; + // Indicates that this network is available for use by apps, and not a + // network that is being kept up in the background to facilitate fast + // network switching. + NET_CAPABILITY_FOREGROUND = 19; + } + repeated NetCapability capabilities = 2; + + // Passive link bandwidth. This is a rough guide of the expected peak + // bandwidth for the first hop on the given transport. It is not measured, + // but may take into account link parameters (Radio technology, allocated + // channels, etc). + optional int32 link_up_bandwidth_kbps = 3; + optional int32 link_down_bandwidth_kbps = 4; + + optional string network_specifier = 5 [ (.android.privacy).dest = DEST_EXPLICIT ]; + + // True if this object specifies a signal strength. + optional bool can_report_signal_strength = 6; + // This is a signed integer, and higher values indicate better signal. The + // exact units are bearer-dependent. For example, Wi-Fi uses RSSI. + // Only valid if can_report_signal_strength is true. + optional sint32 signal_strength = 7; +} diff --git a/core/proto/android/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto index 0041f199b448..57b9f7162e67 100644 --- a/core/proto/android/net/networkrequest.proto +++ b/core/proto/android/net/networkrequest.proto @@ -20,8 +20,8 @@ package android.net; option java_multiple_files = true; +import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; import "frameworks/base/core/proto/android/privacy.proto"; -import "frameworks/proto_logging/stats/enums/net/networkcapabilities.proto"; /** * An android.net.NetworkRequest object. diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index a5fbae9878ac..998ec96f6f23 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -55,13 +55,13 @@ import "frameworks/base/core/proto/android/service/print.proto"; import "frameworks/base/core/proto/android/service/procstats.proto"; import "frameworks/base/core/proto/android/service/restricted_image.proto"; import "frameworks/base/core/proto/android/service/sensor_service.proto"; +import "frameworks/base/core/proto/android/service/usb.proto"; import "frameworks/base/core/proto/android/util/event_log_tags.proto"; import "frameworks/base/core/proto/android/util/log.proto"; import "frameworks/base/core/proto/android/util/textdump.proto"; import "frameworks/base/core/proto/android/privacy.proto"; import "frameworks/base/core/proto/android/section.proto"; import "frameworks/base/proto/src/ipconnectivity.proto"; -import "frameworks/proto_logging/stats/enums/service/usb.proto"; import "packages/modules/Permission/service/proto/com/android/role/roleservice.proto"; package android.os; diff --git a/core/proto/android/server/biometrics.proto b/core/proto/android/server/biometrics.proto index 900235ea65fb..bbb0edd4fd59 100644 --- a/core/proto/android/server/biometrics.proto +++ b/core/proto/android/server/biometrics.proto @@ -125,6 +125,13 @@ message SensorStateProto { // User states for this sensor. repeated UserStateProto user_states = 4; + + // True if resetLockout requires a HAT to be verified in the TEE or equivalent. + optional bool reset_lockout_requires_hardware_auth_token = 5; + + // True if a HAT is required (field above) AND a challenge needs to be generated by the + // biometric TEE (or equivalent), and wrapped within the HAT. + optional bool reset_lockout_requires_challenge = 6; } // State of a specific user for a specific sensor. diff --git a/core/proto/android/service/enums.proto b/core/proto/android/service/enums.proto deleted file mode 100644 index b64e685104b7..000000000000 --- a/core/proto/android/service/enums.proto +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2018 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. - */ - -syntax = "proto2"; -package android.service; - -option java_outer_classname = "ServiceProtoEnums"; -option java_multiple_files = true; - -enum UsbEndPointType { - USB_ENDPOINT_TYPE_XFER_CONTROL = 0; - USB_ENDPOINT_TYPE_XFER_ISOC = 1; - USB_ENDPOINT_TYPE_XFER_BULK = 2; - USB_ENDPOINT_TYPE_XFER_INT = 3; -} - -enum UsbEndPointDirection { - USB_ENDPOINT_DIR_OUT = 0; - USB_ENDPOINT_DIR_IN = 0x80; -} - -enum UsbConnectionRecordMode { - USB_CONNECTION_RECORD_MODE_CONNECT = 0; - USB_CONNECTION_RECORD_MODE_CONNECT_BADPARSE = 1; - USB_CONNECTION_RECORD_MODE_CONNECT_BADDEVICE = 2; - USB_CONNECTION_RECORD_MODE_DISCONNECT = -1; -}
\ No newline at end of file diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto new file mode 100644 index 000000000000..45f8c132fbf0 --- /dev/null +++ b/core/proto/android/service/usb.proto @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2018 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. + */ + +syntax = "proto2"; +package android.service.usb; + +option java_multiple_files = true; +option java_outer_classname = "UsbServiceProto"; + +import "frameworks/base/core/proto/android/content/component_name.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; +import "frameworks/proto_logging/stats/enums/service/enums.proto"; + +message UsbServiceDumpProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional UsbDeviceManagerProto device_manager = 1; + optional UsbHostManagerProto host_manager = 2; + optional UsbPortManagerProto port_manager = 3; + optional UsbAlsaManagerProto alsa_manager = 4; + optional UsbSettingsManagerProto settings_manager = 5; + optional UsbPermissionsManagerProto permissions_manager = 6; +} + +message UsbDeviceManagerProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional UsbHandlerProto handler = 1; + optional UsbDebuggingManagerProto debugging_manager = 2; +} + +message UsbHandlerProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + /* Same as android.hardware.usb.gadget.V1_0.GadgetFunction.* */ + enum Function { + FUNCTION_ADB = 1; + FUNCTION_ACCESSORY = 2; + FUNCTION_MTP = 4; + FUNCTION_MIDI = 8; + FUNCTION_PTP = 16; + FUNCTION_RNDIS = 32; + FUNCTION_AUDIO_SOURCE = 64; + } + + repeated Function current_functions = 1; + optional bool current_functions_applied = 2; + repeated Function screen_unlocked_functions = 3; + optional bool screen_locked = 4; + optional bool connected = 5; + optional bool configured = 6; + optional UsbAccessoryProto current_accessory = 7; + optional bool host_connected = 8; + optional bool source_power = 9; + optional bool sink_power = 10; + optional bool usb_charging = 11; + optional bool hide_usb_notification = 12; + optional bool audio_accessory_connected = 13; + optional bool adb_enabled = 14; + optional string kernel_state = 15; + optional string kernel_function_list = 16; +} + +message UsbAccessoryProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional string manufacturer = 1; + optional string model = 2; + // For "classical" USB-accessories the manufacturer bakes this into the + // firmware of the device. If an Android phone is configured as accessory, the + // app that sets up the accessory side of the connection set this. Either way, + // these are part of the detection protocol, and so they cannot be user set or + // unique. + optional string description = 3; + optional string version = 4; + optional string uri = 5 [ (android.privacy).dest = DEST_EXPLICIT ]; + // Non-resettable hardware ID. + optional string serial = 6 [ (android.privacy).dest = DEST_LOCAL ]; +} + +message UsbDebuggingManagerProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional bool connected_to_adb = 1; + // A workstation that connects to the phone for debugging is identified by + // this key. + optional string last_key_received = 2 [ (android.privacy).dest = DEST_EXPLICIT ]; + optional string user_keys = 3 [ (android.privacy).dest = DEST_LOCAL ]; + optional string system_keys = 4 [ (android.privacy).dest = DEST_LOCAL ]; +} + +message UsbHostManagerProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional android.content.ComponentNameProto default_usb_host_connection_handler = 1; + repeated UsbDeviceProto devices = 2; + optional int32 num_connects = 3; + repeated UsbConnectionRecordProto connections = 4; +} + +message UsbDeviceProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + // Generic USB name, not user-provided. + optional string name = 1; + // ID specific to the vendor, not the device. + optional int32 vendor_id = 2; + // ID of this product type: Each vendor gives each product a unique ID. E.g. + // all mice of the same model would have the same ID. + optional int32 product_id = 3; + optional int32 class = 4; + optional int32 subclass = 5; + optional int32 protocol = 6; + optional string manufacturer_name = 7; + optional string product_name = 8; + optional string version = 9; + // Non-resettable hardware ID. + optional string serial_number = 10 [ (android.privacy).dest = DEST_LOCAL ]; + repeated UsbConfigurationProto configurations = 11; +} + +message UsbConfigurationProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + // A single USB device can have several configurations and the app accessing + // the USB device can switch between them. At any time only one can be active. + // Each configuration can present completely different interfaces end + // endpoints, i.e. a completely different behavior. + optional int32 id = 1; + // Hardware-defined name, not set by the user. + optional string name = 2; + optional uint32 attributes = 3; + optional int32 max_power = 4; + repeated UsbInterfaceProto interfaces = 5; +} + +message UsbInterfaceProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + // Hardware defined. This is the id used by the app to identify the interface. + optional int32 id = 1; + optional int32 alternate_settings = 2; + optional string name = 3; + optional int32 class = 4; + optional int32 subclass = 5; + optional int32 protocol = 6; + repeated UsbEndPointProto endpoints = 7; +} + +message UsbEndPointProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 endpoint_number = 1; + optional android.service.UsbEndPointDirection direction = 2; + // The address of the endpoint. Needed to read and write to the endpoint. + optional int32 address = 3; + optional android.service.UsbEndPointType type = 4; + optional uint32 attributes = 5; + optional int32 max_packet_size = 6; + optional int32 interval = 7; +} + +message UsbConnectionRecordProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + // usb device's address, e.g. 001/002, nothing about the phone + optional string device_address = 1; + optional android.service.UsbConnectionRecordMode mode = 2; + optional int64 timestamp = 3; + optional int32 manufacturer = 4; + optional int32 product = 5; + optional UsbIsHeadsetProto is_headset = 6; +} + +message UsbIsHeadsetProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional bool in = 1; + optional bool out = 2; +} + +message UsbPortManagerProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional bool is_simulation_active = 1; + repeated UsbPortInfoProto usb_ports = 2; + optional bool enable_usb_data_signaling = 3; +} + +message UsbPortInfoProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional UsbPortProto port = 1; + optional UsbPortStatusProto status = 2; + optional bool can_change_mode = 3; + optional bool can_change_power_role = 4; + optional bool can_change_data_role = 5; + optional int64 connected_at_millis = 6; + optional int64 last_connect_duration_millis = 7; +} + +message UsbPortProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + /* Same as android.hardware.usb.V1_1.Constants.PortMode_1_1 */ + enum Mode { + MODE_NONE = 0; + MODE_UFP = 1; + MODE_DFP = 2; + MODE_DRP = 3; + MODE_AUDIO_ACCESSORY = 4; + MODE_DEBUG_ACCESSORY = 8; + } + + // ID of the port. A device (eg: Chromebooks) might have multiple ports. + optional string id = 1; + repeated Mode supported_modes = 2; +} + +message UsbPortStatusProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + /* Same as android.hardware.usb.V1_0.Constants.PortPowerRole */ + enum PowerRole { + POWER_ROLE_NONE = 0; + POWER_ROLE_SOURCE = 1; + POWER_ROLE_SINK = 2; + } + + /* Same as android.hardware.usb.V1_0.Constants.PortDataRole */ + enum DataRole { + DATA_ROLE_NONE = 0; + DATA_ROLE_HOST = 1; + DATA_ROLE_DEVICE = 2; + } + + optional bool connected = 1; + optional UsbPortProto.Mode current_mode = 2; + optional PowerRole power_role = 3; + optional DataRole data_role = 4; + repeated UsbPortStatusRoleCombinationProto role_combinations = 5; + optional android.service.ContaminantPresenceStatus contaminant_presence_status = 6; +} + +message UsbPortStatusRoleCombinationProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional UsbPortStatusProto.PowerRole power_role = 1; + optional UsbPortStatusProto.DataRole data_role = 2; +} + +message UsbAlsaManagerProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 cards_parser = 1; + repeated UsbAlsaDeviceProto alsa_devices = 2; + repeated UsbMidiDeviceProto midi_devices = 3; +} + +message UsbAlsaDeviceProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 card = 1; + optional int32 device = 2; + optional string name = 3; + optional bool has_playback = 4; + optional bool has_capture = 5; + // usb device's address, e.g. 001/002, nothing about the phone + optional string address = 6; +} + +message UsbMidiDeviceProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 card = 1; + optional int32 device = 2; + // usb device's address, e.g. 001/002, nothing about the phone + optional string device_address = 3; +} + +message UsbSettingsManagerProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + repeated UsbUserSettingsManagerProto user_settings = 1; + repeated UsbProfileGroupSettingsManagerProto profile_group_settings = 2; +} + +message UsbUserSettingsManagerProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 user_id = 1; + reserved 2; // previously device_permissions, now unused + reserved 3; // previously accessory_permissions, now unused + repeated UsbDeviceAttachedActivities device_attached_activities = 4; + repeated UsbAccessoryAttachedActivities accessory_attached_activities = 5; +} + +message UsbProfileGroupSettingsManagerProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + // The user id of the personal profile if the device has a work profile. + optional int32 parent_user_id = 1; + repeated UsbSettingsDevicePreferenceProto device_preferences = 2; + repeated UsbSettingsAccessoryPreferenceProto accessory_preferences = 3; +} + +message UsbSettingsDevicePreferenceProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional UsbDeviceFilterProto filter = 1; + optional UserPackageProto user_package = 2; +} + +message UsbPermissionsManagerProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + repeated UsbUserPermissionsManagerProto user_permissions = 1; +} + +message UsbUserPermissionsManagerProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 user_id = 1; + + repeated UsbDevicePermissionProto device_permissions = 2; + repeated UsbAccessoryPermissionProto accessory_permissions = 3; + + repeated UsbDevicePersistentPermissionProto device_persistent_permissions = 4; + repeated UsbAccessoryPersistentPermissionProto accessory_persistent_permissions = 5; +} + +message UsbDevicePermissionProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + // Name of device set by manufacturer + // All devices of the same model have the same name + optional string device_name = 1; + repeated int32 uids = 2; +} + +message UsbAccessoryPermissionProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + // Description of accessory set by manufacturer + // All accessories of the same model have the same description + optional string accessory_description = 1; + repeated int32 uids = 2; +} + +message UsbDevicePersistentPermissionProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional UsbDeviceFilterProto device_filter = 1; + repeated UsbUidPermissionProto permission_values = 2; +} + +message UsbAccessoryPersistentPermissionProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional UsbAccessoryFilterProto accessory_filter = 1; + repeated UsbUidPermissionProto permission_values = 2; +} + +message UsbUidPermissionProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 uid = 1; + optional bool is_granted = 2; +} + +message UsbDeviceFilterProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + // Mirrors the vendor_id of UsbDeviceProto. + optional int32 vendor_id = 1; + optional int32 product_id = 2; + optional int32 class = 3; + optional int32 subclass = 4; + optional int32 protocol = 5; + optional string manufacturer_name = 6; + optional string product_name = 7; + optional string serial_number = 8 [ (android.privacy).dest = DEST_EXPLICIT ]; +} + +message UserPackageProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 user_id = 1; + optional string package_name =2; +} + +message UsbSettingsAccessoryPreferenceProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional UsbAccessoryFilterProto filter = 1; + optional UserPackageProto user_package = 2; +} + +message UsbAccessoryFilterProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional string manufacturer = 1; + optional string model = 2; + optional string version = 3; +} + +message UsbDeviceAttachedActivities { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional android.content.ComponentNameProto activity = 1; + repeated UsbDeviceFilterProto filters = 2; +} + +message UsbAccessoryAttachedActivities { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional android.content.ComponentNameProto activity = 1; + repeated UsbAccessoryFilterProto filters = 2; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 521d246dc0dc..da750de3221e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -694,6 +694,7 @@ <!-- Added in S --> <protected-broadcast android:name="android.intent.action.REBOOT_READY" /> + <protected-broadcast android:name="android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED" /> <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> @@ -3707,6 +3708,13 @@ <permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to manage hotword detection on the device. + <p>Protection level: internal|preinstalled + @hide This is not a third-party API (intended for OEMs and system apps). + --> + <permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" + android:protectionLevel="internal|preinstalled" /> + <!-- Must be required by a {@link android.service.autofill.AutofillService}, to ensure that only the system can bind to it. <p>Protection level: signature diff --git a/core/res/res/drawable/ic_accessibility_24dp.xml b/core/res/res/drawable/ic_accessibility_24dp.xml new file mode 100644 index 000000000000..51e695969c85 --- /dev/null +++ b/core/res/res/drawable/ic_accessibility_24dp.xml @@ -0,0 +1,27 @@ +<?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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="21dp" + android:height="21dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M20.5,6c-2.61,0.7 -5.67,1 -8.5,1S6.11,6.7 3.5,6L3,8c1.86,0.5 4,0.83 6, + 1v13h2v-6h2v6h2V9c2,-0.17 4.14,-0.5 6,-1L20.5,6zM12,6c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2s-2, + 0.9 -2,2S10.9,6 12,6z" + android:fillColor="#FF000000"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 62278d5b86f1..9e1a08527330 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2194,6 +2194,7 @@ Note that even if no splashscreen content is set on the theme, the system may still show a splash screen using the other attributes on the theme, like the {@link android.R.attr#windowBackground}. + {@deprecated Use windowSplashscreenAnimatedIcon instead.} --> <attr name="windowSplashscreenContent" format="reference" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 0979ab556432..ba21679df2fe 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2816,6 +2816,7 @@ <public type="attr" name="iconSpaceReserved" id="0x01010561"/> <public type="attr" name="defaultFocusHighlightEnabled" id="0x01010562" /> <public type="attr" name="persistentWhenFeatureAvailable" id="0x01010563"/> + <!-- {@deprecated Use windowSplashscreenAnimatedIcon instead } --> <public type="attr" name="windowSplashscreenContent" id="0x01010564" /> <!-- @hide @SystemApi --> <public type="attr" name="requiredSystemPropertyName" id="0x01010565" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 054d1080f4d4..0228dfd45972 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -743,6 +743,10 @@ magnification. [CHAR_LIMIT=NONE]--> <string name="notification_channel_accessibility_magnification">Magnification</string> + <!-- Text shown when viewing channel settings for notifications related to accessibility + security policy. [CHAR_LIMIT=NONE]--> + <string name="notification_channel_accessibility_security_policy">Accessibility security policy</string> + <!-- Label for foreground service notification when one app is running. [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6826789589341671842] --> <string name="foreground_service_app_in_background"><xliff:g id="app_name">%1$s</xliff:g> is @@ -5910,4 +5914,9 @@ ul.</string> <string name="splash_screen_view_icon_description">Application icon</string> <!-- Content description for the branding image on the splash screen. [CHAR LIMIT=50] --> <string name="splash_screen_view_branding_description">Application branding image</string> + + <!-- Notification title to prompt the user that some accessibility service has view and control access. [CHAR LIMIT=50] --> + <string name="view_and_control_notification_title">Check access settings</string> + <!-- Notification content to prompt the user that some accessibility service has view and control access. [CHAR LIMIT=none] --> + <string name="view_and_control_notification_content"><xliff:g id="service_name" example="TalkBack">%s</xliff:g> can view and control your screen. Tap to review.</string> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ef5191af2e6b..567feee31673 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3541,6 +3541,7 @@ <java-symbol type="string" name="notification_channel_system_changes" /> <java-symbol type="string" name="notification_channel_do_not_disturb" /> <java-symbol type="string" name="notification_channel_accessibility_magnification" /> + <java-symbol type="string" name="notification_channel_accessibility_security_policy" /> <java-symbol type="string" name="config_defaultAutofillService" /> <java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" /> <java-symbol type="string" name="config_defaultTextClassifierPackage" /> @@ -4323,4 +4324,9 @@ <java-symbol type="id" name="remote_views_next_child" /> <java-symbol type="id" name="remote_views_stable_id" /> <java-symbol type="id" name="remote_views_override_id" /> + + <!-- View and control prompt --> + <java-symbol type="drawable" name="ic_accessibility_24dp" /> + <java-symbol type="string" name="view_and_control_notification_title" /> + <java-symbol type="string" name="view_and_control_notification_content" /> </resources> diff --git a/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java b/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java new file mode 100644 index 000000000000..85073b0ecfdd --- /dev/null +++ b/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + + +import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; +import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; + +import android.os.UserHandle; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class TimeCapabilitiesTest { + + private static final UserHandle USER_HANDLE = UserHandle.of(332211); + + @Test + public void testBuilder() { + TimeCapabilities capabilities = new TimeCapabilities.Builder(USER_HANDLE) + .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_NOT_APPLICABLE) + .setSuggestTimeManuallyCapability(CAPABILITY_NOT_SUPPORTED) + .build(); + + assertThat(capabilities.getConfigureAutoTimeDetectionEnabledCapability()) + .isEqualTo(CAPABILITY_NOT_APPLICABLE); + assertThat(capabilities.getSuggestTimeManuallyCapability()) + .isEqualTo(CAPABILITY_NOT_SUPPORTED); + + try { + new TimeCapabilities.Builder(USER_HANDLE) + .build(); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException ignored) { + // expected + } + + try { + new TimeCapabilities.Builder(USER_HANDLE) + .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_NOT_APPLICABLE) + .build(); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException ignored) { + // expected + } + + try { + new TimeCapabilities.Builder(USER_HANDLE) + .setSuggestTimeManuallyCapability(CAPABILITY_NOT_APPLICABLE) + .build(); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException ignored) { + // expected + } + } + + @Test + public void userHandle_notIgnoredInEquals() { + TimeCapabilities firstUserCapabilities = new TimeCapabilities.Builder(UserHandle.of(1)) + .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setSuggestTimeManuallyCapability(CAPABILITY_POSSESSED) + .build(); + + TimeCapabilities secondUserCapabilities = new TimeCapabilities.Builder(UserHandle.of(2)) + .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setSuggestTimeManuallyCapability(CAPABILITY_POSSESSED) + .build(); + + assertThat(firstUserCapabilities).isNotEqualTo(secondUserCapabilities); + } + + @Test + public void testParcelable() { + TimeCapabilities.Builder builder = new TimeCapabilities.Builder(USER_HANDLE) + .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_NOT_SUPPORTED) + .setSuggestTimeManuallyCapability(CAPABILITY_NOT_SUPPORTED); + + assertRoundTripParcelable(builder.build()); + + builder.setSuggestTimeManuallyCapability(CAPABILITY_POSSESSED); + assertRoundTripParcelable(builder.build()); + + builder.setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_POSSESSED); + assertRoundTripParcelable(builder.build()); + } + +} diff --git a/core/tests/coretests/src/android/app/time/TimeConfigurationTest.java b/core/tests/coretests/src/android/app/time/TimeConfigurationTest.java new file mode 100644 index 000000000000..7c7cd12bcb73 --- /dev/null +++ b/core/tests/coretests/src/android/app/time/TimeConfigurationTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class TimeConfigurationTest { + + @Test + public void testBuilder() { + TimeConfiguration first = new TimeConfiguration.Builder() + .setAutoDetectionEnabled(true) + .build(); + + assertThat(first.isAutoDetectionEnabled()).isTrue(); + + TimeConfiguration copyFromBuilderConfiguration = new TimeConfiguration.Builder(first) + .build(); + + assertThat(first).isEqualTo(copyFromBuilderConfiguration); + } + + @Test + public void testParcelable() { + TimeConfiguration.Builder builder = new TimeConfiguration.Builder(); + + assertRoundTripParcelable(builder.setAutoDetectionEnabled(true).build()); + + assertRoundTripParcelable(builder.setAutoDetectionEnabled(false).build()); + } + +} diff --git a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java index 01a25b27baf6..dd93997b00c5 100644 --- a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java +++ b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java @@ -16,8 +16,8 @@ package android.app.time; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; +import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; import static org.junit.Assert.assertEquals; diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java index 7a2e6b75272f..5de55d71c1b4 100644 --- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java @@ -91,7 +91,8 @@ public class ImeInsetsSourceConsumerTest { @Test public void testImeVisibility() { - final InsetsSourceControl ime = new InsetsSourceControl(ITYPE_IME, mLeash, new Point()); + final InsetsSourceControl ime = + new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE); mController.onControlsChanged(new InsetsSourceControl[] { ime }); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { @@ -119,7 +120,8 @@ public class ImeInsetsSourceConsumerTest { mImeConsumer.applyImeVisibility(true /* setVisible */); // set control and verify visibility is applied. - InsetsSourceControl control = new InsetsSourceControl(ITYPE_IME, mLeash, new Point()); + InsetsSourceControl control = + new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE); mController.onControlsChanged(new InsetsSourceControl[] { control }); // IME show animation should be triggered when control becomes available. verify(mController).applyAnimation( @@ -138,7 +140,7 @@ public class ImeInsetsSourceConsumerTest { // set control and verify visibility is applied. InsetsSourceControl control = Mockito.spy( - new InsetsSourceControl(ITYPE_IME, mLeash, new Point())); + new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE)); // Simulate IME source control set this flag when the target has starting window. control.setSkipAnimationOnce(true); mController.onControlsChanged(new InsetsSourceControl[] { control }); diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index 873627eae696..613232fe1dfa 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -94,13 +94,14 @@ public class InsetsAnimationControlImplTest { InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mInsetsState, () -> mMockTransaction, mMockController); topConsumer.setControl( - new InsetsSourceControl(ITYPE_STATUS_BAR, mTopLeash, new Point(0, 0)), + new InsetsSourceControl( + ITYPE_STATUS_BAR, mTopLeash, new Point(0, 0), Insets.of(0, 100, 0, 0)), new int[1], new int[1]); InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(ITYPE_NAVIGATION_BAR, mInsetsState, () -> mMockTransaction, mMockController); navConsumer.setControl(new InsetsSourceControl(ITYPE_NAVIGATION_BAR, mNavLeash, - new Point(400, 0)), new int[1], new int[1]); + new Point(400, 0), Insets.of(0, 0, 100, 0)), new int[1], new int[1]); navConsumer.hide(); SparseArray<InsetsSourceControl> controls = new SparseArray<>(); diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 2770ed820562..ff505c400b09 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -213,7 +213,8 @@ public class InsetsControllerTest { mController.onFrameChanged(new Rect(0, 0, 100, 100)); mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200)); InsetsSourceControl control = - new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()); + new InsetsSourceControl( + ITYPE_STATUS_BAR, mLeash, new Point(), Insets.of(0, 10, 0, 0)); mController.onControlsChanged(new InsetsSourceControl[] { control }); WindowInsetsAnimationControlListener controlListener = mock(WindowInsetsAnimationControlListener.class); @@ -814,7 +815,7 @@ public class InsetsControllerTest { // Simulate binder behavior by copying SurfaceControl. Otherwise, InsetsController will // attempt to release mLeash directly. SurfaceControl copy = new SurfaceControl(mLeash, "InsetsControllerTest.createControl"); - return new InsetsSourceControl(type, copy, new Point()); + return new InsetsSourceControl(type, copy, new Point(), Insets.NONE); } private InsetsSourceControl[] createSingletonControl(@InternalInsetsType int type) { diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index 7efd616c5607..b3aa7e86efd5 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -34,6 +34,7 @@ import static org.mockito.Mockito.verifyZeroInteractions; import android.app.Instrumentation; import android.content.Context; +import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -108,7 +109,8 @@ public class InsetsSourceConsumerTest { }); instrumentation.waitForIdleSync(); - mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()), + mConsumer.setControl( + new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point(), Insets.NONE), new int[1], new int[1]); } @@ -177,7 +179,8 @@ public class InsetsSourceConsumerTest { mConsumer.hide(); verifyZeroInteractions(mMockTransaction); int[] hideTypes = new int[1]; - mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()), + mConsumer.setControl( + new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point(), Insets.NONE), new int[1], hideTypes); assertEquals(statusBars(), hideTypes[0]); assertFalse(mRemoveSurfaceCalled); @@ -194,7 +197,8 @@ public class InsetsSourceConsumerTest { verifyZeroInteractions(mMockTransaction); mRemoveSurfaceCalled = false; int[] hideTypes = new int[1]; - mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()), + mConsumer.setControl( + new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point(), Insets.NONE), new int[1], hideTypes); assertTrue(mRemoveSurfaceCalled); assertEquals(0, hideTypes[0]); diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index 0268953eab42..8991a6117306 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -24,6 +24,14 @@ package { } prebuilt_etc { + name: "allowed_privapp_com.android.carsystemui", + system_ext_specific: true, + sub_dir: "permissions", + src: "com.android.carsystemui.xml", + filename_from_src: true, +} + +prebuilt_etc { name: "allowed_privapp_android.car.cluster.loggingrenderer", sub_dir: "permissions", src: "android.car.cluster.loggingrenderer.xml", diff --git a/data/etc/car/com.android.car.messenger.xml b/data/etc/car/com.android.car.messenger.xml index 16595c30c65c..9e5f339a0afa 100644 --- a/data/etc/car/com.android.car.messenger.xml +++ b/data/etc/car/com.android.car.messenger.xml @@ -16,6 +16,7 @@ --> <permissions> <privapp-permissions package="com.android.car.messenger"> + <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/> </privapp-permissions> </permissions> diff --git a/data/etc/car/com.android.carsystemui.xml b/data/etc/car/com.android.carsystemui.xml new file mode 100644 index 000000000000..4e2f29438531 --- /dev/null +++ b/data/etc/car/com.android.carsystemui.xml @@ -0,0 +1,25 @@ +<?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.systemui"> + <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/> + <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/> + <permission name="android.car.permission.CAR_ENROLL_TRUST"/> + <permission name="android.car.permission.CAR_POWER"/> + <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/platform.xml b/data/etc/platform.xml index b1715991a099..27bf4ef4c84d 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -60,10 +60,6 @@ <group gid="log" /> </permission> - <permission name="android.permission.MANAGE_EXTERNAL_STORAGE" > - <group gid="external_storage" /> - </permission> - <permission name="android.permission.ACCESS_MTP" > <group gid="mtp" /> </permission> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index a7b6636a15de..31cdaeb84077 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -490,6 +490,8 @@ applications that come with the platform <permission name="android.permission.SET_CLIP_SOURCE" /> <!-- Permission required for CTS test - FontManagerTest --> <permission name="android.permission.UPDATE_FONTS" /> + <!-- Permission required for hotword detection service CTS tests --> + <permission name="android.permission.MANAGE_HOTWORD_DETECTION" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 115bd9b08a53..5b57f1901550 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3307,6 +3307,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/TransitionController.java" }, + "1804245629": { + "message": "Attempted to add starting window to token but already cleaned", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "1810019902": { "message": "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps", "level": "DEBUG", @@ -3433,6 +3439,12 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" }, + "1931178855": { + "message": "\tnonApp=%s", + "level": "DEBUG", + "group": "WM_DEBUG_REMOTE_ANIMATIONS", + "at": "com\/android\/server\/wm\/RemoteAnimationController.java" + }, "1947239194": { "message": "Deferring rotation, still finishing previous rotation", "level": "VERBOSE", @@ -3481,6 +3493,12 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1999594750": { + "message": "startAnimation", + "level": "DEBUG", + "group": "WM_DEBUG_REMOTE_ANIMATIONS", + "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java" + }, "2018454757": { "message": "WS.removeImmediately: %s Already removed...", "level": "VERBOSE", diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java index 2ee952cbc5fb..d9d5300e43f9 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java @@ -123,8 +123,9 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor throws InvalidKeyException { resetAll(); - if (!(key instanceof AndroidKeyStorePrivateKey - || key instanceof AndroidKeyStoreSecretKey)) { + // Public key operations get diverted to the default provider. + if (opmode == Cipher.ENCRYPT_MODE + && (key instanceof PrivateKey || key instanceof PublicKey)) { try { mCipher = Cipher.getInstance(getTransform()); String transform = getTransform(); @@ -184,8 +185,9 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { resetAll(); - if (!(key instanceof AndroidKeyStorePrivateKey - || key instanceof AndroidKeyStoreSecretKey)) { + // Public key operations get diverted to the default provider. + if (opmode == Cipher.ENCRYPT_MODE + && (key instanceof PrivateKey || key instanceof PublicKey)) { try { mCipher = Cipher.getInstance(getTransform()); mCipher.init(opmode, key, params, random); @@ -213,8 +215,9 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { resetAll(); - if (!(key instanceof AndroidKeyStorePrivateKey - || key instanceof AndroidKeyStoreSecretKey)) { + // Public key operations get diverted to the default provider. + if (opmode == Cipher.ENCRYPT_MODE + && (key instanceof PrivateKey || key instanceof PublicKey)) { try { mCipher = Cipher.getInstance(getTransform()); mCipher.init(opmode, key, params, random); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index fa852e33a1d8..ba6d22f681ce 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -145,23 +145,15 @@ public class AndroidKeyStoreProvider extends Provider { sInstalled = true; Security.addProvider(new AndroidKeyStoreProvider()); - Security.addProvider( - new android.security.keystore.AndroidKeyStoreProvider( - "AndroidKeyStoreLegacy")); Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider(); - Provider legacyWorkaroundProvider = - new android.security.keystore.AndroidKeyStoreBCWorkaroundProvider( - "AndroidKeyStoreBCWorkaroundLegacy"); if (bcProviderIndex != -1) { // Bouncy Castle provider found -- install the workaround provider above it. // insertProviderAt uses 1-based positions. - Security.insertProviderAt(legacyWorkaroundProvider, bcProviderIndex + 1); Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1); } else { // Bouncy Castle provider not found -- install the workaround provider at lowest // priority. Security.addProvider(workaroundProvider); - Security.addProvider(legacyWorkaroundProvider); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 16ede735660f..e5c9e2567ec5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -151,6 +151,7 @@ public class BubbleStackView extends FrameLayout * starting a new animation. */ private final ShellExecutor mDelayedAnimationExecutor; + private Runnable mDelayedAnimation; /** * Interface to synchronize {@link View} state and the screen. @@ -1865,7 +1866,7 @@ public class BubbleStackView extends FrameLayout mExpandedBubble.getExpandedView().setAlphaAnimating(true); } - mDelayedAnimationExecutor.executeDelayed(() -> { + mDelayedAnimation = () -> { mExpandedViewAlphaAnimator.start(); PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); @@ -1898,7 +1899,8 @@ public class BubbleStackView extends FrameLayout } }) .start(); - }, startDelay); + }; + mDelayedAnimationExecutor.executeDelayed(mDelayedAnimation, startDelay); } private void animateCollapse() { @@ -2097,7 +2099,7 @@ public class BubbleStackView extends FrameLayout * animating flags for those animations. */ private void cancelDelayedExpandCollapseSwitchAnimations() { - mDelayedAnimationExecutor.removeAllCallbacks(); + mDelayedAnimationExecutor.removeCallbacks(mDelayedAnimation); mIsExpansionAnimating = false; mIsBubbleSwitchAnimating = false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java index a4cd3c5a583d..bfee820870f1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java @@ -18,7 +18,6 @@ package com.android.wm.shell.common; import android.annotation.NonNull; import android.os.Handler; -import android.os.Looper; /** Executor implementation which is backed by a Handler. */ public class HandlerExecutor implements ShellExecutor { @@ -47,11 +46,6 @@ public class HandlerExecutor implements ShellExecutor { } @Override - public void removeAllCallbacks() { - mHandler.removeCallbacksAndMessages(null); - } - - @Override public void removeCallbacks(@NonNull Runnable r) { mHandler.removeCallbacks(r); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java index 6abc8f6dda89..f729164ed303 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java @@ -16,16 +16,10 @@ package com.android.wm.shell.common; -import android.os.Looper; -import android.os.SystemClock; -import android.os.Trace; - import java.lang.reflect.Array; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -import java.util.function.BooleanSupplier; -import java.util.function.Predicate; import java.util.function.Supplier; /** @@ -94,11 +88,6 @@ public interface ShellExecutor extends Executor { void executeDelayed(Runnable runnable, long delayMillis); /** - * Removes all pending callbacks. - */ - void removeAllCallbacks(); - - /** * See {@link android.os.Handler#removeCallbacks}. */ void removeCallbacks(Runnable runnable); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 44dad57ea3b8..c1a93ce753a6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -73,6 +73,7 @@ public class OneHandedController { private final Context mContext; private final DisplayController mDisplayController; private final OneHandedGestureHandler mGestureHandler; + private final OneHandedSettingsUtil mOneHandedSettingsUtil; private final OneHandedTimeoutHandler mTimeoutHandler; private final OneHandedTouchHandler mTouchHandler; private final OneHandedTutorialHandler mTutorialHandler; @@ -108,8 +109,12 @@ public class OneHandedController { new AccessibilityManager.AccessibilityStateChangeListener() { @Override public void onAccessibilityStateChanged(boolean enabled) { + if (mOneHandedSettingsUtil == null) { + Slog.w(TAG, "mOneHandedSettingsUtil may not instantiate yet"); + return; + } if (enabled) { - final int mOneHandedTimeout = OneHandedSettingsUtil + final int mOneHandedTimeout = mOneHandedSettingsUtil .getSettingsOneHandedModeTimeout(mContext.getContentResolver()); final int timeout = mAccessibilityManager .getRecommendedTimeoutMillis(mOneHandedTimeout * 1000 @@ -117,7 +122,7 @@ public class OneHandedController { AccessibilityManager.FLAG_CONTENT_CONTROLS); mTimeoutHandler.setTimeout(timeout / 1000); } else { - mTimeoutHandler.setTimeout(OneHandedSettingsUtil + mTimeoutHandler.setTimeout(mOneHandedSettingsUtil .getSettingsOneHandedModeTimeout(mContext.getContentResolver())); } } @@ -166,13 +171,14 @@ public class OneHandedController { OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer( context, windowManager, animationController, tutorialHandler, oneHandedBackgroundPanelOrganizer, mainExecutor); + OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil(); OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger); IOverlayManager overlayManager = IOverlayManager.Stub.asInterface( ServiceManager.getService(Context.OVERLAY_SERVICE)); return new OneHandedController(context, windowManager, displayController, oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler, - gestureHandler, timeoutHandler, oneHandedUiEventsLogger, overlayManager, - taskStackListener, mainExecutor, mainHandler); + gestureHandler, settingsUtil, timeoutHandler, oneHandedUiEventsLogger, + overlayManager, taskStackListener, mainExecutor, mainHandler); } @VisibleForTesting @@ -184,6 +190,7 @@ public class OneHandedController { OneHandedTouchHandler touchHandler, OneHandedTutorialHandler tutorialHandler, OneHandedGestureHandler gestureHandler, + OneHandedSettingsUtil settingsUtil, OneHandedTimeoutHandler timeoutHandler, OneHandedUiEventLogger uiEventsLogger, IOverlayManager overlayManager, @@ -191,6 +198,7 @@ public class OneHandedController { ShellExecutor mainExecutor, Handler mainHandler) { mContext = context; + mOneHandedSettingsUtil = settingsUtil; mWindowManager = windowManager; mBackgroundPanelOrganizer = backgroundPanelOrganizer; mDisplayAreaOrganizer = displayAreaOrganizer; @@ -209,10 +217,10 @@ public class OneHandedController { final int sysPropPercentageConfig = SystemProperties.getInt( ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f)); mOffSetFraction = sysPropPercentageConfig / 100.0f; - mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + mIsOneHandedEnabled = mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled( context.getContentResolver()); mIsSwipeToNotificationEnabled = - OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( context.getContentResolver()); mTimeoutHandler = timeoutHandler; @@ -325,25 +333,25 @@ public class OneHandedController { } private void setupSettingObservers() { - OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED, + mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED, mContext.getContentResolver(), mEnabledObserver); - OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT, + mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT, mContext.getContentResolver(), mTimeoutObserver); - OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT, + mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT, mContext.getContentResolver(), mTaskChangeExitObserver); - OneHandedSettingsUtil.registerSettingsKeyObserver( + mOneHandedSettingsUtil.registerSettingsKeyObserver( Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, mContext.getContentResolver(), mSwipeToNotificationEnabledObserver); } private void updateSettings() { - setOneHandedEnabled(OneHandedSettingsUtil + setOneHandedEnabled(mOneHandedSettingsUtil .getSettingsOneHandedModeEnabled(mContext.getContentResolver())); - mTimeoutHandler.setTimeout(OneHandedSettingsUtil + mTimeoutHandler.setTimeout(mOneHandedSettingsUtil .getSettingsOneHandedModeTimeout(mContext.getContentResolver())); - setTaskChangeToExit(OneHandedSettingsUtil + setTaskChangeToExit(mOneHandedSettingsUtil .getSettingsTapsAppToExit(mContext.getContentResolver())); - setSwipeToNotificationEnabled(OneHandedSettingsUtil + setSwipeToNotificationEnabled(mOneHandedSettingsUtil .getSettingsSwipeToNotificationEnabled(mContext.getContentResolver())); } @@ -358,7 +366,7 @@ public class OneHandedController { @VisibleForTesting void onEnabledSettingChanged() { - final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + final boolean enabled = mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled( mContext.getContentResolver()); mOneHandedUiEventLogger.writeEvent(enabled ? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON @@ -368,13 +376,13 @@ public class OneHandedController { // Also checks swipe to notification settings since they all need gesture overlay. setEnabledGesturalOverlay( - enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + enabled || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver())); } @VisibleForTesting void onTimeoutSettingChanged() { - final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout( + final int newTimeout = mOneHandedSettingsUtil.getSettingsOneHandedModeTimeout( mContext.getContentResolver()); int metricsId = OneHandedUiEventLogger.OneHandedSettingsTogglesEvent.INVALID.getId(); switch (newTimeout) { @@ -403,7 +411,7 @@ public class OneHandedController { @VisibleForTesting void onTaskChangeExitSettingChanged() { - final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit( + final boolean enabled = mOneHandedSettingsUtil.getSettingsTapsAppToExit( mContext.getContentResolver()); mOneHandedUiEventLogger.writeEvent(enabled ? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON @@ -415,13 +423,13 @@ public class OneHandedController { @VisibleForTesting void onSwipeToNotificationEnabledSettingChanged() { final boolean enabled = - OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver()); setSwipeToNotificationEnabled(enabled); // Also checks one handed mode settings since they all need gesture overlay. setEnabledGesturalOverlay( - enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + enabled || mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled( mContext.getContentResolver())); } @@ -480,7 +488,8 @@ public class OneHandedController { } private void setupGesturalOverlay() { - if (!OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(mContext.getContentResolver())) { + if (!mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + mContext.getContentResolver())) { return; } @@ -551,7 +560,7 @@ public class OneHandedController { mTutorialHandler.dump(pw); } - OneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver()); + mOneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver()); if (mOverlayManager != null) { OverlayInfo info = null; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java index f8217c64e53d..4768cf58152b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java @@ -67,7 +67,7 @@ public final class OneHandedSettingsUtil { * @param observer Observer from caller * @return uri key for observing */ - public static Uri registerSettingsKeyObserver(String key, ContentResolver resolver, + public Uri registerSettingsKeyObserver(String key, ContentResolver resolver, ContentObserver observer) { Uri uriKey = null; uriKey = Settings.Secure.getUriFor(key); @@ -83,7 +83,7 @@ public final class OneHandedSettingsUtil { * @param resolver ContentResolver of context * @param observer preference key change observer */ - public static void unregisterSettingsKeyObserver(ContentResolver resolver, + public void unregisterSettingsKeyObserver(ContentResolver resolver, ContentObserver observer) { if (resolver != null) { resolver.unregisterContentObserver(observer); @@ -95,7 +95,7 @@ public final class OneHandedSettingsUtil { * * @return enable or disable one handed mode flag. */ - public static boolean getSettingsOneHandedModeEnabled(ContentResolver resolver) { + public boolean getSettingsOneHandedModeEnabled(ContentResolver resolver) { return Settings.Secure.getInt(resolver, Settings.Secure.ONE_HANDED_MODE_ENABLED, 0 /* Disabled */) == 1; } @@ -105,7 +105,7 @@ public final class OneHandedSettingsUtil { * * @return enable or disable taps app exit. */ - public static boolean getSettingsTapsAppToExit(ContentResolver resolver) { + public boolean getSettingsTapsAppToExit(ContentResolver resolver) { return Settings.Secure.getInt(resolver, Settings.Secure.TAPS_APP_TO_EXIT, 0) == 1; } @@ -116,7 +116,7 @@ public final class OneHandedSettingsUtil { * * @return timeout value in seconds. */ - public static @OneHandedTimeout int getSettingsOneHandedModeTimeout(ContentResolver resolver) { + public @OneHandedTimeout int getSettingsOneHandedModeTimeout(ContentResolver resolver) { return Settings.Secure.getInt(resolver, Settings.Secure.ONE_HANDED_MODE_TIMEOUT, ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); } @@ -124,12 +124,12 @@ public final class OneHandedSettingsUtil { /** * Returns whether swipe bottom to notification gesture enabled or not. */ - public static boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver) { + public boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver) { return Settings.Secure.getInt(resolver, Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1) == 1; } - protected static void dump(PrintWriter pw, String prefix, ContentResolver resolver) { + void dump(PrintWriter pw, String prefix, ContentResolver resolver) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); pw.print(innerPrefix + "isOneHandedModeEnable="); @@ -139,6 +139,6 @@ public final class OneHandedSettingsUtil { pw.print(innerPrefix + "tapsAppToExit="); pw.println(getSettingsTapsAppToExit(resolver)); } - - private OneHandedSettingsUtil() {} + public OneHandedSettingsUtil() { + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index 50d8098e1d34..9212c4b86105 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -69,9 +69,6 @@ public class StartingSurfaceDrawer { private final ShellExecutor mSplashScreenExecutor; private final SplashscreenContentDrawer mSplashscreenContentDrawer; - // TODO(b/131727939) remove this when clearing ActivityRecord - private static final int REMOVE_WHEN_TIMEOUT = 2000; - public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor, TransactionPool pool) { mContext = context; @@ -295,12 +292,8 @@ public class StartingSurfaceDrawer { TaskSnapshot snapshot) { final int taskId = startingWindowInfo.taskInfo.taskId; final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken, - snapshot, mSplashScreenExecutor, - () -> removeWindowNoAnimate(taskId)); - mSplashScreenExecutor.executeDelayed(() -> removeWindowNoAnimate(taskId), - REMOVE_WHEN_TIMEOUT); - final StartingWindowRecord tView = - new StartingWindowRecord(null/* decorView */, surface); + snapshot, mSplashScreenExecutor, () -> removeWindowNoAnimate(taskId)); + final StartingWindowRecord tView = new StartingWindowRecord(null/* decorView */, surface); mStartingWindowRecords.put(taskId, tView); } @@ -354,8 +347,6 @@ public class StartingSurfaceDrawer { } if (shouldSaveView) { removeWindowNoAnimate(taskId); - mSplashScreenExecutor.executeDelayed( - () -> removeWindowNoAnimate(taskId), REMOVE_WHEN_TIMEOUT); saveSplashScreenRecord(taskId, view); } return shouldSaveView; @@ -392,7 +383,6 @@ public class StartingSurfaceDrawer { if (leash != null || playRevealAnimation) { mSplashscreenContentDrawer.applyExitAnimation(record.mContentView, leash, frame, record.isEarlyExit(), exitFinish); - mSplashScreenExecutor.executeDelayed(exitFinish, REMOVE_WHEN_TIMEOUT); } else { // the SplashScreenView has been copied to client, skip default exit // animation diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index 4847c98f54e9..cd20ddee04fc 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -16,10 +16,8 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.Surface -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -64,7 +62,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @Postsubmit + @Presubmit @Test fun pipWindowBecomesVisible() { testSpec.assertWm { @@ -74,22 +72,6 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @FlakyTest(bugId = 140855415) - @Test - override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible() - - @FlakyTest(bugId = 140855415) - @Test - override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() - - @FlakyTest(bugId = 140855415) - @Test - override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() - - @FlakyTest(bugId = 140855415) - @Test - override fun noUncoveredRegions() = super.noUncoveredRegions() - companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt index d011419150e5..f554ca344a80 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt @@ -16,7 +16,7 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.launcher3.tapl.LauncherInstrumentation @@ -55,19 +55,11 @@ class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec } } - @Postsubmit - @Test - override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() - - @Postsubmit - @Test - override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() - - @Postsubmit + @Presubmit @Test fun pipAlwaysVisible() = testSpec.assertWm { this.showsAppWindow(pipApp.windowName) } - @Postsubmit + @Presubmit @Test fun pipLayerInsideDisplay() { testSpec.assertLayersStart { @@ -75,7 +67,7 @@ class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec } } - @Postsubmit + @Presubmit @Test fun pipWindowMovesUp() = testSpec.assertWmEnd { val initialState = this.trace?.first()?.wmState diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index 49a1055af4a0..8ceef8afe857 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.view.Surface -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -76,19 +75,19 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, testSpec.config.endRotation, allStates = false) - @FlakyTest(bugId = 140855415) + @Presubmit @Test override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, testSpec.config.endRotation) - @FlakyTest(bugId = 140855415) + @Presubmit @Test override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, testSpec.config.endRotation) - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun appLayerRotates_StartingBounds() { testSpec.assertLayersStart { @@ -97,7 +96,7 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) } } - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun appLayerRotates_EndingBounds() { testSpec.assertLayersEnd { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt index 67e1768f3a9f..102af92c8968 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.FlakyTest @@ -112,7 +111,7 @@ class SetRequestedOrientationWhilePinnedTest( } } - @Postsubmit + @Presubmit @Test fun pipAlwaysVisible() = testSpec.assertWm { this.showsAppWindow(pipApp.windowName) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java index bf84a6e30c98..da95c77d2b89 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java @@ -16,8 +16,6 @@ package com.android.wm.shell; -import android.os.Looper; - import com.android.wm.shell.common.ShellExecutor; import java.util.ArrayList; @@ -40,11 +38,6 @@ public class TestShellExecutor implements ShellExecutor { } @Override - public void removeAllCallbacks() { - mRunnables.clear(); - } - - @Override public void removeCallbacks(Runnable r) { mRunnables.remove(r); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java index 4cedc483fc21..ef046d48e1cf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import android.graphics.Insets; import android.graphics.Point; import android.view.InsetsSource; import android.view.InsetsSourceControl; @@ -119,7 +120,8 @@ public class DisplayImeControllerTest { private InsetsSourceControl[] insetsSourceControl() { return new InsetsSourceControl[]{ - new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0)) + new InsetsSourceControl( + ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0), Insets.NONE) }; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index b21276f5401b..bd5fe2bcbdad 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -16,11 +16,10 @@ package com.android.wm.shell.onehanded; -import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED; - import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; @@ -67,6 +66,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Mock OneHandedGestureHandler mMockGestureHandler; @Mock + OneHandedSettingsUtil mMockSettingsUitl; + @Mock OneHandedUiEventLogger mMockUiEventLogger; @Mock IOverlayManager mMockOverlayManager; @@ -79,13 +80,9 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Mock Handler mMockShellMainHandler; - final boolean mDefaultEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( - getTestContext().getContentResolver()); - final boolean mDefaultSwipeToNotificationEnabled = - OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( - getTestContext().getContentResolver()); - final boolean mDefaultTapAppToExitEnabled = OneHandedSettingsUtil.getSettingsTapsAppToExit( - getTestContext().getContentResolver()); + final boolean mDefaultEnabled = true; + final boolean mDefaultSwipeToNotificationEnabled = false; + final boolean mDefaultTapAppToExitEnabled = true; @Before public void setUp() { @@ -97,6 +94,14 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash); + when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any())).thenReturn( + mDefaultEnabled); + when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any())).thenReturn( + OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); + when(mMockSettingsUitl.getSettingsTapsAppToExit(any())).thenReturn( + mDefaultTapAppToExitEnabled); + when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any())).thenReturn( + mDefaultSwipeToNotificationEnabled); mSpiedOneHandedController = spy(new OneHandedController( mContext, @@ -107,6 +112,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { mMockTouchHandler, mMockTutorialHandler, mMockGestureHandler, + mMockSettingsUitl, mSpiedTimeoutHandler, mMockUiEventLogger, mMockOverlayManager, @@ -128,15 +134,6 @@ public class OneHandedControllerTest extends OneHandedTestCase { } @Test - public void testNoRegisterAndUnregisterInSameCall() { - if (mDefaultEnabled) { - verify(mMockDisplayAreaOrganizer, never()).unregisterOrganizer(); - } else { - verify(mMockDisplayAreaOrganizer, never()).registerOrganizer(FEATURE_ONE_HANDED); - } - } - - @Test public void testStartOneHandedShouldTriggerScheduleOffset() { when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); mSpiedOneHandedController.setOneHandedEnabled(true); @@ -190,35 +187,39 @@ public class OneHandedControllerTest extends OneHandedTestCase { public void testUpdateEnabled() { mSpiedOneHandedController.setOneHandedEnabled(true); - verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(mDefaultEnabled); - verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled( - mDefaultEnabled || mDefaultSwipeToNotificationEnabled); + verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean()); + verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean()); } @Test public void testUpdateSwipeToNotification() { mSpiedOneHandedController.setSwipeToNotificationEnabled(mDefaultSwipeToNotificationEnabled); - verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(mDefaultEnabled); - verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled( - mDefaultEnabled || mDefaultSwipeToNotificationEnabled); + verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean()); + verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled(anyBoolean()); } @Test - public void testSettingsObserverUpdateTapAppToExit() { - mSpiedOneHandedController.onTaskChangeExitSettingChanged(); - if (mDefaultTapAppToExitEnabled) { - verify(mMockTaskStackListener, atLeastOnce()).addListener(any()); - } else { - verify(mMockTaskStackListener, atLeastOnce()).removeListener(any()); - } + public void testTapAppToExitEnabledAddListener() { + mSpiedOneHandedController.setTaskChangeToExit(mDefaultTapAppToExitEnabled); + + // If device settings default ON, then addListener() will be trigger 1 time at init + verify(mMockTaskStackListener, atLeastOnce()).addListener(any()); + } + + @Test + public void testTapAppToExitDisabledRemoveListener() { + mSpiedOneHandedController.setTaskChangeToExit(!mDefaultTapAppToExitEnabled); + + // If device settings default ON, then removeListener() will be trigger 1 time at init + verify(mMockTaskStackListener, atLeastOnce()).removeListener(any()); } @Test public void testSettingsObserverUpdateEnabled() { mSpiedOneHandedController.onEnabledSettingChanged(); - verify(mSpiedOneHandedController).setOneHandedEnabled(mDefaultEnabled); + verify(mSpiedOneHandedController).setOneHandedEnabled(anyBoolean()); } @Test @@ -232,14 +233,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { public void testSettingsObserverUpdateSwipeToNotification() { mSpiedOneHandedController.onSwipeToNotificationEnabledSettingChanged(); - // Swipe to notification function is opposite with one handed mode function - if (mDefaultSwipeToNotificationEnabled) { - verify(mSpiedOneHandedController).setSwipeToNotificationEnabled( - mDefaultSwipeToNotificationEnabled); - } else { - verify(mSpiedOneHandedController, never()).setSwipeToNotificationEnabled( - mDefaultSwipeToNotificationEnabled); - } + verify(mSpiedOneHandedController).setSwipeToNotificationEnabled(anyBoolean()); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java index 61643d86c8d9..1e6c41af4397 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java @@ -16,17 +16,11 @@ package com.android.wm.shell.onehanded; -import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS; -import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS; -import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER; -import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS; - -import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; import android.content.ContentResolver; import android.database.ContentObserver; -import android.net.Uri; -import android.provider.Settings; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; @@ -34,76 +28,30 @@ import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) public class OneHandedSettingsUtilTest extends OneHandedTestCase { - ContentResolver mContentResolver; - ContentObserver mContentObserver; - boolean mOnChanged; + OneHandedSettingsUtil mSettingsUtil; + + @Mock + ContentResolver mMockContentResolver; + @Mock + ContentObserver mMockContentObserver; @Before public void setUp() { - mContentResolver = mContext.getContentResolver(); - mContentObserver = new ContentObserver(mContext.getMainThreadHandler()) { - @Override - public void onChange(boolean selfChange) { - super.onChange(selfChange); - mOnChanged = true; - } - }; - } - - @Test - public void testRegisterSecureKeyObserver() { - final Uri result = OneHandedSettingsUtil.registerSettingsKeyObserver( - Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver); + MockitoAnnotations.initMocks(this); - assertThat(result).isNotNull(); - - OneHandedSettingsUtil.registerSettingsKeyObserver( - Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver); + mSettingsUtil = new OneHandedSettingsUtil(); } @Test public void testUnregisterSecureKeyObserver() { - OneHandedSettingsUtil.registerSettingsKeyObserver( - Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver); - OneHandedSettingsUtil.unregisterSettingsKeyObserver(mContentResolver, mContentObserver); - - assertThat(mOnChanged).isFalse(); - - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.TAPS_APP_TO_EXIT, 0); - - assertThat(mOnChanged).isFalse(); - } + mSettingsUtil.unregisterSettingsKeyObserver(mMockContentResolver, mMockContentObserver); - @Test - public void testGetSettingsIsOneHandedModeEnabled() { - assertThat(OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( - mContentResolver)).isAnyOf(true, false); - } - - @Test - public void testGetSettingsTapsAppToExit() { - assertThat(OneHandedSettingsUtil.getSettingsTapsAppToExit( - mContentResolver)).isAnyOf(true, false); - } - - @Test - public void testGetSettingsOneHandedModeTimeout() { - assertThat(OneHandedSettingsUtil.getSettingsOneHandedModeTimeout( - mContentResolver)).isAnyOf( - ONE_HANDED_TIMEOUT_NEVER, - ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS, - ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS, - ONE_HANDED_TIMEOUT_LONG_IN_SECONDS); - } - - @Test - public void testGetSettingsSwipeToNotificationEnabled() { - assertThat(OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( - mContentResolver)).isAnyOf(true, false); + verify(mMockContentResolver).unregisterContentObserver(any()); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java index 69c537c2efbe..f586dda145d7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java @@ -64,6 +64,8 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { Handler mMockShellMainHandler; @Mock OneHandedUiEventLogger mMockUiEventLogger; + @Mock + OneHandedSettingsUtil mMockSettingsUtil; @Before public void setUp() { @@ -80,6 +82,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { mMockTouchHandler, mMockTutorialHandler, mMockGestureHandler, + mMockSettingsUtil, mTimeoutHandler, mMockUiEventLogger, mMockOverlayManager, diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index e8e263147c6e..9e9d02e4eebf 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -5287,7 +5287,10 @@ public class AudioManager { * otherwise (typically one device, except for duplicated paths). */ @SystemApi - @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + @RequiresPermission(anyOf = { + android.Manifest.permission.MODIFY_AUDIO_ROUTING, + android.Manifest.permission.QUERY_AUDIO_STATE + }) public @NonNull List<AudioDeviceAttributes> getDevicesForAttributes( @NonNull AudioAttributes attributes) { Objects.requireNonNull(attributes); @@ -5426,7 +5429,10 @@ public class AudioManager { * @return the volume behavior for the device */ @SystemApi - @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + @RequiresPermission(anyOf = { + android.Manifest.permission.MODIFY_AUDIO_ROUTING, + android.Manifest.permission.QUERY_AUDIO_STATE + }) public @DeviceVolumeBehavior int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) { // verify arguments (validity of device type is enforced in server) @@ -5440,6 +5446,28 @@ public class AudioManager { } } + /** + * @hide + * Returns {@code true} if the volume device behavior is {@link #DEVICE_VOLUME_BEHAVIOR_FULL}. + */ + @TestApi + @RequiresPermission(anyOf = { + android.Manifest.permission.MODIFY_AUDIO_ROUTING, + android.Manifest.permission.QUERY_AUDIO_STATE + }) + public boolean isFullVolumeDevice() { + final AudioAttributes attributes = new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .build(); + final List<AudioDeviceAttributes> devices = getDevicesForAttributes(attributes); + for (AudioDeviceAttributes device : devices) { + if (getDeviceVolumeBehavior(device) == DEVICE_VOLUME_BEHAVIOR_FULL) { + return true; + } + } + return false; + } + /** * Indicate wired accessory connection state change. * @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx) diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml index ed120b53d636..36e59ba832cc 100644 --- a/packages/CompanionDeviceManager/res/values-af/strings.xml +++ b/packages/CompanionDeviceManager/res/values-af/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Kies \'n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om deur <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> bestuur te word"</string> <string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string> <string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Stel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> om jou <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> te bestuur"</string> + <string name="profile_summary" msgid="2059360676631420073">"Hierdie program is nodig om jou <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string> + <string name="consent_no" msgid="2640796915611404382">"Moenie toelaat nie"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml index 76f68e7ee495..0fefa8af15de 100644 --- a/packages/CompanionDeviceManager/res/values-am/strings.xml +++ b/packages/CompanionDeviceManager/res/values-am/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"በ<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> የሚተዳደር <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string> <string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> - <strong></strong> ለማስተዳደር"</string> + <string name="profile_summary" msgid="2059360676631420073">"የእርስዎን <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ለማስተዳደር ይህ መተግበሪያ ያስፈልጋል <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string> + <string name="consent_no" msgid="2640796915611404382">"አትፍቀድ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml index 92783a529cfc..ca3b9f364c76 100644 --- a/packages/CompanionDeviceManager/res/values-ar/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"اختَر <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ليديره تطبيق <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string> <string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"ضبط <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> لإدارة <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"هذا التطبيق مطلوب لإدارة <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"السماح"</string> + <string name="consent_no" msgid="2640796915611404382">"عدم السماح"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml index b07cad615d57..8e4a202b18ea 100644 --- a/packages/CompanionDeviceManager/res/values-az/strings.xml +++ b/packages/CompanionDeviceManager/res/values-az/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> tərəfindən idarə ediləcək <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string> <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string> <string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazınızı idarə etmək üçün <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ayarlayın"</string> + <string name="profile_summary" msgid="2059360676631420073">"Bu tətbiq <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizi idarə etmək üçün lazımdır. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string> + <string name="consent_no" msgid="2640796915611404382">"İcazə verməyin"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml index edeaa7785e36..ef19c48207f3 100644 --- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> <string name="profile_name_watch" msgid="576290739483672360">"sat"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Podesite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tako da upravlja uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string> + <string name="consent_no" msgid="2640796915611404382">"Ne dozvoli"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml index 9410d686133b..4366a0895222 100644 --- a/packages/CompanionDeviceManager/res/values-be/strings.xml +++ b/packages/CompanionDeviceManager/res/values-be/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Выберыце прыладу (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string> <string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> кіраваць прыладай <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Гэта праграма неабходная для кіравання профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string> + <string name="consent_no" msgid="2640796915611404382">"Не дазваляць"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml index 4457dbd2cb3a..77f3413f8b88 100644 --- a/packages/CompanionDeviceManager/res/values-bg/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Изберете устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), което да се управлява от <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string> <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Задайте <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управлява устройството ви <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Това приложение е необходимо за управление на <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string> + <string name="consent_no" msgid="2640796915611404382">"Забраняване"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml index 4aa7e74e5432..5fa47813dfe2 100644 --- a/packages/CompanionDeviceManager/res/values-bn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন যেটি <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ম্যানেজ করবে"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string> <string name="profile_name_watch" msgid="576290739483672360">"দেখুন"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"আপনার <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ম্যানেজ করার জন্য <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> সেট করুন"</string> + <string name="profile_summary" msgid="2059360676631420073">"আপনার <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ম্যানেজ করতে এই অ্যাপটি প্রয়োজন। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string> + <string name="consent_no" msgid="2640796915611404382">"অনুমতি দেবেন না"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml index 8ffa3272fb8c..18153b589372 100644 --- a/packages/CompanionDeviceManager/res/values-bs/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Odaberite uređaj <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> <string name="profile_name_watch" msgid="576290739483672360">"sat"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Postavite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja vašim uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string> + <string name="consent_no" msgid="2640796915611404382">"Nemoj dozvoliti"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml index c9c186eba741..bbd1125eef6d 100644 --- a/packages/CompanionDeviceManager/res/values-ca/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> perquè el gestioni <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string> <string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Defineix que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestioni el dispositiu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Aquesta aplicació es necessita per gestionar <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Permet"</string> + <string name="consent_no" msgid="2640796915611404382">"No permetis"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml index b2ac32db2a99..c5fed3372030 100644 --- a/packages/CompanionDeviceManager/res/values-cs/strings.xml +++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Vyberte zařízení <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete spravovat pomocí aplikace <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string> <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Nastavit aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pro správu vašeho zařízení: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Tato aplikace je nutná pro správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Povolit"</string> + <string name="consent_no" msgid="2640796915611404382">"Nepovolovat"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml index bc67948c3ecf..21280d527b43 100644 --- a/packages/CompanionDeviceManager/res/values-da/strings.xml +++ b/packages/CompanionDeviceManager/res/values-da/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Vælg den enhed (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), som skal administreres af <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string> <string name="profile_name_watch" msgid="576290739483672360">"ur"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Indstil <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> til at administrere <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Du skal bruge denne app for at administrere dit <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Tillad"</string> + <string name="consent_no" msgid="2640796915611404382">"Tillad ikke"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml index fed516fb04eb..162d6fc8b32d 100644 --- a/packages/CompanionDeviceManager/res/values-el/strings.xml +++ b/packages/CompanionDeviceManager/res/values-el/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για διαχείριση από την εφαρμογή <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string> <string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Ορίστε την εφαρμογή <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> για διαχείριση της συσκευής <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Αυτή η εφαρμογή είναι απαραίτητη για τη διαχείριση του προφίλ σας <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string> + <string name="consent_no" msgid="2640796915611404382">"Να μην επιτρέπεται"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml index 4fd057f3e7e4..de89b39a37dd 100644 --- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> + <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml index 4fd057f3e7e4..de89b39a37dd 100644 --- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> + <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml index 4fd057f3e7e4..de89b39a37dd 100644 --- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> + <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml index 4fd057f3e7e4..de89b39a37dd 100644 --- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> + <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml index 0e3902c50aea..10c20121883b 100644 --- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml @@ -22,10 +22,7 @@ <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> <!-- no translation found for confirmation_title (814973816731238955) --> <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> + <string name="consent_no" msgid="2640796915611404382">"Don’t allow"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml index 6b903c609fe9..8dae51c706b8 100644 --- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para que <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> lo administre"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Configura <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para administrar el dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Esta app es necesaria para administrar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> + <string name="consent_no" msgid="2640796915611404382">"No permitir"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml index 0a2906a97a39..a307b61a44f9 100644 --- a/packages/CompanionDeviceManager/res/values-es/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para gestionarlo con <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Configura <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para que gestione tu dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Se necesita esta aplicación para gestionar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> + <string name="consent_no" msgid="2640796915611404382">"No permitir"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml index 4958a17206f7..2fb2bc49daff 100644 --- a/packages/CompanionDeviceManager/res/values-et/strings.xml +++ b/packages/CompanionDeviceManager/res/values-et/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Valige seade <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mida haldab rakendus <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"seade"</string> <string name="profile_name_watch" msgid="576290739483672360">"käekell"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Määrake rakendus <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> haldama teie seadet <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Seda rakendust on vaja teie profiili <xliff:g id="PROFILE_NAME">%1$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Luba"</string> + <string name="consent_no" msgid="2640796915611404382">"Ära luba"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml index 2a61fd519511..646f844ac572 100644 --- a/packages/CompanionDeviceManager/res/values-eu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Aukeratu <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string> <string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Konfiguratu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> kudea dezan"</string> + <string name="profile_summary" msgid="2059360676631420073">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> kudeatzeko beharrezkoa da aplikazioa. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string> + <string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml index 8d102303d91a..69b9647a07dd 100644 --- a/packages/CompanionDeviceManager/res/values-fa/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای مدیریت کردن با <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string> <string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"تنظیم <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> برای مدیریت <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"این برنامه برای مدیریت <xliff:g id="PROFILE_NAME">%1$s</xliff:g> شما لازم است. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"مجاز"</string> + <string name="consent_no" msgid="2640796915611404382">"مجاز نیست"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml index cfa03af870dc..b174b8a9d5bb 100644 --- a/packages/CompanionDeviceManager/res/values-fi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, jota <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> hallinnoi"</string> <string name="profile_name_generic" msgid="6851028682723034988">"laite"</string> <string name="profile_name_watch" msgid="576290739483672360">"kello"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Valitse <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> laitteen (<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>) ylläpitäjäksi"</string> + <string name="profile_summary" msgid="2059360676631420073">"Profiilin (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) ylläpitoon tarvitaan tätä sovellusta. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Salli"</string> + <string name="consent_no" msgid="2640796915611404382">"Älä salli"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml index d3cfc299c4ce..696598d9f4b9 100644 --- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Choisissez un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré par <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string> <string name="profile_name_watch" msgid="576290739483672360">"montre"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Utiliser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pour gérer votre <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Cette application est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string> + <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml index d82e3905c5d1..787794bf3893 100644 --- a/packages/CompanionDeviceManager/res/values-fr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Sélectionner le/la <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré(e) par <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string> <string name="profile_name_watch" msgid="576290739483672360">"montre"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Définir <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pour gérer votre <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Cette appli est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string> + <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml index 2a85398cf3cc..a3efc4c0610d 100644 --- a/packages/CompanionDeviceManager/res/values-gl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Escolle un perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) para que o xestione a aplicación <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Configura <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para que xestione o teu dispositivo <strong>(<xliff:g id="DEVICE_NAME">%2$s</xliff:g>)</strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Esta aplicación é necesaria para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> + <string name="consent_no" msgid="2640796915611404382">"Non permitir"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml index 6f52403ed416..1b0fe1ac2efc 100644 --- a/packages/CompanionDeviceManager/res/values-gu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> દ્વારા મેનેજ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string> <string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"તમારું <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> મેનેજ કરવા માટે, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> સેટ કરો"</string> + <string name="profile_summary" msgid="2059360676631420073">"તમારી <xliff:g id="PROFILE_NAME">%1$s</xliff:g> મેનેજ કરવા માટે આ ઍપ જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string> + <string name="consent_no" msgid="2640796915611404382">"મંજૂરી આપશો નહીં"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml index 9af99924c76a..dee30f259562 100644 --- a/packages/CompanionDeviceManager/res/values-hi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें, ताकि उसे <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> की मदद से प्रबंधित किया जा सके"</string> <string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string> <string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> को मैनेज करने के लिए, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> सेट अप करें"</string> + <string name="profile_summary" msgid="2059360676631420073">"यह ऐप्लिकेशन, <xliff:g id="PROFILE_NAME">%1$s</xliff:g> मैनेज करने के लिए ज़रूरी है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string> + <string name="consent_no" msgid="2640796915611404382">"अनुमति न दें"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml index fe30ec485169..bc36521dd1e1 100644 --- a/packages/CompanionDeviceManager/res/values-hr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> <string name="profile_name_watch" msgid="576290739483672360">"satom"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Postavite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja vašim uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Ta je aplikacija potrebna za upravljanje vašim profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string> + <string name="consent_no" msgid="2640796915611404382">"Nemoj dopustiti"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml index 370d4dff6a98..ef505443ffee 100644 --- a/packages/CompanionDeviceManager/res/values-hu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"A(z) <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> alkalmazással kezelni kívánt <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiválasztása"</string> <string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string> <string name="profile_name_watch" msgid="576290739483672360">"óra"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"A(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> alkalmazás beállítva a(z) <xliff:g id="DEVICE_NAME">%2$s</xliff:g> (<strong></strong>) kezelésére"</string> + <string name="profile_summary" msgid="2059360676631420073">"Szükség van erre az alkalmazásra a következő kezeléséhez: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string> + <string name="consent_no" msgid="2640796915611404382">"Tiltás"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml index fee55d0c5396..103361aa9cfa 100644 --- a/packages/CompanionDeviceManager/res/values-hy/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը, որը պետք է կառավարվի <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> հավելվածի կողմից"</string> <string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string> <string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Ընտրեք <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածը որպես <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> սարքի կառավարիչ"</string> + <string name="profile_summary" msgid="2059360676631420073">"Այս հավելվածն անհրաժեշտ է ձեր <xliff:g id="PROFILE_NAME">%1$s</xliff:g> պրոֆիլը կառավարելու համար։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string> + <string name="consent_no" msgid="2640796915611404382">"Չթույլատրել"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml index 498bf2cd3351..225b276a608d 100644 --- a/packages/CompanionDeviceManager/res/values-in/strings.xml +++ b/packages/CompanionDeviceManager/res/values-in/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk dikelola oleh <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string> <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Tetapkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengelola <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Aplikasi ini diperlukan untuk mengelola <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string> + <string name="consent_no" msgid="2640796915611404382">"Jangan izinkan"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml index bd12658284ef..7855a2ac4240 100644 --- a/packages/CompanionDeviceManager/res/values-is/strings.xml +++ b/packages/CompanionDeviceManager/res/values-is/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Velja <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sem <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> á að stjórna"</string> <string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string> <string name="profile_name_watch" msgid="576290739483672360">"úr"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> stjórn á <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Þetta forrit er nauðsynlegt til að hafa umsjón með <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string> + <string name="consent_no" msgid="2640796915611404382">"Ekki leyfa"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml index 40d43207bced..9e503e16e71d 100644 --- a/packages/CompanionDeviceManager/res/values-it/strings.xml +++ b/packages/CompanionDeviceManager/res/values-it/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Scegli un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> che sia gestito da <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="profile_name_watch" msgid="576290739483672360">"orologio"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Configura l\'app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> affinché gestisca <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Questa app è necessaria per gestire il tuo <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Consenti"</string> + <string name="consent_no" msgid="2640796915611404382">"Non consentire"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml index 807cdd471642..d293fb0f6c18 100644 --- a/packages/CompanionDeviceManager/res/values-iw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"בחירה של <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string> <string name="profile_name_watch" msgid="576290739483672360">"שעון"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"הגדרה של <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לניהול <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"האפליקציה הזו נחוצה כדי לנהל את <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string> + <string name="consent_no" msgid="2640796915611404382">"אין אישור"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml index 92022be9fade..ac8e2d2d0698 100644 --- a/packages/CompanionDeviceManager/res/values-ja/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> の管理対象となる<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string> <string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string> <string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> を管理する <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> の設定"</string> + <string name="profile_summary" msgid="2059360676631420073">"このアプリは<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の管理に必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"許可"</string> + <string name="consent_no" msgid="2640796915611404382">"許可しない"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml index 64a79b48ec7e..8b7680ee34c1 100644 --- a/packages/CompanionDeviceManager/res/values-ka/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, რომელიც უნდა მართოს <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>-მა"</string> <string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string> <string name="profile_name_watch" msgid="576290739483672360">"საათი"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"დააყენეთ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, რათა მართოთ თქვენი <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"ეს აპი საჭიროა თქვენი <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-ს სამართავად. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"დაშვება"</string> + <string name="consent_no" msgid="2640796915611404382">"არ დაიშვას"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml index edd5c0ee9cd0..1ad854e3087b 100644 --- a/packages/CompanionDeviceManager/res/values-kk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> арқылы басқарылатын <xliff:g id="PROFILE_NAME">%1$s</xliff:g> құрылғысын таңдаңыз"</string> <string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string> <string name="profile_name_watch" msgid="576290739483672360">"сағат"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> құрылғысын басқаруға рұқсат беріңіз"</string> + <string name="profile_summary" msgid="2059360676631420073">"Бұл қолданба <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профиліңізді басқару үшін қажет. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string> + <string name="consent_no" msgid="2640796915611404382">"Рұқсат бермеу"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml index 36c02de6790d..7231c2d577a8 100644 --- a/packages/CompanionDeviceManager/res/values-km/strings.xml +++ b/packages/CompanionDeviceManager/res/values-km/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីឱ្យស្ថិតក្រោមការគ្រប់គ្រងរបស់ <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string> <string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"កំណត់ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ដើម្បីគ្រប់គ្រង <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> របស់អ្នក"</string> + <string name="profile_summary" msgid="2059360676631420073">"ត្រូវការកម្មវិធីនេះ ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%1$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string> + <string name="consent_no" msgid="2640796915611404382">"កុំអនុញ្ញាត"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml index 56c1557e5fef..6f7532877fb5 100644 --- a/packages/CompanionDeviceManager/res/values-kn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string> <string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"ನಿಮ್ಮ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ಅನ್ನು ನಿರ್ವಹಿಸಲು <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಅನ್ನು ಸೆಟ್ ಮಾಡಿ"</string> + <string name="profile_summary" msgid="2059360676631420073">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. ಅನ್ನು ನಿರ್ವಹಿಸಲು ಈ ಆ್ಯಪ್ನ ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string> + <string name="consent_no" msgid="2640796915611404382">"ಅನುಮತಿಸಬೇಡಿ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml index 79c36dd017fa..5b171eaded63 100644 --- a/packages/CompanionDeviceManager/res/values-ko/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>에서 관리할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g>을(를) 선택"</string> <string name="profile_name_generic" msgid="6851028682723034988">"기기"</string> <string name="profile_name_watch" msgid="576290739483672360">"시계"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 앱이 <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> 기기를 관리하도록 설정"</string> + <string name="profile_summary" msgid="2059360676631420073">"이 앱은 <xliff:g id="PROFILE_NAME">%1$s</xliff:g> 프로필을 관리하는 데 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"허용"</string> + <string name="consent_no" msgid="2640796915611404382">"허용 안함"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml index 8a90b3dc7d6a..f7c896be7f98 100644 --- a/packages/CompanionDeviceManager/res/values-ky/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> тарабынан башкарылсын"</string> <string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string> <string name="profile_name_watch" msgid="576290739483672360">"саат"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> түзмөгүңүздү башкаруу үчүн <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосун жөндөңүз"</string> + <string name="profile_summary" msgid="2059360676631420073">"Бул колдонмо <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профилиңизди башкаруу үчүн керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Уруксат берүү"</string> + <string name="consent_no" msgid="2640796915611404382">"Уруксат берилбесин"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml index a6564b347651..8ad881f63e8b 100644 --- a/packages/CompanionDeviceManager/res/values-lo/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ເພື່ອໃຫ້ຖືກຈັດການໂດຍ <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string> <string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"ຕັ້ງຄ່າ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ເພື່ອຈັດການ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ຂອງທ່ານ"</string> + <string name="profile_summary" msgid="2059360676631420073">"ຕ້ອງໃຊ້ແອັບນີ້ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"ອະນຸຍາດ"</string> + <string name="consent_no" msgid="2640796915611404382">"ບໍ່ອະນຸຍາດ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml index 382f1cf69a2d..c40d4a75753f 100644 --- a/packages/CompanionDeviceManager/res/values-lt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, kurį valdys <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> (pasirinkite)"</string> <string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string> <string name="profile_name_watch" msgid="576290739483672360">"laikrodis"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> tvarkymo naudojant <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> nustatymas"</string> + <string name="profile_summary" msgid="2059360676631420073">"Ši programa reikalinga norint tvarkyti jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Leisti"</string> + <string name="consent_no" msgid="2640796915611404382">"Neleisti"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml index 8d70bf788e27..c842ee1436e3 100644 --- a/packages/CompanionDeviceManager/res/values-lv/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) izvēle, ko pārvaldīt lietotnē <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string> <string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Lietotnes <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> iestatīšana ierīces <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> pārvaldībai"</string> + <string name="profile_summary" msgid="2059360676631420073">"Šī lietotne ir nepieciešama jūsu profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) pārvaldībai. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string> + <string name="consent_no" msgid="2640796915611404382">"Neatļaut"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml index 5322e98c37b7..fdc366e1900f 100644 --- a/packages/CompanionDeviceManager/res/values-mk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> со којшто ќе управува <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"уред"</string> <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Поставете <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> за да управувате со вашиот <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Апликацијава е потребна за управување со вашиот <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string> + <string name="consent_no" msgid="2640796915611404382">"Не дозволувај"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml index a9262c7973fb..ae445a3f0f12 100644 --- a/packages/CompanionDeviceManager/res/values-ml/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string> <string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"നിങ്ങളുടെ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> മാനേജ് ചെയ്യുന്നതിന് <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> സജ്ജീകരിക്കുക"</string> + <string name="profile_summary" msgid="2059360676631420073">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> മാനേജ് ചെയ്യാൻ ഈ ആപ്പ് ആവശ്യമാണ്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string> + <string name="consent_no" msgid="2640796915611404382">"അനുവദിക്കരുത്"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml index 303286466f4b..01850e707fb4 100644 --- a/packages/CompanionDeviceManager/res/values-mn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>-н удирдах<xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г сонгоно уу"</string> <string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string> <string name="profile_name_watch" msgid="576290739483672360">"цаг"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Та <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>-г удирдахын тулд <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-г тохируулна уу"</string> + <string name="profile_summary" msgid="2059360676631420073">"Энэ апп таны <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г удирдахад шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string> + <string name="consent_no" msgid="2640796915611404382">"Бүү зөвшөөр"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml index 01dae7d3a21e..7eea9bf0941a 100644 --- a/packages/CompanionDeviceManager/res/values-mr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> द्वारे व्यवस्थापित करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string> <string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string> <string name="profile_name_watch" msgid="576290739483672360">"पाहा"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"तुमचे <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> व्यवस्थापित करण्यासाठी <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> सेट करा"</string> + <string name="profile_summary" msgid="2059360676631420073">"तुमची <xliff:g id="PROFILE_NAME">%1$s</xliff:g> प्रोफाइल व्यवस्थापित करण्यासाठी हे ॲप आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string> + <string name="consent_no" msgid="2640796915611404382">"अनुमती देऊ नका"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml index 4e0f58bc2653..e43a27f0cecf 100644 --- a/packages/CompanionDeviceManager/res/values-ms/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk diurus oleh <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string> <string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Tetapkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengurus <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> anda"</string> + <string name="profile_summary" msgid="2059360676631420073">"Apl ini diperlukan untuk menguruskan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string> + <string name="consent_no" msgid="2640796915611404382">"Jangan benarkan"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml index 050c8ce4364a..1bd3a1d9f9e7 100644 --- a/packages/CompanionDeviceManager/res/values-my/strings.xml +++ b/packages/CompanionDeviceManager/res/values-my/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> က စီမံခန့်ခွဲရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးချယ်ပါ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string> <string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"သင်၏ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ကို စီမံခန့်ခွဲရန် <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ကို သတ်မှတ်ပါ"</string> + <string name="profile_summary" msgid="2059360676631420073">"သင်၏ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို စီမံခန့်ခွဲရန် ဤအက်ပ်ကိုလိုအပ်သည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string> + <string name="consent_no" msgid="2640796915611404382">"ခွင့်မပြုပါ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml index a8e22033faed..e4d247b4ae00 100644 --- a/packages/CompanionDeviceManager/res/values-nb/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal administreres av <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string> <string name="profile_name_watch" msgid="576290739483672360">"klokke"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Velg at <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> skal administrere <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Denne appen kreves for å administrere <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Tillat"</string> + <string name="consent_no" msgid="2640796915611404382">"Ikke tillat"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml index e7e03904843e..9dc23e7ee750 100644 --- a/packages/CompanionDeviceManager/res/values-nl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om te beheren met <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string> <string name="profile_name_watch" msgid="576290739483672360">"horloge"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> instellen om je <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> te beheren"</string> + <string name="profile_summary" msgid="2059360676631420073">"Deze app is vereist om je <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string> + <string name="consent_no" msgid="2640796915611404382">"Niet toestaan"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml index 1f516fa4c0d0..4cfe0573dd42 100644 --- a/packages/CompanionDeviceManager/res/values-or/strings.xml +++ b/packages/CompanionDeviceManager/res/values-or/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ବାଛନ୍ତୁ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string> <string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"ଆପଣଙ୍କ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ସେଟ୍ କରନ୍ତୁ"</string> + <string name="profile_summary" msgid="2059360676631420073">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ ଏହି ଆପ୍ ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string> + <string name="consent_no" msgid="2640796915611404382">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml index 7adf064e80b6..3dbd2f75a68d 100644 --- a/packages/CompanionDeviceManager/res/values-pl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Wybierz profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, którym ma zarządzać aplikacja <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string> <string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Skonfiguruj aplikację <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, aby zarządzała urządzeniem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacja jest niezbędna do zarządzania profilem <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string> + <string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml index b5ddc6db6f78..91a9fa4a58b6 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Configure o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para gerenciar seu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> + <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml index c06ac7de1c11..5ad9389f82e2 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerido pela app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Defina a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para gerir o seu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Esta app é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> + <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml index b5ddc6db6f78..91a9fa4a58b6 100644 --- a/packages/CompanionDeviceManager/res/values-pt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Configure o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para gerenciar seu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> + <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml index 437e8dc78c76..15f5393e7c75 100644 --- a/packages/CompanionDeviceManager/res/values-ro/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Alegeți un profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pe care să îl gestioneze <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string> <string name="profile_name_watch" msgid="576290739483672360">"ceas"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Setați <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pentru a vă gestiona dispozitivul <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Această aplicație este necesară pentru a gestiona <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string> + <string name="consent_no" msgid="2640796915611404382">"Nu permiteți"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml index d087959971b6..2f79416ffc4b 100644 --- a/packages/CompanionDeviceManager/res/values-ru/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Выберите устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), которым будет управлять приложение <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string> <string name="profile_name_watch" msgid="576290739483672360">"часы"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Разрешите приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> управлять устройством <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Это приложение необходимо для управления вашим профилем (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string> + <string name="consent_no" msgid="2640796915611404382">"Запретить"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml index 365b6bfb3d2a..d108a25ce2da 100644 --- a/packages/CompanionDeviceManager/res/values-si/strings.xml +++ b/packages/CompanionDeviceManager/res/values-si/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> මගින් කළමනාකරණය කරනු ලැබීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string> <string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string> <string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ඔබගේ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> කළමනාකරණය කිරීමට සකසන්න"</string> + <string name="profile_summary" msgid="2059360676631420073">"මෙම යෙදුමට ඔබගේ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> කළමනාකරණය කිරීමට අවශ්යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string> + <string name="consent_no" msgid="2640796915611404382">"ඉඩ නොදෙන්න"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml index d5c099a36424..d53728b34d5a 100644 --- a/packages/CompanionDeviceManager/res/values-sk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý bude spravovať aplikácia <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string> <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Nastavte aplikáciu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, aby spravovala zariadenie <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Táto aplikácia sa vyžaduje na správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string> + <string name="consent_no" msgid="2640796915611404382">"Nepovoliť"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml index d855db1cea72..28492105f8f2 100644 --- a/packages/CompanionDeviceManager/res/values-sl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Izbira naprave <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ki jo bo upravljala aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string> <string name="profile_name_watch" msgid="576290739483672360">"ura"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Nastavitev aplikacije <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> za upravljanje naprave <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacija je potrebna za upravljanje profila »<xliff:g id="PROFILE_NAME">%1$s</xliff:g>«. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string> + <string name="consent_no" msgid="2640796915611404382">"Ne dovoli"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml index 7335446765da..a57835a1d92e 100644 --- a/packages/CompanionDeviceManager/res/values-sq/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Zgjidh një profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> që do të menaxhohet nga <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string> <string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Cakto <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> që të menaxhojë pajisjen tënde <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Ky aplikacion nevojitet për të menaxhuar profilin tënd <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Lejo"</string> + <string name="consent_no" msgid="2640796915611404382">"Mos lejo"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml index f3f65e17101b..0b01cbce9a12 100644 --- a/packages/CompanionDeviceManager/res/values-sr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> којим ће управљати апликација <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string> <string name="profile_name_watch" msgid="576290739483672360">"сат"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Подесите апликацију <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> тако да управља уређајем <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Ова апликација је потребна за управљање профилом <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string> + <string name="consent_no" msgid="2640796915611404382">"Не дозволи"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml index 574166822006..f6dcec9054c7 100644 --- a/packages/CompanionDeviceManager/res/values-sv/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Välj en <xliff:g id="PROFILE_NAME">%1$s</xliff:g> för hantering av <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string> <string name="profile_name_watch" msgid="576290739483672360">"klocka"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Konfigurera <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> för att hantera din <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Appen behövs för att hantera <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string> + <string name="consent_no" msgid="2640796915611404382">"Tillåt inte"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml index 357eb4a475b8..0999c5b87635 100644 --- a/packages/CompanionDeviceManager/res/values-sw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili idhibitiwe na <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string> <string name="profile_name_watch" msgid="576290739483672360">"saa"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Weka <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ili udhibiti <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> yako"</string> + <string name="profile_summary" msgid="2059360676631420073">"Programu hii inahitajika ili udhibiti wasifu wako wa <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string> + <string name="consent_no" msgid="2640796915611404382">"Usiruhusu"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml index 651d9e276a7a..884d57f5b61b 100644 --- a/packages/CompanionDeviceManager/res/values-ta/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ஆப்ஸ் நிர்வகிக்கக்கூடிய <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ஐத் தேர்ந்தெடுங்கள்"</string> <string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string> <string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"உங்கள் <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> சாதனத்தை நிர்வகிக்க <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அமையுங்கள்"</string> + <string name="profile_summary" msgid="2059360676631420073">"உங்கள் <xliff:g id="PROFILE_NAME">%1$s</xliff:g> சுயவிவரத்தை நிர்வகிக்க இந்த ஆப்ஸ் தேவைப்படுகிறது. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string> + <string name="consent_no" msgid="2640796915611404382">"அனுமதிக்க வேண்டாம்"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml index 557515af6ac8..58ebda67e685 100644 --- a/packages/CompanionDeviceManager/res/values-th/strings.xml +++ b/packages/CompanionDeviceManager/res/values-th/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะให้มีการจัดการโดย <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string> <string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"ตั้งค่าให้ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> จัดการ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ของคุณ"</string> + <string name="profile_summary" msgid="2059360676631420073">"ต้องใช้แอปนี้ในการจัดการ<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ของคุณ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string> + <string name="consent_no" msgid="2640796915611404382">"ไม่อนุญาต"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml index 6cbf2e875881..1a8328464b3e 100644 --- a/packages/CompanionDeviceManager/res/values-tl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para pamahalaan ng <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="profile_name_watch" msgid="576290739483672360">"relo"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Itakda ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para pamahalaan ang iyong <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Kinakailangan ang app na ito para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Payagan"</string> + <string name="consent_no" msgid="2640796915611404382">"Huwag payagan"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml index bc1ab31d9b90..b89e8748e04e 100644 --- a/packages/CompanionDeviceManager/res/values-tr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> tarafından yönetilecek bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string> <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string> <string name="profile_name_watch" msgid="576290739483672360">"saat"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazınızı yönetmek için <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasını ayarlayın"</string> + <string name="profile_summary" msgid="2059360676631420073">"Bu uygulama, <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizin yönetilmesi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string> + <string name="consent_no" msgid="2640796915611404382">"İzin verme"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml index 33477c8f1429..2ae852cb7c3f 100644 --- a/packages/CompanionDeviceManager/res/values-uk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, яким керуватиме додаток <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string> <string name="profile_name_watch" msgid="576290739483672360">"годинник"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Налаштуйте додаток <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, щоб керувати пристроєм <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"Цей додаток потрібен, щоб керувати профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string> + <string name="consent_no" msgid="2640796915611404382">"Не дозволяти"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml index b16db730def4..5681118c6998 100644 --- a/packages/CompanionDeviceManager/res/values-uz/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> boshqaradigan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qurilmasini tanlang"</string> <string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string> <string name="profile_name_watch" msgid="576290739483672360">"soat"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> qurilmasini boshqarish uchun <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasini sozlang"</string> + <string name="profile_summary" msgid="2059360676631420073">"Bu ilova <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilini boshqarish uchun kerak. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string> + <string name="consent_no" msgid="2640796915611404382">"Ruxsat berilmasin"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml index 75d2de37cb14..3bbdb57a227c 100644 --- a/packages/CompanionDeviceManager/res/values-vi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sẽ do <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> quản lý"</string> <string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string> <string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Đặt <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> để quản lý <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> của bạn"</string> + <string name="profile_summary" msgid="2059360676631420073">"Cần có ứng dụng này để quản lý <xliff:g id="PROFILE_NAME">%1$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string> + <string name="consent_no" msgid="2640796915611404382">"Không cho phép"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml index 576f3f46e8ba..be29925b72e2 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"選擇由 <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> 管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string> <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"設定 <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 來管理您的 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> - <strong></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"必須使用此應用程式,才能管理<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"允許"</string> + <string name="consent_no" msgid="2640796915611404382">"不允許"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml index 8a50658ad25c..80448690cec9 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"選擇要讓「<xliff:g id="APP_NAME">%2$s</xliff:g>」<strong></strong>管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string> <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>管理你的「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」<strong></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"你必須使用這個應用程式,才能管理<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"允許"</string> + <string name="consent_no" msgid="2640796915611404382">"不允許"</string> </resources> diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml index fc3f19d72ee2..3ed177cd6e08 100644 --- a/packages/CompanionDeviceManager/res/values-zu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml @@ -20,12 +20,8 @@ <string name="chooser_title" msgid="2262294130493605839">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ezophathwa yi-<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string> <string name="profile_name_watch" msgid="576290739483672360">"buka"</string> - <!-- no translation found for confirmation_title (814973816731238955) --> - <skip /> - <!-- no translation found for profile_summary (2059360676631420073) --> - <skip /> - <!-- no translation found for consent_yes (8344487259618762872) --> - <skip /> - <!-- no translation found for consent_no (2640796915611404382) --> - <skip /> + <string name="confirmation_title" msgid="814973816731238955">"Setha i-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukuze iphathe i-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> yakho - <strong></strong>"</string> + <string name="profile_summary" msgid="2059360676631420073">"I-app iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string> + <string name="consent_yes" msgid="8344487259618762872">"Vumela"</string> + <string name="consent_no" msgid="2640796915611404382">"Ungavumeli"</string> </resources> diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp index 86b85e8398ce..017ff51f366d 100644 --- a/packages/Connectivity/framework/Android.bp +++ b/packages/Connectivity/framework/Android.bp @@ -23,6 +23,25 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +java_library { + name: "framework-connectivity-protos", + proto: { + type: "nano", + }, + srcs: [ + // TODO: consider moving relevant .proto files directly to the module directory + ":framework-javastream-protos", + ], + apex_available: [ + "//apex_available:platform", + "com.android.tethering", + ], + jarjar_rules: "jarjar-rules-proto.txt", + visibility: [ + "//visibility:private", + ], +} + filegroup { name: "framework-connectivity-internal-sources", srcs: [ @@ -83,3 +102,39 @@ java_sdk_library { ], permitted_packages: ["android.net", "com.android.connectivity.aidl"], } + +java_library { + name: "framework-connectivity.impl", + // Instead of building against private API (framework.jar), + // build against core_platform + framework-minus-apex + module + // stub libs. This allows framework.jar to depend on this library, + // so it can be part of the private API until all clients have been migrated. + // TODO: just build against module_api, and remove this jar from + // the private API. + sdk_version: "core_platform", + srcs: [ + ":framework-connectivity-sources", + ], + aidl: { + include_dirs: [ + "frameworks/base/core/java", // For framework parcelables + "frameworks/native/aidl/binder", // For PersistableBundle.aidl + ], + }, + libs: [ + "framework-minus-apex", + // TODO: just framework-tethering, framework-wifi when building against module_api + "framework-tethering.stubs.module_lib", + "framework-wifi.stubs.module_lib", + "unsupportedappusage", + "ServiceConnectivityResources", + ], + static_libs: [ + "framework-connectivity-protos", + "net-utils-device-common", + ], + jarjar_rules: "jarjar-rules.txt", + apex_available: ["com.android.tethering"], + installable: true, + permitted_packages: ["android.net", "com.android.connectivity.aidl"], +} diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt index bb296476c72f..7a91f6454b90 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -6,16 +6,25 @@ package android.net { } public class ConnectivityManager { + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void factoryReset(); 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 @Nullable public android.net.ProxyInfo getGlobalProxy(); method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange(); method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context); 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, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptPartialConnectivity(@NonNull android.net.Network, boolean, boolean); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptUnvalidated(@NonNull android.net.Network, boolean, boolean); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAvoidUnvalidated(@NonNull android.net.Network); + method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setGlobalProxy(@Nullable android.net.ProxyInfo); method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable); 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); + method public void systemReady(); field public static final String PRIVATE_DNS_MODE_OFF = "off"; field public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic"; field public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname"; + field public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; // 0x0 + field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1 } public final class NetworkAgentConfig implements android.os.Parcelable { diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt index 4dca411cca24..031bb916c4f2 100644 --- a/packages/Connectivity/framework/api/system-current.txt +++ b/packages/Connectivity/framework/api/system-current.txt @@ -67,8 +67,6 @@ package android.net { method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback); field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; - field public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; // 0x0 - field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1 field public static final int TETHERING_BLUETOOTH = 2; // 0x2 field public static final int TETHERING_USB = 1; // 0x1 field public static final int TETHERING_WIFI = 0; // 0x0 diff --git a/packages/Connectivity/framework/jarjar-rules-proto.txt b/packages/Connectivity/framework/jarjar-rules-proto.txt new file mode 100644 index 000000000000..37b4dec1c331 --- /dev/null +++ b/packages/Connectivity/framework/jarjar-rules-proto.txt @@ -0,0 +1,3 @@ +keep android.net.NetworkCapabilitiesProto +keep android.net.NetworkProto +keep android.net.NetworkRequestProto diff --git a/packages/Connectivity/framework/jarjar-rules.txt b/packages/Connectivity/framework/jarjar-rules.txt new file mode 100644 index 000000000000..0959840f2dd2 --- /dev/null +++ b/packages/Connectivity/framework/jarjar-rules.txt @@ -0,0 +1,10 @@ +rule com.android.net.module.util.** android.net.connectivity.framework.util.@1 + +# TODO (b/149403767): remove the annotations from net-utils-device-common instead of here +zap android.annotation.** +zap com.android.net.module.annotation.** +zap com.android.internal.annotations.** + +rule android.net.NetworkCapabilitiesProto* android.net.connectivity.proto.NetworkCapabilitiesProto@1 +rule android.net.NetworkProto* android.net.connectivity.proto.NetworkProto@1 +rule android.net.NetworkRequestProto* android.net.connectivity.proto.NetworkRequestProto@1 diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index ba5eb1090dbf..eac896c6664a 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -16,6 +16,8 @@ package android.net; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE; import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; import static android.net.NetworkRequest.Type.LISTEN; import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST; @@ -23,8 +25,6 @@ import static android.net.NetworkRequest.Type.REQUEST; import static android.net.NetworkRequest.Type.TRACK_DEFAULT; import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT; import static android.net.QosCallback.QosCallbackRegistrationException; -import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE; -import static android.provider.Settings.Global.PRIVATE_DNS_MODE; import android.annotation.CallbackExecutor; import android.annotation.IntDef; @@ -62,7 +62,6 @@ import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; -import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.UserHandle; import android.provider.Settings; @@ -842,7 +841,6 @@ public class ConnectivityManager { private final Context mContext; - private INetworkPolicyManager mNPManager; private final TetheringManager mTetheringManager; /** @@ -915,8 +913,8 @@ public class ConnectivityManager { /** * @hide - * TODO: Expose for SystemServer when becomes a module. */ + @SystemApi(client = MODULE_LIBRARIES) public void systemReady() { try { mService.systemReady(); @@ -975,7 +973,7 @@ public class ConnectivityManager { * Specify that the traffic for this user should by follow the default rules. * @hide */ - @SystemApi + @SystemApi(client = MODULE_LIBRARIES) public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; /** @@ -985,7 +983,7 @@ public class ConnectivityManager { * if no such network is available. * @hide */ - @SystemApi + @SystemApi(client = MODULE_LIBRARIES) public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; /** @hide */ @@ -3038,8 +3036,9 @@ public class ConnectivityManager { * HTTP proxy. A {@code null} value will clear the global HTTP proxy. * @hide */ + @SystemApi(client = MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.NETWORK_STACK) - public void setGlobalProxy(ProxyInfo p) { + public void setGlobalProxy(@Nullable ProxyInfo p) { try { mService.setGlobalProxy(p); } catch (RemoteException e) { @@ -3054,6 +3053,8 @@ public class ConnectivityManager { * if no global HTTP proxy is set. * @hide */ + @SystemApi(client = MODULE_LIBRARIES) + @Nullable public ProxyInfo getGlobalProxy() { try { return mService.getGlobalProxy(); @@ -4388,8 +4389,13 @@ public class ConnectivityManager { * * @hide */ - @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) - public void setAcceptUnvalidated(Network network, boolean accept, boolean always) { + @SystemApi(client = MODULE_LIBRARIES) + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) + public void setAcceptUnvalidated(@NonNull Network network, boolean accept, boolean always) { try { mService.setAcceptUnvalidated(network, accept, always); } catch (RemoteException e) { @@ -4411,8 +4417,14 @@ public class ConnectivityManager { * * @hide */ - @RequiresPermission(android.Manifest.permission.NETWORK_STACK) - public void setAcceptPartialConnectivity(Network network, boolean accept, boolean always) { + @SystemApi(client = MODULE_LIBRARIES) + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) + public void setAcceptPartialConnectivity(@NonNull Network network, boolean accept, + boolean always) { try { mService.setAcceptPartialConnectivity(network, accept, always); } catch (RemoteException e) { @@ -4430,8 +4442,13 @@ public class ConnectivityManager { * * @hide */ - @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) - public void setAvoidUnvalidated(Network network) { + @SystemApi(client = MODULE_LIBRARIES) + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD, + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) + public void setAvoidUnvalidated(@NonNull Network network) { try { mService.setAvoidUnvalidated(network); } catch (RemoteException e) { @@ -4561,7 +4578,10 @@ public class ConnectivityManager { * Resets all connectivity manager settings back to factory defaults. * @hide */ - @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + @SystemApi(client = MODULE_LIBRARIES) + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void factoryReset() { try { mService.factoryReset(); @@ -4764,17 +4784,6 @@ public class ConnectivityManager { public @interface RestrictBackgroundStatus { } - private INetworkPolicyManager getNetworkPolicyManager() { - synchronized (this) { - if (mNPManager != null) { - return mNPManager; - } - mNPManager = INetworkPolicyManager.Stub.asInterface(ServiceManager - .getService(Context.NETWORK_POLICY_SERVICE)); - return mNPManager; - } - } - /** * Determines if the calling application is subject to metered network restrictions while * running on background. @@ -4785,7 +4794,7 @@ public class ConnectivityManager { */ public @RestrictBackgroundStatus int getRestrictBackgroundStatus() { try { - return getNetworkPolicyManager().getRestrictBackgroundByCaller(); + return mService.getRestrictBackgroundStatusByCaller(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/services/core/java/com/android/server/connectivity/ConnectivityResources.java b/packages/Connectivity/framework/src/android/net/ConnectivityResources.java index 45cf21e035ca..18f0de0cc9a1 100644 --- a/services/core/java/com/android/server/connectivity/ConnectivityResources.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityResources.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity; +package android.net; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; @@ -27,13 +27,14 @@ import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.util.Log; -import com.android.server.ConnectivityService; +import com.android.internal.annotations.VisibleForTesting; import java.util.List; /** - * Utility to obtain the {@link ConnectivityService} {@link Resources}, in the + * Utility to obtain the {@link com.android.server.ConnectivityService} {@link Resources}, in the * ServiceConnectivityResources APK. + * @hide */ public class ConnectivityResources { private static final String RESOURCES_APK_INTENT = @@ -44,18 +45,35 @@ public class ConnectivityResources { private final Context mContext; @Nullable - private Resources mResources = null; + private Context mResourcesContext = null; + + @Nullable + private static Context sTestResourcesContext = null; public ConnectivityResources(Context context) { mContext = context; } /** - * Get the {@link Resources} of the ServiceConnectivityResources APK. + * Convenience method to mock all resources for the duration of a test. + * + * Call with a null context to reset after the test. + */ + @VisibleForTesting + public static void setResourcesContextForTest(@Nullable Context testContext) { + sTestResourcesContext = testContext; + } + + /** + * Get the {@link Context} of the resources package. */ - public synchronized Resources get() { - if (mResources != null) { - return mResources; + public synchronized Context getResourcesContext() { + if (sTestResourcesContext != null) { + return sTestResourcesContext; + } + + if (mResourcesContext != null) { + return mResourcesContext; } final List<ResolveInfo> pkgs = mContext.getPackageManager() @@ -77,7 +95,14 @@ public class ConnectivityResources { throw new IllegalStateException("Resolved package not found", e); } - mResources = pkgContext.getResources(); - return mResources; + mResourcesContext = pkgContext; + return pkgContext; + } + + /** + * Get the {@link Resources} of the ServiceConnectivityResources APK. + */ + public Resources get() { + return getResourcesContext().getResources(); } } diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java index d4543654522f..bbd83931ee0d 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java @@ -16,6 +16,11 @@ package android.net; +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A manager class for connectivity module settings. * @@ -25,6 +30,196 @@ public class ConnectivitySettingsManager { private ConnectivitySettingsManager() {} + /** Data activity timeout settings */ + + /** + * Inactivity timeout to track mobile data activity. + * + * If set to a positive integer, it indicates the inactivity timeout value in seconds to + * infer the data activity of mobile network. After a period of no activity on mobile + * networks with length specified by the timeout, an {@code ACTION_DATA_ACTIVITY_CHANGE} + * intent is fired to indicate a transition of network status from "active" to "idle". Any + * subsequent activity on mobile networks triggers the firing of {@code + * ACTION_DATA_ACTIVITY_CHANGE} intent indicating transition from "idle" to "active". + * + * Network activity refers to transmitting or receiving data on the network interfaces. + * + * Tracking is disabled if set to zero or negative value. + */ + public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile"; + + /** + * Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE} + * but for Wifi network. + */ + public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi"; + + /** Dns resolver settings */ + + /** + * Sample validity in seconds to configure for the system DNS resolver. + */ + public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS = + "dns_resolver_sample_validity_seconds"; + + /** + * Success threshold in percent for use with the system DNS resolver. + */ + public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT = + "dns_resolver_success_threshold_percent"; + + /** + * Minimum number of samples needed for statistics to be considered meaningful in the + * system DNS resolver. + */ + public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples"; + + /** + * Maximum number taken into account for statistics purposes in the system DNS resolver. + */ + public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples"; + + /** Network switch notification settings */ + + /** + * The maximum number of notifications shown in 24 hours when switching networks. + */ + public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT = + "network_switch_notification_daily_limit"; + + /** + * The minimum time in milliseconds between notifications when switching networks. + */ + public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS = + "network_switch_notification_rate_limit_millis"; + + /** Captive portal settings */ + + /** + * The URL used for HTTP captive portal detection upon a new connection. + * A 204 response code from the server is used for validation. + */ + public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; + + /** + * What to do when connecting a network that presents a captive portal. + * Must be one of the CAPTIVE_PORTAL_MODE_* constants above. + * + * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. + */ + public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; + + /** + * Don't attempt to detect captive portals. + */ + public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; + + /** + * When detecting a captive portal, display a notification that + * prompts the user to sign in. + */ + public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; + + /** + * When detecting a captive portal, immediately disconnect from the + * network and do not reconnect to that network in the future. + */ + public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + CAPTIVE_PORTAL_MODE_IGNORE, + CAPTIVE_PORTAL_MODE_PROMPT, + CAPTIVE_PORTAL_MODE_AVOID, + }) + public @interface CaptivePortalMode {} + + /** Global http proxy settings */ + + /** + * Host name for global http proxy. Set via ConnectivityManager. + */ + public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host"; + + /** + * Integer host port for global http proxy. Set via ConnectivityManager. + */ + public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port"; + + /** + * Exclusion list for global proxy. This string contains a list of + * comma-separated domains where the global proxy does not apply. + * Domains should be listed in a comma- separated list. Example of + * acceptable formats: ".domain1.com,my.domain2.com" Use + * ConnectivityManager to set/get. + */ + public static final String GLOBAL_HTTP_PROXY_EXCLUSION_LIST = + "global_http_proxy_exclusion_list"; + + /** + * The location PAC File for the proxy. + */ + public static final String GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url"; + + /** Private dns settings */ + + /** + * The requested Private DNS mode (string), and an accompanying specifier (string). + * + * Currently, the specifier holds the chosen provider name when the mode requests + * a specific provider. It may be used to store the provider name even when the + * mode changes so that temporarily disabling and re-enabling the specific + * provider mode does not necessitate retyping the provider hostname. + */ + public static final String PRIVATE_DNS_MODE = "private_dns_mode"; + + /** + * The specific Private DNS provider name. + */ + public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier"; + + /** + * Forced override of the default mode (hardcoded as "automatic", nee "opportunistic"). + * This allows changing the default mode without effectively disabling other modes, + * all of which require explicit user action to enable/configure. See also b/79719289. + * + * Value is a string, suitable for assignment to PRIVATE_DNS_MODE above. + */ + public static final String PRIVATE_DNS_DEFAULT_MODE = "private_dns_default_mode"; + + /** Other settings */ + + /** + * The number of milliseconds to hold on to a PendingIntent based request. This delay gives + * the receivers of the PendingIntent an opportunity to make a new network request before + * the Network satisfying the request is potentially removed. + */ + public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS = + "connectivity_release_pending_intent_delay_ms"; + + /** + * Whether the mobile data connection should remain active even when higher + * priority networks like WiFi are active, to help make network switching faster. + * + * See ConnectivityService for more info. + * + * (0 = disabled, 1 = enabled) + */ + public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on"; + + /** + * Whether the wifi data connection should remain active even when higher + * priority networks like Ethernet are active, to keep both networks. + * In the case where higher priority networks are connected, wifi will be + * unused unless an application explicitly requests to use it. + * + * See ConnectivityService for more info. + * + * (0 = disabled, 1 = enabled) + */ + public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested"; + /** * Whether to automatically switch away from wifi networks that lose Internet access. * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index d83cc163b53f..98f3d40c0b07 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -220,4 +220,6 @@ interface IConnectivityManager void setProfileNetworkPreference(in UserHandle profile, int preference, in IOnCompleteListener listener); + + int getRestrictBackgroundStatusByCaller(); } diff --git a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java index bf5b26e278f9..85b24713f256 100644 --- a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java @@ -19,12 +19,12 @@ package android.net.apf; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.content.Context; import android.content.res.Resources; +import android.net.ConnectivityResources; import android.os.Parcel; import android.os.Parcelable; -import com.android.internal.R; - /** * APF program support capabilities. APF stands for Android Packet Filtering and it is a flexible * way to drop unwanted network packets to save power. @@ -36,6 +36,8 @@ import com.android.internal.R; */ @SystemApi public final class ApfCapabilities implements Parcelable { + private static ConnectivityResources sResources; + /** * Version of APF instruction set supported for packet filtering. 0 indicates no support for * packet filtering using APF programs. @@ -65,6 +67,14 @@ public final class ApfCapabilities implements Parcelable { apfPacketFormat = in.readInt(); } + @NonNull + private static synchronized ConnectivityResources getResources(@NonNull Context ctx) { + if (sResources == null) { + sResources = new ConnectivityResources(ctx); + } + return sResources; + } + @Override public int describeContents() { @@ -121,13 +131,43 @@ public final class ApfCapabilities implements Parcelable { * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames. */ public static boolean getApfDrop8023Frames() { - return Resources.getSystem().getBoolean(R.bool.config_apfDrop802_3Frames); + // TODO(b/183076074): remove reading resources from system resources + final Resources systemRes = Resources.getSystem(); + final int id = systemRes.getIdentifier("config_apfDrop802_3Frames", "bool", "android"); + return systemRes.getBoolean(id); + } + + /** + * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames. + * @hide + */ + public static boolean getApfDrop8023Frames(@NonNull Context context) { + final ConnectivityResources res = getResources(context); + // TODO(b/183076074): use R.bool.config_apfDrop802_3Frames directly + final int id = res.get().getIdentifier("config_apfDrop802_3Frames", "bool", + res.getResourcesContext().getPackageName()); + return res.get().getBoolean(id); } /** * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped. */ public static @NonNull int[] getApfEtherTypeBlackList() { - return Resources.getSystem().getIntArray(R.array.config_apfEthTypeBlackList); + // TODO(b/183076074): remove reading resources from system resources + final Resources systemRes = Resources.getSystem(); + final int id = systemRes.getIdentifier("config_apfEthTypeBlackList", "array", "android"); + return systemRes.getIntArray(id); + } + + /** + * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped. + * @hide + */ + public static @NonNull int[] getApfEtherTypeDenyList(@NonNull Context context) { + final ConnectivityResources res = getResources(context); + // TODO(b/183076074): use R.array.config_apfEthTypeDenyList directly + final int id = res.get().getIdentifier("config_apfEthTypeDenyList", "array", + res.getResourcesContext().getPackageName()); + return res.get().getIntArray(id); } } diff --git a/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java b/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java index bfc4563fbf8f..8d7a0b3d02ed 100644 --- a/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java +++ b/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java @@ -19,12 +19,11 @@ package android.net.util; import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; +import android.net.ConnectivityResources; import android.net.NetworkCapabilities; import android.text.TextUtils; import android.util.AndroidRuntimeException; -import com.android.internal.R; - /** * Collection of utilities for socket keepalive offload. * @@ -52,8 +51,11 @@ public final class KeepaliveUtils { public static int[] getSupportedKeepalives(@NonNull Context context) { String[] res = null; try { - res = context.getResources().getStringArray( - R.array.config_networkSupportedKeepaliveCount); + final ConnectivityResources connRes = new ConnectivityResources(context); + // TODO: use R.id.config_networkSupportedKeepaliveCount directly + final int id = connRes.get().getIdentifier("config_networkSupportedKeepaliveCount", + "array", connRes.getResourcesContext().getPackageName()); + res = new ConnectivityResources(context).get().getStringArray(id); } catch (Resources.NotFoundException unused) { } if (res == null) throw new KeepaliveDeviceConfigurationException("invalid resource"); diff --git a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java index 6a49aa2576c3..0b42a0036925 100644 --- a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java +++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java @@ -27,6 +27,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.database.ContentObserver; +import android.net.ConnectivityResources; import android.net.Uri; import android.os.Handler; import android.provider.Settings; @@ -35,7 +36,6 @@ import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.Log; -import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import java.util.Arrays; @@ -64,6 +64,7 @@ public class MultinetworkPolicyTracker { private static String TAG = MultinetworkPolicyTracker.class.getSimpleName(); private final Context mContext; + private final ConnectivityResources mResources; private final Handler mHandler; private final Runnable mAvoidBadWifiCallback; private final List<Uri> mSettingsUris; @@ -107,6 +108,7 @@ public class MultinetworkPolicyTracker { public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) { mContext = ctx; + mResources = new ConnectivityResources(ctx); mHandler = handler; mAvoidBadWifiCallback = avoidBadWifiCallback; mSettingsUris = Arrays.asList( @@ -160,12 +162,16 @@ public class MultinetworkPolicyTracker { * Whether the device or carrier configuration disables avoiding bad wifi by default. */ public boolean configRestrictsAvoidBadWifi() { - return (getResourcesForActiveSubId().getInteger(R.integer.config_networkAvoidBadWifi) == 0); + // TODO: use R.integer.config_networkAvoidBadWifi directly + final int id = mResources.get().getIdentifier("config_networkAvoidBadWifi", + "integer", mResources.getResourcesContext().getPackageName()); + return (getResourcesForActiveSubId().getInteger(id) == 0); } @NonNull private Resources getResourcesForActiveSubId() { - return SubscriptionManager.getResourcesForSubId(mContext, mActiveSubId); + return SubscriptionManager.getResourcesForSubId( + mResources.getResourcesContext(), mActiveSubId); } /** @@ -205,8 +211,10 @@ public class MultinetworkPolicyTracker { * The default (device and carrier-dependent) value for metered multipath preference. */ public int configMeteredMultipathPreference() { - return mContext.getResources().getInteger( - R.integer.config_networkMeteredMultipathPreference); + // TODO: use R.integer.config_networkMeteredMultipathPreference directly + final int id = mResources.get().getIdentifier("config_networkMeteredMultipathPreference", + "integer", mResources.getResourcesContext().getPackageName()); + return mResources.get().getInteger(id); } public void updateMeteredMultipathPreference() { diff --git a/packages/Connectivity/service/ServiceConnectivityResources/Android.bp b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp index f2446b7f7eb8..fa4501ac7f29 100644 --- a/packages/Connectivity/service/ServiceConnectivityResources/Android.bp +++ b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp @@ -21,7 +21,7 @@ package { android_app { name: "ServiceConnectivityResources", - sdk_version: "system_current", + sdk_version: "module_current", resource_dirs: [ "res", ], diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc204-mnc04/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc204-mnc04/config.xml new file mode 100644 index 000000000000..7e7025fb042f --- /dev/null +++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc204-mnc04/config.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- Configuration values for ConnectivityService + DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources + Overlay package following the overlayable.xml configuration in the same directory: + https://source.android.com/devices/architecture/rros --> +<resources> + <!-- Whether the device should automatically switch away from Wi-Fi networks that lose + Internet access. Actual device behaviour is controlled by + Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. --> + <integer translatable="false" name="config_networkAvoidBadWifi">0</integer> +</resources>
\ No newline at end of file diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc310-mnc004/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc310-mnc004/config.xml new file mode 100644 index 000000000000..7e7025fb042f --- /dev/null +++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc310-mnc004/config.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- Configuration values for ConnectivityService + DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources + Overlay package following the overlayable.xml configuration in the same directory: + https://source.android.com/devices/architecture/rros --> +<resources> + <!-- Whether the device should automatically switch away from Wi-Fi networks that lose + Internet access. Actual device behaviour is controlled by + Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. --> + <integer translatable="false" name="config_networkAvoidBadWifi">0</integer> +</resources>
\ No newline at end of file diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc311-mnc480/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc311-mnc480/config.xml new file mode 100644 index 000000000000..7e7025fb042f --- /dev/null +++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values-mcc311-mnc480/config.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- Configuration values for ConnectivityService + DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources + Overlay package following the overlayable.xml configuration in the same directory: + https://source.android.com/devices/architecture/rros --> +<resources> + <!-- Whether the device should automatically switch away from Wi-Fi networks that lose + Internet access. Actual device behaviour is controlled by + Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. --> + <integer translatable="false" name="config_networkAvoidBadWifi">0</integer> +</resources>
\ No newline at end of file diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml index 06c81921fd3f..71674e4dc606 100644 --- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml +++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml @@ -52,4 +52,41 @@ <item>12,60000</item><!-- mobile_cbs --> </string-array> -</resources>
\ No newline at end of file + <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames + Those frames are identified by the field Eth-type having values + less than 0x600 --> + <bool translatable="false" name="config_apfDrop802_3Frames">true</bool> + + <!-- An array of Denylisted EtherType, packets with EtherTypes within this array + will be dropped + TODO: need to put proper values, these are for testing purposes only --> + <integer-array translatable="false" name="config_apfEthTypeDenyList"> + <item>0x88A2</item> + <item>0x88A4</item> + <item>0x88B8</item> + <item>0x88CD</item> + <item>0x88E3</item> + </integer-array> + + <!-- Default supported concurrent socket keepalive slots per transport type, used by + ConnectivityManager.createSocketKeepalive() for calculating the number of keepalive + offload slots that should be reserved for privileged access. This string array should be + overridden by the device to present the capability of creating socket keepalives. --> + <!-- An Array of "[NetworkCapabilities.TRANSPORT_*],[supported keepalives] --> + <string-array translatable="false" name="config_networkSupportedKeepaliveCount"> + <item>0,1</item> + <item>1,3</item> + </string-array> + + + <!-- Default value for ConnectivityManager.getMultipathPreference() on metered networks. Actual + device behaviour is controlled by the metered multipath preference in + ConnectivitySettingsManager. This is the default value of that setting. --> + <integer translatable="false" name="config_networkMeteredMultipathPreference">0</integer> + + <!-- Whether the device should automatically switch away from Wi-Fi networks that lose + Internet access. Actual device behaviour is controlled by + Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. --> + <integer translatable="false" name="config_networkAvoidBadWifi">1</integer> + +</resources> diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml index da8aee56276c..25e19cedbbba 100644 --- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml +++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml @@ -21,6 +21,11 @@ <item type="string" name="config_networkCaptivePortalServerUrl"/> <item type="integer" name="config_networkTransitionTimeout"/> <item type="array" name="config_wakeonlan_supported_interfaces"/> + <item type="bool" name="config_apfDrop802_3Frames"/> + <item type="array" name="config_apfEthTypeDenyList"/> + <item type="integer" name="config_networkMeteredMultipathPreference"/> + <item type="array" name="config_networkSupportedKeepaliveCount"/> + <item type="integer" name="config_networkAvoidBadWifi"/> </policy> </overlayable> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index efa9f3c16b48..d801f1bbf5e5 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1067,8 +1067,8 @@ <![CDATA[ Adjust how colors display on your device. This can be helpful when you want to:<br/><br/> <ol> - <li> See colors more accurately</li> - <li> Remove colors to help you focus</li> + <li> See colors more accurately</li> + <li> Remove colors to help you focus</li> </ol> ]]></string> <!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] --> diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index 2528ac1767f8..f180776bbe93 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -1496,6 +1496,13 @@ public class ApplicationsState { } } + /** + * Whether the packages for the user have been initialized. + */ + public boolean isUserAdded(int userId) { + return mEntriesMap.contains(userId); + } + public interface Callbacks { void onRunningStateChanged(boolean running); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index cf66bad45f3b..db38ff64cb04 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -430,6 +430,9 @@ <!-- Permission required for CTS test - FontManagerTest --> <uses-permission android:name="android.permission.UPDATE_FONTS" /> + <!-- Permission required for hotword detection service CTS tests --> + <uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index e0097df62078..d3d7a95dd32c 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -602,6 +602,9 @@ android:resource="@xml/people_space_widget_info" /> </receiver> + <receiver android:name=".people.widget.PeopleSpaceWidgetPinnedReceiver" + android:enabled="true"/> + <!-- Widget service --> <service android:name=".people.widget.PeopleSpaceWidgetService" android:permission="android.permission.BIND_REMOTEVIEWS" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml index 71cdaf5c7091..384e02d62265 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml @@ -22,7 +22,6 @@ android:clipToPadding="false"> <include - style="@style/BouncerSecurityContainer" layout="@layout/keyguard_host_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index b6a41c20a607..6b4fbdbc209d 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -77,8 +77,9 @@ android:fontFamily="@font/clock" android:typeface="monospace" android:elegantTextHeight="false" + android:singleLine="true" dozeWeight="200" - lockScreenWeight="300" + lockScreenWeight="400" /> </FrameLayout> <FrameLayout @@ -109,8 +110,6 @@ <com.android.systemui.statusbar.phone.NotificationIconContainer android:id="@+id/left_aligned_notification_icon_container" - android:paddingStart="16dp" - android:paddingEnd="16dp" android:layout_width="match_parent" android:layout_height="@dimen/notification_shelf_height" android:layout_marginTop="@dimen/widget_vertical_padding" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml index 4db2280fdc0a..9f3ca7459a6b 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml @@ -20,8 +20,6 @@ <!-- This is a view that shows general status information in Keyguard. --> <com.android.keyguard.KeyguardSliceView xmlns:android="http://schemas.android.com/apk/res/android" - android:paddingStart="16dp" - android:paddingEnd="16dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" diff --git a/packages/SystemUI/res-keyguard/values-sw600dp/styles.xml b/packages/SystemUI/res-keyguard/values-sw600dp/integers.xml index e632e7603259..a35fa3afa8f1 100644 --- a/packages/SystemUI/res-keyguard/values-sw600dp/styles.xml +++ b/packages/SystemUI/res-keyguard/values-sw600dp/integers.xml @@ -1,5 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2014 The Android Open Source Project + ~ Copyright (C) 2021 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -11,11 +12,12 @@ ~ 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 + ~ limitations under the License. --> - <resources> - <style name="BouncerSecurityContainer"> - <item name="android:layout_gravity">center</item> - </style> -</resources>
\ No newline at end of file + <!-- This needs to be specified in an integer, rather than a style, as it can change in response + to device config changes, and we need to be able to change it without re-inflation. + + 0x11 = center --> + <integer name="keyguard_host_view_gravity">0x11</integer> +</resources> diff --git a/packages/SystemUI/res-keyguard/values/integers.xml b/packages/SystemUI/res-keyguard/values/integers.xml new file mode 100644 index 000000000000..6f14dc9ba2e1 --- /dev/null +++ b/packages/SystemUI/res-keyguard/values/integers.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. + --> +<resources> + <!-- This needs to be specified in an integer, rather than a style, as it can change in response + to device config changes, and we need to be able to change it without re-inflation. + + 0x50 = bottom, 0x01 = center_horizontal --> + <integer name="keyguard_host_view_gravity">0x51</integer> +</resources> diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 8f42cbe31646..2ffc9927480e 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -100,10 +100,6 @@ <item name="android:shadowRadius">?attr/shadowRadius</item> </style> - <style name="BouncerSecurityContainer"> - <item name="android:layout_gravity">center_horizontal|bottom</item> - </style> - <style name="PasswordTheme" parent="Theme.SystemUI"> <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:colorControlNormal">?android:attr/textColorPrimary</item> diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml index 8efe0539207a..73b02f4fa481 100644 --- a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml +++ b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml @@ -18,33 +18,20 @@ android:paddingMode="stack" > <item android:id="@android:id/background" android:gravity="center_vertical|fill_horizontal"> - <layer-list> - <item> - <shape - android:tint="?android:attr/colorControlActivated" - android:alpha="?android:attr/disabledAlpha"> - <size android:height="@dimen/rounded_slider_height" /> - <solid android:color="@color/white_disabled" /> - <corners android:radius="@dimen/rounded_slider_corner_radius" /> - </shape> - </item> - <item - android:gravity="center_vertical|left" - android:height="@dimen/rounded_slider_icon_size" - android:width="@dimen/rounded_slider_icon_size" - android:left="@dimen/rounded_slider_icon_inset"> - <com.android.systemui.util.AlphaTintDrawableWrapper - android:drawable="@drawable/ic_brightness" - android:tint="?android:attr/colorControlActivated" /> - </item> - </layer-list> + <inset + android:insetLeft="@dimen/rounded_slider_track_inset" + android:insetRight="@dimen/rounded_slider_track_inset" > + <shape> + <size android:height="@dimen/rounded_slider_track_width" /> + <corners android:radius="@dimen/rounded_slider_track_corner_radius" /> + <solid android:color="?android:attr/textColorPrimary" /> + </shape> + </inset> </item> <item android:id="@android:id/progress" android:gravity="center_vertical|fill_horizontal"> - <clip + <com.android.systemui.util.RoundedCornerProgressDrawable android:drawable="@drawable/brightness_progress_full_drawable" - android:clipOrientation="horizontal" - android:gravity="left" /> </item> </layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml index 5bc2773dc657..41140a7a8c85 100644 --- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml +++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml @@ -26,10 +26,10 @@ </item> <item android:id="@+id/slider_icon" - android:gravity="center_vertical|left" + android:gravity="center_vertical|right" android:height="@dimen/rounded_slider_icon_size" android:width="@dimen/rounded_slider_icon_size" - android:left="@dimen/rounded_slider_icon_inset"> + android:right="@dimen/rounded_slider_icon_inset"> <com.android.systemui.util.AlphaTintDrawableWrapper android:drawable="@drawable/ic_brightness" android:tint="?android:attr/colorBackground" diff --git a/packages/SystemUI/res/drawable/ic_qs_wallet.xml b/packages/SystemUI/res/drawable/ic_qs_wallet.xml new file mode 100644 index 000000000000..e146eabecfd3 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_wallet.xml @@ -0,0 +1,11 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M20,4L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2, + -0.89 2,-2L22,6c0,-1.11 -0.89,-2 -2,-2zM20,18L4,18v-6h16v6zM20,8L4,8L4,6h16v2z"/> +</vector> diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml index d36c1a8c961b..42f14f30da00 100644 --- a/packages/SystemUI/res/layout/notif_half_shelf.xml +++ b/packages/SystemUI/res/layout/notif_half_shelf.xml @@ -67,7 +67,7 @@ android:textSize="15sp" android:ellipsize="end" android:maxLines="1" - style="@style/TextAppearance.NotificationInfo.Title" /> + style="@style/TextAppearance.NotificationImportanceChannel" /> <Switch android:id="@+id/toggle" @@ -80,7 +80,7 @@ <View android:layout_width="match_parent" android:layout_height="1dp" - android:background="@color/notification_channel_dialog_separator" + android:background="?android:attr/colorAccent" /> <!-- ChannelRows get added dynamically --> @@ -90,7 +90,7 @@ <View android:layout_width="match_parent" android:layout_height="1dp" - android:background="@color/notification_channel_dialog_separator" + android:background="?android:attr/colorAccent" /> <RelativeLayout android:id="@+id/bottom_actions" diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml index c863e02d5313..87de28650662 100644 --- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml +++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml @@ -53,7 +53,7 @@ android:textSize="14sp" android:ellipsize="end" android:maxLines="1" - style="@style/TextAppearance.NotificationInfo.Title" + style="@style/TextAppearance.NotificationImportanceChannel" /> <TextView @@ -67,7 +67,7 @@ android:ellipsize="end" android:maxLines="1" android:layout_below="@id/channel_name" - style="@style/TextAppearance.NotificationInfo.Secondary" + style="@style/TextAppearance.NotificationImportanceApp" /> </RelativeLayout> diff --git a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl index e4b6e0778664..e9c838999dda 100644 --- a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl +++ b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl @@ -1,11 +1,54 @@ precision mediump float; +#define GAMMA 2.2 +#define INV_GAMMA 1.0 / GAMMA // The actual wallpaper texture. uniform sampler2D uTexture; +uniform float uExposure; varying vec2 vTextureCoordinates; +// Following the Rec. ITU-R BT.709. +float relativeLuminance(vec3 color) { + return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; +} + +// Adjusts the exposure of some luminance value. +float relativeExposureCompensation(in float lum, in float ev) { + return lum * pow(2.0, ev); +} + +vec4 srgbToLinear(in vec4 color) { + vec4 linearColor = vec4(color); + linearColor.rgb = pow(linearColor.rgb, vec3(GAMMA)); + return linearColor; +} + +vec4 linearToSrgb(in vec4 color) { + vec4 srgbColor = vec4(color); + srgbColor.rgb = pow(srgbColor.rgb, vec3(INV_GAMMA)); + return srgbColor; +} + +/* + * Normalizes a value inside a range to a normalized range [0,1]. + */ +float normalizedRange(in float value, in float inMin, in float inMax) { + float valueClamped = clamp(value, inMin, inMax); + return (value - inMin) / (inMax - inMin); +} + void main() { - // gets the pixel value of the wallpaper for this uv coordinates on screen. - gl_FragColor = texture2D(uTexture, vTextureCoordinates); + // Gets the pixel value of the wallpaper for this uv coordinates on screen. + vec4 color = srgbToLinear(texture2D(uTexture, vTextureCoordinates)); + float lum = relativeLuminance(color.rgb); + + // Transform it using the S curve created by the smoothstep. This will increase the contrast. + lum = smoothstep(0., 1., lum) + 0.001; + + lum = relativeExposureCompensation(lum, mix(-15., 10., uExposure)); + lum = mix(clamp(lum, 0.0, 1.0), 1.0, normalizedRange(uExposure, 0.55, 1.0)); + color.rgb *= lum; + + gl_FragColor = linearToSrgb(color); }
\ No newline at end of file diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index d4783197e1f1..55365bd3c692 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -115,8 +115,6 @@ <color name="notification_section_header_label_color">@color/GM2_grey_900</color> <color name="notification_section_clear_all_btn_color">@color/GM2_grey_700</color> - <!-- The divider view for the notification channel editor half-shelf --> - <color name="notification_channel_dialog_separator">@color/GM2_grey_200</color> <color name="assist_orb_color">#ffffff</color> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index b0c5239bb4b8..af6df32a02b0 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -107,7 +107,7 @@ <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> <string name="quick_settings_tiles_stock" translatable="false"> - wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls,alarm + wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle,controls,alarm,wallet </string> <!-- The tiles to display in QuickSettings --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 2062104be2df..6e270a75a6b9 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1356,11 +1356,15 @@ <dimen name="people_space_image_radius">20dp</dimen> <dimen name="people_space_widget_background_padding">6dp</dimen> - <dimen name="rounded_slider_height">48dp</dimen> + <dimen name="rounded_slider_height">44dp</dimen> <!-- rounded_slider_height / 2 --> - <dimen name="rounded_slider_corner_radius">24dp</dimen> - <!-- rounded_slider_height / 2 --> - <dimen name="rounded_slider_icon_size">24dp</dimen> - <!-- rounded_slider_icon_size / 2 --> + <dimen name="rounded_slider_corner_radius">22dp</dimen> + <dimen name="rounded_slider_icon_size">20dp</dimen> + <!-- (rounded_slider_height - rounded_slider_icon_size) / 2 --> <dimen name="rounded_slider_icon_inset">12dp</dimen> + <!-- rounded_slider_corner_radius - rounded_slider_track_corner_radius --> + <dimen name="rounded_slider_track_inset">18dp</dimen> + <dimen name="rounded_slider_track_width">8dp</dimen> + <!-- rounded_slider_track_width / 2 --> + <dimen name="rounded_slider_track_corner_radius">4dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index 8cd5757247e2..2163806666d9 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -38,6 +38,8 @@ <!-- People Tile flag --> <bool name="flag_conversations">false</bool> + <bool name="flag_wallet">false</bool> + <!-- The new animations to/from lockscreen and AOD! --> <bool name="flag_lockscreen_animations">false</bool> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 57c932eaa8e4..78180a7a80a8 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1337,7 +1337,7 @@ <string name="monitoring_description_named_management">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g>.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string> <!-- Dialog that a user can access via Quick Settings. The dialog describes what a Creditor can monitor (and the changes they can make) on the user's financed device. [CHAR LIMIT=NONE]--> - <string name="monitoring_financed_description_named_management"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> may be able to access data associated with this device and change this device\’s settings.\n\nIf you have questions, contact <xliff:g id="organization_name" example="Foo, Inc.">%2$s</xliff:g>.</string> + <string name="monitoring_financed_description_named_management"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> may be able to access data associated with this device, manage apps, and change this device\’s settings.\n\nIf you have questions, contact <xliff:g id="organization_name" example="Foo, Inc.">%2$s</xliff:g>.</string> <!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]--> <string name="monitoring_description_management">This device belongs to your organization.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string> @@ -1621,6 +1621,12 @@ <!-- Name of the alarm status bar icon. --> <string name="status_bar_alarm">Alarm</string> + <!-- Wallet strings --> + <!-- Wallet empty state, title [CHAR LIMIT=32] --> + <string name="wallet_title">Wallet</string> + <!-- Secondary label of the quick access wallet tile. [CHAR LIMIT=32] --> + <string name="wallet_secondary_label">Ready</string> + <!-- Name of the work status bar icon. --> <string name="status_bar_work">Work profile</string> diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java index 64b3d7356d3c..c918d9871a7d 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java @@ -38,8 +38,10 @@ import kotlin.Unit; * The time's text color is a gradient that changes its colors based on its controller. */ public class AnimatableClockView extends TextView { - private static final CharSequence FORMAT_12_HOUR = "hh\nmm"; - private static final CharSequence FORMAT_24_HOUR = "HH\nmm"; + private static final CharSequence DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm"; + private static final CharSequence DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm"; + private static final CharSequence SINGLE_LINE_FORMAT_12_HOUR = "h:mm"; + private static final CharSequence SINGLE_LINE_FORMAT_24_HOUR = "H:mm"; private static final long ANIM_DURATION = 300; private final Calendar mTime = Calendar.getInstance(); @@ -55,6 +57,8 @@ public class AnimatableClockView extends TextView { private TextAnimator mTextAnimator = null; private Runnable mOnTextAnimatorInitialized; + private boolean mIsSingleLine; + public AnimatableClockView(Context context) { this(context, null, 0, 0); } @@ -78,6 +82,15 @@ public class AnimatableClockView extends TextView { } finally { ta.recycle(); } + + ta = context.obtainStyledAttributes( + attrs, android.R.styleable.TextView, defStyleAttr, defStyleRes); + try { + mIsSingleLine = ta.getBoolean(android.R.styleable.TextView_singleLine, false); + } finally { + ta.recycle(); + } + refreshFormat(); } @@ -171,7 +184,16 @@ public class AnimatableClockView extends TextView { void refreshFormat() { final boolean use24HourFormat = DateFormat.is24HourFormat(getContext()); - mFormat = use24HourFormat ? FORMAT_24_HOUR : FORMAT_12_HOUR; + if (mIsSingleLine && use24HourFormat) { + mFormat = SINGLE_LINE_FORMAT_24_HOUR; + } else if (!mIsSingleLine && use24HourFormat) { + mFormat = DOUBLE_LINE_FORMAT_24_HOUR; + } else if (mIsSingleLine && !use24HourFormat) { + mFormat = SINGLE_LINE_FORMAT_12_HOUR; + } else { + mFormat = DOUBLE_LINE_FORMAT_12_HOUR; + } + mDescFormat = getBestDateTimePattern(getContext(), use24HourFormat ? "Hm" : "hm"); refreshTime(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 93ed0eaa3fba..13a84fb605f0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -174,7 +174,7 @@ public class KeyguardClockSwitch extends RelativeLayout { if (mode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { final int startEndPadding = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, - 12, + 32, getResources().getDisplayMetrics()); setPaddingRelative(startEndPadding, 0, startEndPadding, 0); mSmallClockFrame.setVisibility(GONE); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index ea60f0d40369..72dd72eb1676 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -29,6 +29,7 @@ import android.view.KeyEvent; import android.view.View; import android.view.View.OnKeyListener; import android.view.ViewTreeObserver; +import android.widget.FrameLayout; import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; @@ -180,6 +181,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> /** Initialize the Controller. */ public void onInit() { mKeyguardSecurityContainerController.init(); + updateResources(); } @Override @@ -467,5 +469,23 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> mSecurityCallback.finish(strongAuth, currentUser); } - + /** + * Apply keyguard configuration from the currently active resources. This can be called when the + * device configuration changes, to re-apply some resources that are qualified on the device + * configuration. + */ + public void updateResources() { + int gravity = mView.getResources().getInteger(R.integer.keyguard_host_view_gravity); + + // Android SysUI uses a FrameLayout as the top-level, but Auto uses RelativeLayout. + // We're just changing the gravity here though (which can't be applied to RelativeLayout), + // so only attempt the update if mView is inside a FrameLayout. + if (mView.getLayoutParams() instanceof FrameLayout.LayoutParams) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mView.getLayoutParams(); + if (lp.gravity != gravity) { + lp.gravity = gravity; + mView.setLayoutParams(lp); + } + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 4f48bb4f3260..e16c01a4aa36 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -109,7 +109,7 @@ public class KeyguardPatternViewController // Treat single-sized patterns as erroneous taps. if (pattern.size() == 1) { mFalsingCollector.updateFalseConfidence(FalsingClassifier.Result.falsed( - 0.7, "empty pattern input")); + 0.7, getClass().getSimpleName(), "empty pattern input")); } mLockPatternView.enableInput(); onPatternChecked(userId, false, 0, false /* not valid - too short */); diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 7966b388bc4f..8b68ae959ebf 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -29,6 +29,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.MathUtils; import android.util.Size; +import android.view.Choreographer; import android.view.SurfaceHolder; import android.view.WindowManager; @@ -37,6 +38,7 @@ import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.glwallpaper.EglHelper; import com.android.systemui.glwallpaper.ImageWallpaperRenderer; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -53,10 +55,11 @@ public class ImageWallpaper extends WallpaperService { private static final String TAG = ImageWallpaper.class.getSimpleName(); // We delayed destroy render context that subsequent render requests have chance to cancel it. // This is to avoid destroying then recreating render context in a very short time. - private static final int DELAY_FINISH_RENDERING = 1000; + private static final int DELAY_FINISH_RENDERING = 3000; private static final @android.annotation.NonNull RectF LOCAL_COLOR_BOUNDS = new RectF(0, 0, 1, 1); private static final boolean DEBUG = false; + private final StatusBarStateController mStatusBarStateController; private final ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>(); private final ArraySet<RectF> mColorAreas = new ArraySet<>(); private float mShift; @@ -66,8 +69,9 @@ public class ImageWallpaper extends WallpaperService { private Bitmap mMiniBitmap; @Inject - public ImageWallpaper() { + public ImageWallpaper(StatusBarStateController statusBarStateController) { super(); + mStatusBarStateController = statusBarStateController; } @Override @@ -90,7 +94,9 @@ public class ImageWallpaper extends WallpaperService { mMiniBitmap = null; } - class GLEngine extends Engine { + + class GLEngine extends Engine implements StatusBarStateController.StateListener, + Choreographer.FrameCallback { // Surface is rejected if size below a threshold on some devices (ie. 8px on elfin) // set min to 64 px (CTS covers this), please refer to ag/4867989 for detail. @VisibleForTesting @@ -101,11 +107,12 @@ public class ImageWallpaper extends WallpaperService { private ImageWallpaperRenderer mRenderer; private EglHelper mEglHelper; private final Runnable mFinishRenderingTask = this::finishRendering; - private boolean mNeedRedraw; private int mWidth = 1; private int mHeight = 1; private int mImgWidth = 1; private int mImgHeight = 1; + private volatile float mDozeAmount; + private volatile boolean mNewDozeValue = false; GLEngine() { } @@ -130,8 +137,14 @@ public class ImageWallpaper extends WallpaperService { mWidth = window.width(); mMiniBitmap = null; if (mWorker != null && mWorker.getThreadHandler() != null) { - mWorker.getThreadHandler().post(this::updateMiniBitmap); + mWorker.getThreadHandler().post(() -> { + updateMiniBitmap(); + Choreographer.getInstance().postFrameCallback(GLEngine.this); + }); } + + mDozeAmount = mStatusBarStateController.getDozeAmount(); + mStatusBarStateController.addCallback(this); } EglHelper getEglHelperInstance() { @@ -210,11 +223,14 @@ public class ImageWallpaper extends WallpaperService { public void onDestroy() { mMiniBitmap = null; mWorker.getThreadHandler().post(() -> { + Choreographer.getInstance().removeFrameCallback(this); mRenderer.finish(); mRenderer = null; mEglHelper.finish(); mEglHelper = null; }); + + mStatusBarStateController.removeCallback(this); } @Override @@ -323,9 +339,16 @@ public class ImageWallpaper extends WallpaperService { @Override public void onSurfaceRedrawNeeded(SurfaceHolder holder) { if (mWorker == null) return; + mDozeAmount = mStatusBarStateController.getDozeAmount(); mWorker.getThreadHandler().post(this::drawFrame); } + @Override + public void onDozeAmountChanged(float linear, float eased) { + mDozeAmount = linear; + mNewDozeValue = true; + } + private void drawFrame() { preRender(); requestRender(); @@ -381,6 +404,7 @@ public class ImageWallpaper extends WallpaperService { && frame.width() > 0 && frame.height() > 0; if (readyToRender) { + mRenderer.setExposureValue(1 - mDozeAmount); mRenderer.onDrawFrame(); if (!mEglHelper.swapBuffer()) { Log.e(TAG, "drawFrame failed!"); @@ -438,5 +462,14 @@ public class ImageWallpaper extends WallpaperService { mEglHelper.dump(prefix, fd, out, args); mRenderer.dump(prefix, fd, out, args); } + + @Override + public void doFrame(long frameTimeNanos) { + if (mNewDozeValue) { + drawFrame(); + mNewDozeValue = false; + } + Choreographer.getInstance().postFrameCallback(this); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index f210d508907c..f28d1137f3e7 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -74,7 +74,7 @@ public final class Prefs { Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, Key.HAS_SEEN_REVERSE_BOTTOM_SHEET, Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT, - Key.HAS_SEEN_PRIORITY_ONBOARDING + Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S }) // TODO: annotate these with their types so {@link PrefsCommandLine} can know how to set them public @interface Key { @@ -124,7 +124,7 @@ public final class Prefs { String HAS_SEEN_REVERSE_BOTTOM_SHEET = "HasSeenReverseBottomSheet"; String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount"; /** Tracks whether the user has seen the onboarding screen for priority conversations */ - String HAS_SEEN_PRIORITY_ONBOARDING = "HasUserSeenPriorityOnboarding"; + String HAS_SEEN_PRIORITY_ONBOARDING_IN_S = "HasUserSeenPriorityOnboardingInS"; } public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java index c1cf8d31bd67..9f77b7d26c96 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java @@ -19,6 +19,7 @@ package com.android.systemui.accessibility; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; import android.annotation.NonNull; +import android.annotation.UiContext; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.PixelFormat; @@ -69,7 +70,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL private final MagnificationGestureDetector mGestureDetector; private boolean mSingleTapDetected = false; - MagnificationModeSwitch(Context context) { + MagnificationModeSwitch(@UiContext Context context) { this(context, createView(context)); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java index 50f6e9f6af44..1a01ad85fccc 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java @@ -16,6 +16,8 @@ package com.android.systemui.accessibility; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; + import android.annotation.MainThread; import android.content.Context; import android.hardware.display.DisplayManager; @@ -106,10 +108,9 @@ public class ModeSwitchesController { @Override protected MagnificationModeSwitch createInstance(Display display) { - final Context context = (display.getDisplayId() == Display.DEFAULT_DISPLAY) - ? mContext - : mContext.createDisplayContext(display); - return new MagnificationModeSwitch(context); + final Context uiContext = mContext.createWindowContext(display, + TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null); + return new MagnificationModeSwitch(uiContext); } } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index f52dcd596a55..cdd69429132a 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -16,6 +16,8 @@ package com.android.systemui.accessibility; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; + import android.annotation.MainThread; import android.annotation.Nullable; import android.content.Context; @@ -82,9 +84,8 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall @Override protected WindowMagnificationAnimationController createInstance(Display display) { - final Context context = (display.getDisplayId() == Display.DEFAULT_DISPLAY) - ? mContext - : mContext.createDisplayContext(display); + final Context windowContext = mContext.createWindowContext(display, + TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null); final WindowMagnificationController controller = new WindowMagnificationController( mContext, mHandler, new SfVsyncFrameCallbackProvider(), null, @@ -92,7 +93,7 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall final int navBarMode = mNavigationModeController.addListener( controller::onNavigationModeChanged); controller.onNavigationModeChanged(navBarMode); - return new WindowMagnificationAnimationController(context, controller); + return new WindowMagnificationAnimationController(windowContext, controller); } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java index 24d83884f093..5758b1575f5a 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.Nullable; +import android.annotation.UiContext; import android.content.Context; import android.content.res.Resources; import android.os.RemoteException; @@ -69,8 +70,8 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp @MagnificationState private int mState = STATE_DISABLED; - WindowMagnificationAnimationController( - Context context, WindowMagnificationController controller) { + WindowMagnificationAnimationController(@UiContext Context context, + WindowMagnificationController controller) { this(context, controller, newValueAnimator(context.getResources())); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 3f7d2d837e2d..2b666f13efcc 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -24,6 +24,7 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UiContext; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Resources; @@ -134,7 +135,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold @Nullable private MirrorWindowControl mMirrorWindowControl; - WindowMagnificationController(Context context, @NonNull Handler handler, + WindowMagnificationController(@UiContext Context context, @NonNull Handler handler, SfVsyncFrameCallbackProvider sfVsyncFrameProvider, MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction, @NonNull WindowMagnifierCallback callback) { @@ -147,7 +148,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mDisplayId = mContext.getDisplayId(); mRotation = display.getRotation(); - mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mWm = context.getSystemService(WindowManager.class); mResources = mContext.getResources(); mScale = mResources.getInteger(R.integer.magnification_default_scale); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java index 2083f2e8368a..d90d0f8d9f67 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java @@ -16,9 +16,11 @@ package com.android.systemui.biometrics; -import android.annotation.NonNull; +import android.annotation.Nullable; import android.view.Surface; +import com.android.systemui.biometrics.HbmTypes.HbmType; + /** * Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to * enable the HBM while showing the fingerprint illumination, and to disable the HBM after the @@ -26,16 +28,20 @@ import android.view.Surface; */ public interface HbmCallback { /** - * UdfpsView will call this to enable the HBM before drawing the illumination dot. + * UdfpsView will call this to enable the HBM when the fingerprint illumination is needed. * - * @param surface A valid surface for which the HBM should be enabled. + * @param hbmType The type of HBM that should be enabled. See {@link HbmTypes}. + * @param surface The surface for which the HBM is requested, in case the HBM implementation + * needs to set special surface flags to enable the HBM. Can be null. */ - void enableHbm(@NonNull Surface surface); + void enableHbm(@HbmType int hbmType, @Nullable Surface surface); /** * UdfpsView will call this to disable the HBM when the illumination is not longer needed. * - * @param surface A valid surface for which the HBM should be disabled. + * @param hbmType The type of HBM that should be disabled. See {@link HbmTypes}. + * @param surface The surface for which the HBM is requested, in case the HBM implementation + * needs to unset special surface flags to disable the HBM. Can be null. */ - void disableHbm(@NonNull Surface surface); + void disableHbm(@HbmType int hbmType, @Nullable Surface surface); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java b/packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java new file mode 100644 index 000000000000..96ee0f7be919 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Different high-brightness mode (HBM) types that are relevant to this package. + */ +public final class HbmTypes { + /** HBM that applies to the whole screen. */ + public static final int GLOBAL_HBM = 0; + + /** HBM that only applies to a portion of the screen. */ + public static final int LOCAL_HBM = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({GLOBAL_HBM, LOCAL_HBM}) + public @interface HbmType { + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 797a4411b8c9..b9f9b1bc85dd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -19,6 +19,8 @@ package com.android.systemui.biometrics; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; @@ -42,11 +44,9 @@ import android.view.Surface; import android.view.VelocityTracker; import android.view.WindowManager; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; +import com.android.systemui.biometrics.HbmTypes.HbmType; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; @@ -95,7 +95,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // Tracks the velocity of a touch to help filter out the touches that move too fast. @Nullable private VelocityTracker mVelocityTracker; // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active. - private int mActivePointerId; + private int mActivePointerId = -1; // The timestamp of the most recent touch log. private long mTouchLogTime; @@ -580,13 +580,13 @@ public class UdfpsController implements DozeReceiver, HbmCallback { } @Override - public void enableHbm(@NonNull Surface surface) { + public void enableHbm(@HbmType int hbmType, @Nullable Surface surface) { // Do nothing. This method can be implemented for devices that require the high-brightness // mode for fingerprint illumination. } @Override - public void disableHbm(@NonNull Surface surface) { + public void disableHbm(@HbmType int hbmType, @Nullable Surface surface) { // Do nothing. This method can be implemented for devices that require the high-brightness // mode for fingerprint illumination. } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java index 61ec127ee946..4d441bd288fe 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java @@ -23,16 +23,25 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.RectF; +import android.os.Build; +import android.os.UserHandle; +import android.provider.Settings; import android.util.AttributeSet; +import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; +import com.android.systemui.biometrics.HbmTypes.HbmType; + /** * Under-display fingerprint sensor Surface View. The surface should be used for HBM-specific things * only. All other animations should be done on the other view. */ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator { private static final String TAG = "UdfpsSurfaceView"; + private static final String SETTING_HBM_TYPE = + "com.android.systemui.biometrics.UdfpsSurfaceView.hbmType"; + private static final @HbmType int DEFAULT_HBM_TYPE = HbmTypes.GLOBAL_HBM; /** * This is used instead of {@link android.graphics.drawable.Drawable}, because the latter has @@ -45,6 +54,7 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator { @NonNull private final SurfaceHolder mHolder; @NonNull private final Paint mSensorPaint; @NonNull private final SimpleDrawable mIlluminationDotDrawable; + private final @HbmType int mHbmType; @NonNull private RectF mSensorRect; @Nullable private HbmCallback mHbmCallback; @@ -70,6 +80,13 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator { mIlluminationDotDrawable = canvas -> { canvas.drawOval(mSensorRect, mSensorPaint); }; + + if (Build.IS_ENG || Build.IS_USERDEBUG) { + mHbmType = Settings.Secure.getIntForUser(mContext.getContentResolver(), + SETTING_HBM_TYPE, DEFAULT_HBM_TYPE, UserHandle.USER_CURRENT); + } else { + mHbmType = DEFAULT_HBM_TYPE; + } } @Override @@ -79,10 +96,15 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator { @Override public void startIllumination(@Nullable Runnable onIlluminatedRunnable) { - if (mHbmCallback != null && mHolder.getSurface().isValid()) { - mHbmCallback.enableHbm(mHolder.getSurface()); + if (mHbmCallback != null) { + mHbmCallback.enableHbm(mHbmType, mHolder.getSurface()); + } else { + Log.e(TAG, "startIllumination | mHbmCallback is null"); + } + + if (mHbmType == HbmTypes.GLOBAL_HBM) { + drawImmediately(mIlluminationDotDrawable); } - drawImmediately(mIlluminationDotDrawable); if (onIlluminatedRunnable != null) { // No framework API can reliably tell when a frame reaches the panel. A timeout is the @@ -94,8 +116,10 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator { @Override public void stopIllumination() { - if (mHbmCallback != null && mHolder.getSurface().isValid()) { - mHbmCallback.disableHbm(mHolder.getSurface()); + if (mHbmCallback != null) { + mHbmCallback.disableHbm(mHbmType, mHolder.getSurface()); + } else { + Log.e(TAG, "stopIllumination | mHbmCallback is null"); } invalidate(); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java index 292af3f069f5..7e7cdce058bd 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java @@ -42,7 +42,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Locale; import java.util.Queue; import java.util.Set; import java.util.StringJoiner; @@ -94,9 +93,17 @@ public class BrightLineFalsingManager implements FalsingManager { } }; - private final BeliefListener mBeliefListener = belief -> { - if (belief > FALSE_BELIEF_THRESHOLD) { - mFalsingBeliefListeners.forEach(FalsingBeliefListener::onFalse); + private final BeliefListener mBeliefListener = new BeliefListener() { + @Override + public void onBeliefChanged(double belief) { + logInfo(String.format( + "{belief=%s confidence=%s}", + mHistoryTracker.falseBelief(), + mHistoryTracker.falseConfidence())); + if (belief > FALSE_BELIEF_THRESHOLD) { + mFalsingBeliefListeners.forEach(FalsingBeliefListener::onFalse); + logInfo("Triggering False Event (Threshold: " + FALSE_BELIEF_THRESHOLD + ")"); + } } }; @@ -105,13 +112,23 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public void onGestureComplete(long completionTimeMs) { if (mPriorResults != null) { + mPriorResults.forEach(result -> { + if (result.isFalse()) { + String reason = result.getReason(); + if (reason != null) { + logInfo(reason); + } + } + }); + mHistoryTracker.addResults(mPriorResults, completionTimeMs); mPriorResults = null; } else { // Gestures that were not classified get treated as a false. mHistoryTracker.addResults( Collections.singleton( - FalsingClassifier.Result.falsed(.8, "unclassified")), + FalsingClassifier.Result.falsed( + .8, getClass().getSimpleName(), "unclassified")), completionTimeMs); } } @@ -154,30 +171,13 @@ public class BrightLineFalsingManager implements FalsingManager { boolean result; - mDataProvider.setInteractionType(interactionType); - if (!mTestHarness && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked()) { Stream<FalsingClassifier.Result> results = - mClassifiers.stream().map(falsingClassifier -> { - FalsingClassifier.Result classifierResult = - falsingClassifier.classifyGesture( - mHistoryTracker.falseBelief(), - mHistoryTracker.falseConfidence()); - if (classifierResult.isFalse()) { - logInfo(String.format( - (Locale) null, - "{classifier=%s, interactionType=%d}", - falsingClassifier.getClass().getName(), - mDataProvider.getInteractionType())); - String reason = classifierResult.getReason(); - if (reason != null) { - logInfo(reason); - } - } else { - logDebug(falsingClassifier.getClass().getName() + ": false"); - } - return classifierResult; - }); + mClassifiers.stream().map(falsingClassifier -> + falsingClassifier.classifyGesture( + interactionType, + mHistoryTracker.falseBelief(), + mHistoryTracker.falseConfidence())); mPriorResults = new ArrayList<>(); final boolean[] localResult = {false}; results.forEach(classifierResult -> { @@ -190,13 +190,13 @@ public class BrightLineFalsingManager implements FalsingManager { mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1)); } - logDebug("Is false touch? " + result); + logDebug("False Gesture: " + result); if (Build.IS_ENG || Build.IS_USERDEBUG) { // Copy motion events, as the passed in list gets emptied out elsewhere in the code. RECENT_SWIPES.add(new DebugSwipeRecord( result, - mDataProvider.getInteractionType(), + interactionType, mDataProvider.getRecentMotionEvents().stream().map( motionEvent -> new XYDt( (int) motionEvent.getX(), @@ -220,37 +220,36 @@ public class BrightLineFalsingManager implements FalsingManager { FalsingClassifier.Result singleTapResult = mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents()); mPriorResults = Collections.singleton(singleTapResult); - if (singleTapResult.isFalse()) { - logInfo(String.format( - (Locale) null, "{classifier=%s}", mSingleTapClassifier.getClass().getName())); - String reason = singleTapResult.getReason(); - if (reason != null) { - logInfo(reason); - } - return true; - } - if (robustCheck) { + if (!singleTapResult.isFalse() && robustCheck) { if (mDataProvider.isJustUnlockedWithFace()) { // Immediately pass if a face is detected. mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1)); + logDebug("False Single Tap: false (face detected)"); return false; } else if (!isFalseDoubleTap()) { // We must check double tapping before other heuristics. This is because // the double tap will fail if there's only been one tap. We don't want that // failure to be recorded in mPriorResults. + logDebug("False Single Tap: false (double tapped)"); return false; } else if (mHistoryTracker.falseBelief() > TAP_CONFIDENCE_THRESHOLD) { mPriorResults = Collections.singleton( - FalsingClassifier.Result.falsed(0, "bad history")); + FalsingClassifier.Result.falsed( + 0, getClass().getSimpleName(), "bad history")); + logDebug("False Single Tap: true (bad history)"); return true; } else { mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(0.1)); + logDebug("False Single Tap: false (default)"); return false; } + + } else { + logDebug("False Single Tap: " + singleTapResult.isFalse() + " (simple)"); + return singleTapResult.isFalse(); } - return false; } @Override @@ -259,16 +258,12 @@ public class BrightLineFalsingManager implements FalsingManager { return false; } - FalsingClassifier.Result result = mDoubleTapClassifier.classifyGesture(); + FalsingClassifier.Result result = mDoubleTapClassifier.classifyGesture( + Classifier.GENERIC, + mHistoryTracker.falseBelief(), + mHistoryTracker.falseConfidence()); mPriorResults = Collections.singleton(result); - if (result.isFalse()) { - logInfo(String.format( - (Locale) null, "{classifier=%s}", mDoubleTapClassifier.getClass().getName())); - String reason = result.getReason(); - if (reason != null) { - logInfo(reason); - } - } + logDebug("False Double Tap: " + result.isFalse()); return result.isFalse(); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java index ffcdb93b11b1..71edbc06840e 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java @@ -62,15 +62,16 @@ class DiagonalClassifier extends FalsingClassifier { VERTICAL_ANGLE_RANGE); } - Result calculateFalsingResult(double historyBelief, double historyConfidence) { + Result calculateFalsingResult( + @Classifier.InteractionType int interactionType, + double historyBelief, double historyConfidence) { float angle = getAngle(); if (angle == Float.MAX_VALUE) { // Unknown angle return Result.passed(0); } - if (getInteractionType() == LEFT_AFFORDANCE - || getInteractionType() == RIGHT_AFFORDANCE) { + if (interactionType == LEFT_AFFORDANCE || interactionType == RIGHT_AFFORDANCE) { return Result.passed(0); } @@ -86,7 +87,7 @@ class DiagonalClassifier extends FalsingClassifier { || angleBetween(angle, minAngle - NINETY_DEG, maxAngle - NINETY_DEG) || angleBetween(angle, minAngle + ONE_HUNDRED_EIGHTY_DEG, maxAngle + ONE_HUNDRED_EIGHTY_DEG); - return falsed ? Result.falsed(0.5f, getReason()) : Result.passed(0.5); + return falsed ? falsed(0.5f, getReason()) : Result.passed(0.5); } private String getReason() { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java index 0f121c1c9ae7..5a9c3867c578 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java @@ -136,8 +136,6 @@ class DistanceClassifier extends FalsingClassifier { float dX = getLastMotionEvent().getX() - getFirstMotionEvent().getX(); float dY = getLastMotionEvent().getY() - getFirstMotionEvent().getY(); - logInfo("dX: " + dX + " dY: " + dY + " xV: " + vX + " yV: " + vY); - return new DistanceVectors(dX, dY, vX, vY); } @@ -147,9 +145,10 @@ class DistanceClassifier extends FalsingClassifier { } @Override - Result calculateFalsingResult(double historyBelief, double historyConfidence) { - return !getPassedFlingThreshold() - ? Result.falsed(0.5, getReason()) : Result.passed(0.5); + Result calculateFalsingResult( + @Classifier.InteractionType int interactionType, + double historyBelief, double historyConfidence) { + return !getPassedFlingThreshold() ? falsed(0.5, getReason()) : Result.passed(0.5); } String getReason() { @@ -172,7 +171,7 @@ class DistanceClassifier extends FalsingClassifier { Result isLongSwipe() { boolean longSwipe = getPassedDistanceThreshold(); logDebug("Is longSwipe? " + longSwipe); - return longSwipe ? Result.passed(0.5) : Result.falsed(0.5, getReason()); + return longSwipe ? Result.passed(0.5) : falsed(0.5, getReason()); } private boolean getPassedDistanceThreshold() { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java index baa54a65e4fc..e7c9d1863ce2 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java @@ -46,18 +46,20 @@ public class DoubleTapClassifier extends FalsingClassifier { } @Override - Result calculateFalsingResult(double historyBelief, double historyConfidence) { + Result calculateFalsingResult( + @Classifier.InteractionType int interactionType, + double historyBelief, double historyConfidence) { List<MotionEvent> secondTapEvents = getRecentMotionEvents(); List<MotionEvent> firstTapEvents = getPriorMotionEvents(); StringBuilder reason = new StringBuilder(); if (firstTapEvents == null) { - return Result.falsed(0, "Only one gesture recorded"); + return falsed(0, "Only one gesture recorded"); } return !isDoubleTap(firstTapEvents, secondTapEvents, reason) - ? Result.falsed(0.5, reason.toString()) : Result.passed(0.5); + ? falsed(0.5, reason.toString()) : Result.passed(0.5); } /** Returns true if the two supplied lists of {@link MotionEvent}s look like a double-tap. */ diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java index 1af5f7c488a5..81b9f66e9bc3 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java @@ -35,6 +35,14 @@ public abstract class FalsingClassifier { mDataProvider.addMotionEventListener(mMotionEventListener); } + protected String getFalsingContext() { + return getClass().getSimpleName(); + } + + protected Result falsed(double confidence, String reason) { + return Result.falsed(confidence, getFalsingContext(), reason); + } + List<MotionEvent> getRecentMotionEvents() { return mDataProvider.getRecentMotionEvents(); } @@ -87,10 +95,6 @@ public abstract class FalsingClassifier { return mDataProvider.getYdpi(); } - final @Classifier.InteractionType int getInteractionType() { - return mDataProvider.getInteractionType(); - } - void cleanup() { mDataProvider.removeMotionEventListener(mMotionEventListener); } @@ -101,42 +105,32 @@ public abstract class FalsingClassifier { * Useful for classifiers that need to see every MotionEvent, but most can probably * use {@link #getRecentMotionEvents()} instead, which will return a list of MotionEvents. */ - void onTouchEvent(MotionEvent motionEvent) {}; + void onTouchEvent(MotionEvent motionEvent) {} /** * Called when a ProximityEvent occurs (change in near/far). */ - void onProximityEvent(ProximitySensor.ThresholdSensorEvent proximityEvent) {}; + void onProximityEvent(ProximitySensor.ThresholdSensorEvent proximityEvent) {} /** * The phone screen has turned on and we need to begin falsing detection. */ - void onSessionStarted() {}; + void onSessionStarted() {} /** * The phone screen has turned off and falsing data can be discarded. */ - void onSessionEnded() {}; + void onSessionEnded() {} /** - * Returns whether a gesture looks like a false touch. - * - * See also {@link #classifyGesture(double, double)}. - */ - Result classifyGesture() { - return calculateFalsingResult(0.5, 0); - } - - /** - * Returns whether a gesture looks like a false touch, with the option to consider history. - * - * Unlike the parameter-less version of this method, this method allows the classifier to take - * history into account, penalizing or boosting confidence in a gesture based on recent results. + * Returns whether a gesture looks like a false touch, taking history into consideration. * - * See also {@link #classifyGesture()}. + * See {@link HistoryTracker#falseBelief()} and {@link HistoryTracker#falseConfidence()}. */ - Result classifyGesture(double historyBelief, double historyConfidence) { - return calculateFalsingResult(historyBelief, historyConfidence); + Result classifyGesture( + @Classifier.InteractionType int interactionType, + double historyBelief, double historyConfidence) { + return calculateFalsingResult(interactionType, historyBelief, historyConfidence); } /** @@ -144,7 +138,9 @@ public abstract class FalsingClassifier { * * When passed a historyConfidence of 0, the history belief should be wholly ignored. */ - abstract Result calculateFalsingResult(double historyBelief, double historyConfidence); + abstract Result calculateFalsingResult( + @Classifier.InteractionType int interactionType, + double historyBelief, double historyConfidence); /** */ public static void logDebug(String msg) { @@ -167,14 +163,16 @@ public abstract class FalsingClassifier { public static class Result { private final boolean mFalsed; private final double mConfidence; + private final String mContext; private final String mReason; /** - * See {@link #falsed(double, String)} abd {@link #passed(double)}. + * See {@link #falsed(double, String, String)} abd {@link #passed(double)}. */ - private Result(boolean falsed, double confidence, String reason) { + private Result(boolean falsed, double confidence, String context, String reason) { mFalsed = falsed; mConfidence = confidence; + mContext = context; mReason = reason; } @@ -187,21 +185,21 @@ public abstract class FalsingClassifier { } public String getReason() { - return mReason; + return String.format("{context=%s reason=%s}", mContext, mReason); } /** * Construct a "falsed" result indicating that a gesture should be treated as accidental. */ - public static Result falsed(double confidence, String reason) { - return new Result(true, confidence, reason); + public static Result falsed(double confidence, String context, String reason) { + return new Result(true, confidence, context, reason); } /** * Construct a "passed" result indicating that a gesture should be allowed. */ public static Result passed(double confidence) { - return new Result(false, confidence, null); + return new Result(false, confidence, null, null); } } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java index b359860a0fd7..cf6169703dca 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java @@ -127,7 +127,6 @@ class FalsingCollectorImpl implements FalsingCollector { @Override public void onNotificationStartDraggingDown() { - updateInteractionType(Classifier.NOTIFICATION_DRAG_DOWN); } @Override @@ -140,7 +139,6 @@ class FalsingCollectorImpl implements FalsingCollector { @Override public void onQsDown() { - updateInteractionType(Classifier.QUICK_SETTINGS); } @Override @@ -159,7 +157,6 @@ class FalsingCollectorImpl implements FalsingCollector { @Override public void onTrackingStarted(boolean secure) { - updateInteractionType(secure ? Classifier.BOUNCER_UNLOCK : Classifier.UNLOCK); } @Override @@ -176,8 +173,6 @@ class FalsingCollectorImpl implements FalsingCollector { @Override public void onAffordanceSwipingStarted(boolean rightCorner) { - updateInteractionType( - rightCorner ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE); } @Override @@ -186,7 +181,6 @@ class FalsingCollectorImpl implements FalsingCollector { @Override public void onStartExpandingFromPulse() { - updateInteractionType(Classifier.PULSE_EXPAND); } @Override @@ -237,7 +231,6 @@ class FalsingCollectorImpl implements FalsingCollector { @Override public void onNotificationStartDismissing() { - updateInteractionType(Classifier.NOTIFICATION_DISMISS); } @Override @@ -302,11 +295,6 @@ class FalsingCollectorImpl implements FalsingCollector { mHistoryTracker.addResults(Collections.singleton(result), mSystemClock.uptimeMillis()); } - private void updateInteractionType(@Classifier.InteractionType int type) { - logDebug("InteractionType: " + type); - mFalsingDataProvider.setInteractionType(type); - } - private boolean shouldSessionBeActive() { return mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java index 336f13f117d3..f665a7439ebe 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java @@ -47,8 +47,6 @@ public class FalsingDataProvider { private final List<MotionEventListener> mMotionEventListeners = new ArrayList<>(); private final List<GestureCompleteListener> mGestureCompleteListeners = new ArrayList<>(); - private @Classifier.InteractionType int mInteractionType; - private TimeLimitedMotionEventBuffer mRecentMotionEvents = new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS); private List<MotionEvent> mPriorMotionEvents; @@ -138,21 +136,6 @@ public class FalsingDataProvider { } /** - * interactionType is defined by {@link com.android.systemui.classifier.Classifier}. - */ - public final void setInteractionType(@Classifier.InteractionType int interactionType) { - if (mInteractionType != interactionType) { - mInteractionType = interactionType; - mDirty = true; - } - } - - /** Return the interaction type that is being compared against for falsing. */ - public final int getInteractionType() { - return mInteractionType; - } - - /** * Get the first recorded {@link MotionEvent} of the most recent gesture. * * Note that MotionEvents are not kept forever. As a gesture gets longer in duration, older diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java index 77d2d4267679..17942a6e00bf 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java @@ -56,14 +56,15 @@ class PointerCountClassifier extends FalsingClassifier { } @Override - Result calculateFalsingResult(double historyBelief, double historyConfidence) { - int interactionType = getInteractionType(); + Result calculateFalsingResult( + @Classifier.InteractionType int interactionType, + double historyBelief, double historyConfidence) { int allowedPointerCount = (interactionType == QUICK_SETTINGS || interactionType == NOTIFICATION_DRAG_DOWN) ? MAX_ALLOWED_POINTERS_SWIPE_DOWN : MAX_ALLOWED_POINTERS; return mMaxPointerCount > allowedPointerCount - ? Result.falsed(1, getReason(allowedPointerCount)) : Result.passed(0); + ? falsed(1, getReason(allowedPointerCount)) : Result.passed(0); } private String getReason(int allowedPointerCount) { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java index 6e97857f83da..ac330f069ae6 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java @@ -112,28 +112,31 @@ class ProximityClassifier extends FalsingClassifier { } @Override - Result calculateFalsingResult(double historyBelief, double historyConfidence) { - if (getInteractionType() == QUICK_SETTINGS) { + Result calculateFalsingResult( + @Classifier.InteractionType int interactionType, + double historyBelief, double historyConfidence) { + if (interactionType == QUICK_SETTINGS) { return Result.passed(0); } - logInfo("Percent of gesture in proximity: " + mPercentNear); - if (mPercentNear > mPercentCoveredThreshold) { Result longSwipeResult = mDistanceClassifier.isLongSwipe(); return longSwipeResult.isFalse() - ? Result.falsed(0.5, getReason(longSwipeResult)) : Result.passed(0.5); + ? falsed( + 0.5, getReason(longSwipeResult, mPercentNear, mPercentCoveredThreshold)) + : Result.passed(0.5); } return Result.passed(0.5); } - private String getReason(Result longSwipeResult) { + private static String getReason(Result longSwipeResult, float percentNear, + float percentCoveredThreshold) { return String.format( (Locale) null, "{percentInProximity=%f, threshold=%f, distanceClassifier=%s}", - mPercentNear, - mPercentCoveredThreshold, + percentNear, + percentCoveredThreshold, longSwipeResult.getReason()); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java index 4dd20ccff98e..68a9e5f0d489 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java @@ -39,14 +39,16 @@ public class SingleTapClassifier extends FalsingClassifier { } @Override - Result calculateFalsingResult(double historyBelief, double historyConfidence) { + Result calculateFalsingResult( + @Classifier.InteractionType int interactionType, + double historyBelief, double historyConfidence) { return isTap(getRecentMotionEvents()); } /** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */ public Result isTap(List<MotionEvent> motionEvents) { if (motionEvents.isEmpty()) { - return Result.falsed(0, "no motion events"); + return falsed(0, "no motion events"); } float downX = motionEvents.get(0).getX(); float downY = motionEvents.get(0).getY(); @@ -58,13 +60,13 @@ public class SingleTapClassifier extends FalsingClassifier { + Math.abs(event.getX() - downX) + "vs " + mTouchSlop; - return Result.falsed(0.5, reason); + return falsed(0.5, reason); } else if (Math.abs(event.getY() - downY) >= mTouchSlop) { reason = "dY too big for a tap: " + Math.abs(event.getY() - downY) + " vs " + mTouchSlop; - return Result.falsed(0.5, reason); + return falsed(0.5, reason); } } return Result.passed(0); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java index 4e032ea487c9..427c2eff8793 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java @@ -38,13 +38,15 @@ public class TypeClassifier extends FalsingClassifier { } @Override - Result calculateFalsingResult(double historyBelief, double historyConfidence) { + Result calculateFalsingResult( + @Classifier.InteractionType int interactionType, + double historyBelief, double historyConfidence) { boolean vertical = isVertical(); boolean up = isUp(); boolean right = isRight(); boolean wrongDirection = true; - switch (getInteractionType()) { + switch (interactionType) { case QUICK_SETTINGS: case PULSE_EXPAND: case NOTIFICATION_DRAG_DOWN: @@ -68,10 +70,11 @@ public class TypeClassifier extends FalsingClassifier { break; } - return wrongDirection ? Result.falsed(1, getReason()) : Result.passed(0.5); + return wrongDirection ? falsed(1, getReason(interactionType)) : Result.passed(0.5); } - private String getReason() { - return String.format("{vertical=%s, up=%s, right=%s}", isVertical(), isUp(), isRight()); + private String getReason(int interactionType) { + return String.format("{interaction=%s, vertical=%s, up=%s, right=%s}", + interactionType, isVertical(), isUp(), isRight()); } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java index 205825790461..1d413af43ab0 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java @@ -84,7 +84,9 @@ class ZigZagClassifier extends FalsingClassifier { } @Override - Result calculateFalsingResult(double historyBelief, double historyConfidence) { + Result calculateFalsingResult( + @Classifier.InteractionType int interactionType, + double historyBelief, double historyConfidence) { List<MotionEvent> motionEvents = getRecentMotionEvents(); // Rotate horizontal gestures to be horizontal between their first and last point. // Rotate vertical gestures to be vertical between their first and last point. @@ -156,7 +158,7 @@ class ZigZagClassifier extends FalsingClassifier { logDebug("Straightness Deviance: (" + devianceX + "," + devianceY + ") vs " + "(" + maxXDeviance + "," + maxYDeviance + ")"); return devianceX > maxXDeviance || devianceY > maxYDeviance - ? Result.falsed(0.5, getReason()) : Result.passed(0.5); + ? falsed(0.5, getReason()) : Result.passed(0.5); } private String getReason() { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java index 25d02a66d56d..2a7023a58637 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java @@ -19,6 +19,7 @@ package com.android.systemui.dagger; import android.content.BroadcastReceiver; import com.android.systemui.media.dialog.MediaOutputDialogReceiver; +import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver; import com.android.systemui.screenshot.ActionProxyReceiver; import com.android.systemui.screenshot.DeleteScreenshotReceiver; import com.android.systemui.screenshot.SmartActionsReceiver; @@ -69,4 +70,13 @@ public abstract class DefaultBroadcastReceiverBinder { public abstract BroadcastReceiver bindMediaOutputDialogReceiver( MediaOutputDialogReceiver broadcastReceiver); + /** + * + */ + @Binds + @IntoMap + @ClassKey(PeopleSpaceWidgetPinnedReceiver.class) + public abstract BroadcastReceiver bindPeopleSpaceWidgetPinnedReceiver( + PeopleSpaceWidgetPinnedReceiver broadcastReceiver); + } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 123ccee80179..8e344d2c2df7 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -31,6 +31,7 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.ServiceManager; import android.os.UserHandle; +import android.service.quickaccesswallet.QuickAccessWalletClient; import android.view.Choreographer; import android.view.IWindowManager; import android.view.LayoutInflater; @@ -360,4 +361,11 @@ public class DependencyProvider { public ModeSwitchesController providesModeSwitchesController(Context context) { return new ModeSwitchesController(context); } + + /** */ + @Provides + @SysUISingleton + public QuickAccessWalletClient provideQuickAccessWalletClient(Context context) { + return QuickAccessWalletClient.create(context); + } } diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java index 1a53c28c0fc5..58c41d582413 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java @@ -29,6 +29,7 @@ import static android.opengl.GLES20.glDrawArrays; import static android.opengl.GLES20.glEnableVertexAttribArray; import static android.opengl.GLES20.glGenTextures; import static android.opengl.GLES20.glTexParameteri; +import static android.opengl.GLES20.glUniform1f; import static android.opengl.GLES20.glUniform1i; import static android.opengl.GLES20.glVertexAttribPointer; @@ -52,6 +53,7 @@ class ImageGLWallpaper { private static final String A_POSITION = "aPosition"; private static final String A_TEXTURE_COORDINATES = "aTextureCoordinates"; private static final String U_TEXTURE = "uTexture"; + private static final String U_EXPOSURE = "uExposure"; private static final int POSITION_COMPONENT_COUNT = 2; private static final int TEXTURE_COMPONENT_COUNT = 2; private static final int BYTES_PER_FLOAT = 4; @@ -83,6 +85,7 @@ class ImageGLWallpaper { private int mAttrPosition; private int mAttrTextureCoordinates; private int mUniTexture; + private int mUniExposure; private int mTextureId; ImageGLWallpaper(ImageGLProgram program) { @@ -125,6 +128,7 @@ class ImageGLWallpaper { private void setupUniforms() { mUniTexture = mProgram.getUniformHandle(U_TEXTURE); + mUniExposure = mProgram.getUniformHandle(U_EXPOSURE); } void draw() { @@ -171,6 +175,10 @@ class ImageGLWallpaper { glUniform1i(mUniTexture, 0); } + void setExposureValue(float exposureValue) { + glUniform1f(mUniExposure, exposureValue); + } + /** * Called to dump current state. * @param prefix prefix. diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java index 01a353ce8f1f..cdf88f3898bb 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -46,6 +46,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer { private final ImageGLWallpaper mWallpaper; private final Rect mSurfaceSize = new Rect(); private final WallpaperTexture mTexture; + private float mExposureValue; public ImageWallpaperRenderer(Context context) { final WallpaperManager wpm = context.getSystemService(WallpaperManager.class); @@ -66,6 +67,13 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer { mTexture.use(c); } + /** + * @hide + */ + public void setExposureValue(float exposureValue) { + mExposureValue = exposureValue; + } + @Override public boolean isWcgContent() { return mTexture.isWcgContent(); @@ -94,6 +102,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer { public void onDrawFrame() { glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, mSurfaceSize.width(), mSurfaceSize.height()); + mWallpaper.setExposureValue(mExposureValue); mWallpaper.useTexture(); mWallpaper.draw(); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 9d298221fb79..a0906dfdfd5e 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -271,7 +271,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, /** Only for default display */ @Nullable - private AssistHandleViewController mAssistHandlerViewController; + AssistHandleViewController mAssistHandlerViewController; private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() { @@ -1381,7 +1381,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, return true; } - private void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) { + void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) { if (mNavigationBarView == null) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index 0bfd065c4b67..ca69e6de2d0a 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -324,7 +324,7 @@ public class NavigationBarController implements Callbacks, }); } - private void removeNavigationBar(int displayId) { + void removeNavigationBar(int displayId) { NavigationBar navBar = mNavigationBars.get(displayId); if (navBar != null) { navBar.setAutoHideController(/* autoHideController */ null); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java index 27074606d251..7342f91a8108 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java @@ -40,9 +40,9 @@ import com.android.systemui.R; import com.android.systemui.navigationbar.buttons.ButtonDispatcher; import com.android.systemui.navigationbar.buttons.KeyButtonView; import com.android.systemui.navigationbar.buttons.ReverseLinearLayout; +import com.android.systemui.navigationbar.buttons.ReverseLinearLayout.ReverseRelativeLayout; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.navigationbar.buttons.ReverseLinearLayout.ReverseRelativeLayout; import java.io.PrintWriter; import java.util.Objects; @@ -361,7 +361,7 @@ public class NavigationBarInflaterView extends FrameLayout return v; } - private View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) { + View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) { View v = null; String button = extractButton(buttonSpec); if (LEFT.equals(button)) { diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java index 6295692c788c..59329d1b091f 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java @@ -16,9 +16,7 @@ package com.android.systemui.people; -import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; -import android.app.people.PeopleSpaceTile; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; @@ -33,13 +31,16 @@ import android.os.UserHandle; import android.util.Log; import android.widget.RemoteViews; +import com.android.systemui.Dependency; import com.android.systemui.shared.system.PeopleProviderUtils; +import com.android.systemui.statusbar.notification.NotificationEntryManager; /** API that returns a People Tile preview. */ public class PeopleProvider extends ContentProvider { LauncherApps mLauncherApps; IPeopleManager mPeopleManager; + NotificationEntryManager mNotificationEntryManager; private static final String TAG = "PeopleProvider"; private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; @@ -57,18 +58,6 @@ public class PeopleProvider extends ContentProvider { throw new IllegalArgumentException("Invalid method"); } - // If services are not set as mocks in tests, fetch them now. - mPeopleManager = mPeopleManager != null ? mPeopleManager - : IPeopleManager.Stub.asInterface( - ServiceManager.getService(Context.PEOPLE_SERVICE)); - mLauncherApps = mLauncherApps != null ? mLauncherApps - : getContext().getSystemService(LauncherApps.class); - - if (mPeopleManager == null || mLauncherApps == null) { - Log.w(TAG, "Null system managers"); - return null; - } - if (extras == null) { Log.w(TAG, "Extras can't be null"); throw new IllegalArgumentException("Extras can't be null"); @@ -94,24 +83,22 @@ public class PeopleProvider extends ContentProvider { throw new IllegalArgumentException("Null user handle"); } - ConversationChannel channel; - try { - channel = mPeopleManager.getConversation( - packageName, userHandle.getIdentifier(), shortcutId); - } catch (Exception e) { - Log.w(TAG, "Exception getting tiles: " + e); - return null; - } - PeopleSpaceTile tile = PeopleSpaceUtils.getTile(channel, mLauncherApps); + // If services are not set as mocks in tests, fetch them now. + mPeopleManager = mPeopleManager != null ? mPeopleManager + : IPeopleManager.Stub.asInterface( + ServiceManager.getService(Context.PEOPLE_SERVICE)); + mLauncherApps = mLauncherApps != null ? mLauncherApps + : getContext().getSystemService(LauncherApps.class); + mNotificationEntryManager = mNotificationEntryManager != null ? mNotificationEntryManager + : Dependency.get(NotificationEntryManager.class); - if (tile == null) { - if (DEBUG) Log.i(TAG, "No tile was returned"); + RemoteViews view = PeopleSpaceUtils.getPreview(getContext(), mPeopleManager, mLauncherApps, + mNotificationEntryManager, shortcutId, userHandle, packageName); + if (view == null) { + if (DEBUG) Log.d(TAG, "No preview available for shortcutId: " + shortcutId); return null; } - - if (DEBUG) Log.i(TAG, "Returning tile preview for shortcutId: " + shortcutId); final Bundle bundle = new Bundle(); - RemoteViews view = PeopleSpaceUtils.createRemoteViews(getContext(), tile, 0, bundle); bundle.putParcelable(PeopleProviderUtils.RESPONSE_KEY_REMOTE_VIEWS, view); return bundle; } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index 378e49deb699..09461c36a27b 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -35,7 +35,6 @@ import android.view.ViewGroup; import com.android.systemui.R; import com.android.systemui.people.widget.PeopleSpaceWidgetManager; -import com.android.systemui.people.widget.PeopleTileKey; import com.android.systemui.statusbar.notification.NotificationEntryManager; import java.util.List; @@ -62,9 +61,12 @@ public class PeopleSpaceActivity extends Activity { private boolean mShowSingleConversation; @Inject - public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager) { + public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager, + PeopleSpaceWidgetManager peopleSpaceWidgetManager) { super(); mNotificationEntryManager = notificationEntryManager; + mPeopleSpaceWidgetManager = peopleSpaceWidgetManager; + } @Override @@ -78,7 +80,6 @@ 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); setTileViewsWithPriorityConversations(); mAppWidgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, @@ -139,9 +140,7 @@ public class PeopleSpaceActivity extends Activity { + mAppWidgetId); } } - PeopleTileKey key = new PeopleTileKey( - tile.getId(), tile.getUserHandle().getIdentifier(), tile.getPackageName()); - mPeopleSpaceWidgetManager.addNewWidget(mAppWidgetId, key); + mPeopleSpaceWidgetManager.addNewWidget(mAppWidgetId, tile); finishActivity(); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index e07e9cff4e7d..51638798277f 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -59,6 +59,8 @@ import android.icu.util.MeasureUnit; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; +import android.os.ServiceManager; +import android.os.UserHandle; import android.provider.ContactsContract; import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; @@ -92,7 +94,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -207,92 +208,6 @@ public class PeopleSpaceUtils { return tiles; } - /** - * Updates {@code appWidgetIds} with their associated conversation stored, handling a - * notification being posted or removed. - */ - public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds, - AppWidgetManager appWidgetManager, IPeopleManager peopleManager) { - Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>(); - for (int appWidgetId : appWidgetIds) { - PeopleSpaceTile tile = getPeopleSpaceTile( - context, appWidgetId, appWidgetManager, peopleManager); - if (tile == null) { - if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID"); - //TODO: Delete app widget id when crash is fixed (b/172932636) - continue; - } - Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId); - updateAppWidgetViews(appWidgetManager, context, appWidgetId, tile, options); - widgetIdToTile.put(appWidgetId, tile); - } - getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds); - } - - /** - * Returns a {@link PeopleSpaceTile} based on the {@code appWidgetId}. If the PeopleSpaceTile - * isn't cached, store it in AppWidgetOptions. - */ - @Nullable - public static PeopleSpaceTile getPeopleSpaceTile(Context context, int appWidgetId, - AppWidgetManager appWidgetManager, IPeopleManager peopleManager) { - // First, check if tile is cached in AppWidgetOptions. - PeopleSpaceTile tile = AppWidgetOptionsHelper.getPeopleTile(appWidgetManager, appWidgetId); - if (tile != null) { - if (DEBUG) Log.d(TAG, "People Tile is cached for widget: " + appWidgetId); - return tile; - } - - // If not, we get the PeopleTileKey from SharedPreferences, retrieve the Conversation from - // persisted storage, and cache it in AppWidgetOptions. - SharedPreferences widgetSp = context.getSharedPreferences( - String.valueOf(appWidgetId), - Context.MODE_PRIVATE); - PeopleTileKey sharedPreferencesKey = new PeopleTileKey( - widgetSp.getString(SHORTCUT_ID, EMPTY_STRING), - widgetSp.getInt(USER_ID, INVALID_USER_ID), - widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)); - - if (!sharedPreferencesKey.isValid()) { - Log.e(TAG, "Cannot find shortcut info for widgetId: " + appWidgetId); - return null; - } - - if (DEBUG) Log.d(TAG, "PeopleTile key is present in sharedPreferences: " + appWidgetId); - // If tile is null, we need to retrieve from persisted storage. - return getPeopleTileFromPersistentStorage(context, sharedPreferencesKey, peopleManager); - } - - /** - * Returns a {@link PeopleSpaceTile} based on {@link ConversationChannel} returned by - * {@link IPeopleManager}. - */ - public static PeopleSpaceTile getPeopleTileFromPersistentStorage(Context context, - PeopleTileKey peopleTileKey, IPeopleManager peopleManager) { - try { - if (DEBUG) Log.d(TAG, "Retrieving Tile from storage: " + peopleTileKey.toString()); - LauncherApps launcherApps = context.getSystemService(LauncherApps.class); - if (launcherApps == null) { - Log.d(TAG, "LauncherApps is null"); - return null; - } - - ConversationChannel channel = peopleManager.getConversation( - peopleTileKey.getPackageName(), - peopleTileKey.getUserId(), - peopleTileKey.getShortcutId()); - if (channel == null) { - Log.d(TAG, "Could not retrieve conversation from storage"); - return null; - } - - return new PeopleSpaceTile.Builder(channel, launcherApps).build(); - } catch (Exception e) { - Log.e(TAG, "Failed to retrieve conversation for tile: " + e); - return null; - } - } - /** Returns stored widgets for the conversation specified. */ public static Set<String> getStoredWidgetIds(SharedPreferences sp, PeopleTileKey key) { if (!key.isValid()) { @@ -347,6 +262,14 @@ public class PeopleSpaceUtils { widgetEditor.apply(); } + /** Augments a single {@link PeopleSpaceTile} with notification content, if one is present. */ + public static PeopleSpaceTile augmentSingleTileFromVisibleNotifications(Context context, + PeopleSpaceTile tile, NotificationEntryManager notificationEntryManager) { + List<PeopleSpaceTile> augmentedTile = augmentTilesFromVisibleNotifications( + context, Arrays.asList(tile), notificationEntryManager); + return augmentedTile.get(0); + } + static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context, List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) { if (notificationEntryManager == null) { @@ -914,7 +837,7 @@ public class PeopleSpaceUtils { } /** Calls to retrieve birthdays on a background thread. */ - private static void getBirthdaysOnBackgroundThread(Context context, + public static void getBirthdaysOnBackgroundThread(Context context, AppWidgetManager appWidgetManager, Map<Integer, PeopleSpaceTile> peopleSpaceTiles, int[] appWidgetIds) { ThreadUtils.postOnBackgroundThread( @@ -992,7 +915,8 @@ public class PeopleSpaceUtils { removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId); } - private static void updateAppWidgetViews(AppWidgetManager appWidgetManager, + /** Updates the current widget view with provided {@link PeopleSpaceTile}. */ + public static void updateAppWidgetViews(AppWidgetManager appWidgetManager, Context context, int appWidgetId, PeopleSpaceTile tile, Bundle options) { if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName()); RemoteViews views = createRemoteViews(context, tile, appWidgetId, options); @@ -1049,6 +973,43 @@ public class PeopleSpaceUtils { return lookupKeysWithBirthdaysToday; } + /** + * Returns a {@link RemoteViews} preview of a Conversation's People Tile. Returns null if one + * is not available. + */ + public static RemoteViews getPreview(Context context, IPeopleManager peopleManager, + LauncherApps launcherApps, NotificationEntryManager notificationEntryManager, + String shortcutId, UserHandle userHandle, String packageName) { + peopleManager = (peopleManager != null) ? peopleManager : IPeopleManager.Stub.asInterface( + ServiceManager.getService(Context.PEOPLE_SERVICE)); + launcherApps = (launcherApps != null) ? launcherApps + : context.getSystemService(LauncherApps.class); + if (peopleManager == null || launcherApps == null) { + return null; + } + + ConversationChannel channel; + try { + channel = peopleManager.getConversation( + packageName, userHandle.getIdentifier(), shortcutId); + } catch (Exception e) { + Log.w(TAG, "Exception getting tiles: " + e); + return null; + } + PeopleSpaceTile tile = PeopleSpaceUtils.getTile(channel, launcherApps); + + if (tile == null) { + if (DEBUG) Log.i(TAG, "No tile was returned"); + return null; + } + PeopleSpaceTile augmentedTile = augmentSingleTileFromVisibleNotifications( + context, tile, notificationEntryManager); + + if (DEBUG) Log.i(TAG, "Returning tile preview for shortcutId: " + shortcutId); + Bundle bundle = new Bundle(); + return PeopleSpaceUtils.createRemoteViews(context, augmentedTile, 0, bundle); + } + /** 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/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java index 48f6184a96d9..c01a52dd0a8e 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java @@ -27,6 +27,7 @@ import android.service.notification.NotificationStats; import android.text.TextUtils; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.statusbar.IStatusBarService; @@ -34,6 +35,9 @@ import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.wmshell.BubblesManager; + +import java.util.Optional; import javax.inject.Inject; @@ -43,16 +47,23 @@ public class LaunchConversationActivity extends Activity { private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); private NotificationEntryManager mNotificationEntryManager; + private final Optional<BubblesManager> mBubblesManagerOptional; + private boolean mIsForTesting; + private IStatusBarService mIStatusBarService; @Inject - public LaunchConversationActivity(NotificationEntryManager notificationEntryManager) { + public LaunchConversationActivity(NotificationEntryManager notificationEntryManager, + Optional<BubblesManager> bubblesManagerOptional) { super(); mNotificationEntryManager = notificationEntryManager; + mBubblesManagerOptional = bubblesManagerOptional; } @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + if (!mIsForTesting) { + super.onCreate(savedInstanceState); + } if (DEBUG) Log.d(TAG, "onCreate called"); Intent intent = getIntent(); @@ -63,21 +74,30 @@ public class LaunchConversationActivity extends Activity { String notificationKey = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY); - if (tileId != null && !tileId.isEmpty()) { + if (!TextUtils.isEmpty(tileId)) { if (DEBUG) { Log.d(TAG, "Launching conversation with shortcutInfo id " + tileId); } mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_CLICKED); try { + NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif( + notificationKey); + if (entry != null && entry.canBubble() && mBubblesManagerOptional.isPresent()) { + if (DEBUG) Log.d(TAG, "Open bubble for conversation"); + mBubblesManagerOptional.get().expandStackAndSelectBubble(entry); + // Just opt-out and don't cancel the notification for bubbles. + return; + } + + if (mIStatusBarService == null) { + mIStatusBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + } + clearNotificationIfPresent(notificationKey, packageName, userHandle); LauncherApps launcherApps = getApplicationContext().getSystemService(LauncherApps.class); launcherApps.startShortcut( packageName, tileId, null, null, userHandle); - - IStatusBarService statusBarService = IStatusBarService.Stub.asInterface( - ServiceManager.getService(Context.STATUS_BAR_SERVICE)); - clearNotificationIfPresent( - statusBarService, notificationKey, packageName, userHandle); } catch (Exception e) { Log.e(TAG, "Exception:" + e); } @@ -87,15 +107,14 @@ public class LaunchConversationActivity extends Activity { finish(); } - void clearNotificationIfPresent(IStatusBarService statusBarService, - String notifKey, String packageName, UserHandle userHandle) { + void clearNotificationIfPresent(String notifKey, String packageName, UserHandle userHandle) { if (TextUtils.isEmpty(notifKey)) { if (DEBUG) Log.d(TAG, "Skipping clear notification: notification key is empty"); return; } try { - if (statusBarService == null || mNotificationEntryManager == null) { + if (mIStatusBarService == null || mNotificationEntryManager == null) { if (DEBUG) { Log.d(TAG, "Skipping clear notification: null services, key: " + notifKey); } @@ -117,7 +136,7 @@ public class LaunchConversationActivity extends Activity { rank, count, true); if (DEBUG) Log.d(TAG, "Clearing notification, key: " + notifKey + ", rank: " + rank); - statusBarService.onNotificationClear( + mIStatusBarService.onNotificationClear( packageName, userHandle.getIdentifier(), notifKey, NotificationStats.DISMISSAL_OTHER, NotificationStats.DISMISS_SENTIMENT_POSITIVE, notifVisibility); @@ -125,4 +144,10 @@ public class LaunchConversationActivity extends Activity { Log.e(TAG, "Exception cancelling notification:" + e); } } + + @VisibleForTesting + void setIsForTesting(boolean isForTesting, IStatusBarService statusBarService) { + mIsForTesting = isForTesting; + mIStatusBarService = statusBarService; + } } 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 30eb2ac160c7..fa7b7b33dff6 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -22,11 +22,13 @@ 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 static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetViews; +import android.annotation.Nullable; import android.app.NotificationChannel; +import android.app.PendingIntent; import android.app.Person; import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; @@ -47,14 +49,17 @@ import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; +import android.widget.RemoteViews; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; +import com.android.systemui.Dependency; import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import java.util.Collections; import java.util.HashMap; @@ -78,6 +83,7 @@ public class PeopleSpaceWidgetManager { private IPeopleManager mIPeopleManager; private SharedPreferences mSharedPrefs; private PeopleManager mPeopleManager; + private NotificationEntryManager mNotificationEntryManager; public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); @GuardedBy("mLock") public static Map<PeopleTileKey, PeopleSpaceWidgetProvider.TileConversationListener> @@ -93,6 +99,7 @@ public class PeopleSpaceWidgetManager { mLauncherApps = context.getSystemService(LauncherApps.class); mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); mPeopleManager = mContext.getSystemService(PeopleManager.class); + mNotificationEntryManager = Dependency.get(NotificationEntryManager.class); } /** @@ -101,11 +108,13 @@ public class PeopleSpaceWidgetManager { @VisibleForTesting protected void setAppWidgetManager( AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager, - PeopleManager peopleManager, LauncherApps launcherApps) { + PeopleManager peopleManager, LauncherApps launcherApps, + NotificationEntryManager notificationEntryManager) { mAppWidgetManager = appWidgetManager; mIPeopleManager = iPeopleManager; mPeopleManager = peopleManager; mLauncherApps = launcherApps; + mNotificationEntryManager = notificationEntryManager; } /** @@ -124,8 +133,7 @@ public class PeopleSpaceWidgetManager { Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; if (showSingleConversation) { synchronized (mLock) { - PeopleSpaceUtils.updateSingleConversationWidgets( - mContext, widgetIds, mAppWidgetManager, mIPeopleManager); + updateSingleConversationWidgets(widgetIds); } } } catch (Exception e) { @@ -134,6 +142,85 @@ public class PeopleSpaceWidgetManager { } /** + * Updates {@code appWidgetIds} with their associated conversation stored, handling a + * notification being posted or removed. + */ + public void updateSingleConversationWidgets(int[] appWidgetIds) { + Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>(); + for (int appWidgetId : appWidgetIds) { + PeopleSpaceTile tile = getTileForExistingWidget(appWidgetId); + if (tile == null) { + if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID"); + //TODO: Delete app widget id when crash is fixed (b/172932636) + continue; + } + Bundle options = mAppWidgetManager.getAppWidgetOptions(appWidgetId); + updateAppWidgetViews(mAppWidgetManager, mContext, appWidgetId, tile, options); + widgetIdToTile.put(appWidgetId, tile); + } + PeopleSpaceUtils.getBirthdaysOnBackgroundThread( + mContext, mAppWidgetManager, widgetIdToTile, appWidgetIds); + } + + /** + * Returns a {@link PeopleSpaceTile} based on the {@code appWidgetId}. + * Widget already exists, so fetch {@link PeopleTileKey} from {@link SharedPreferences}. + */ + @Nullable + public PeopleSpaceTile getTileForExistingWidget(int appWidgetId) { + // First, check if tile is cached in AppWidgetOptions. + PeopleSpaceTile tile = AppWidgetOptionsHelper.getPeopleTile(mAppWidgetManager, appWidgetId); + if (tile != null) { + if (DEBUG) Log.d(TAG, "People Tile is cached for widget: " + appWidgetId); + return tile; + } + + // If tile is null, we need to retrieve from persistent storage. + if (DEBUG) Log.d(TAG, "Fetching key from sharedPreferences: " + appWidgetId); + SharedPreferences widgetSp = mContext.getSharedPreferences( + String.valueOf(appWidgetId), + Context.MODE_PRIVATE); + PeopleTileKey key = new PeopleTileKey( + widgetSp.getString(SHORTCUT_ID, EMPTY_STRING), + widgetSp.getInt(USER_ID, INVALID_USER_ID), + widgetSp.getString(PACKAGE_NAME, EMPTY_STRING)); + + return getTileFromPersistentStorage(key); + } + + /** + * Returns a {@link PeopleSpaceTile} based on the {@code appWidgetId}. + * If a {@link PeopleTileKey} is not provided, fetch one from {@link SharedPreferences}. + */ + @Nullable + public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key) { + if (!key.isValid()) { + Log.e(TAG, "PeopleTileKey invalid: " + key); + return null; + } + + if (mIPeopleManager == null || mLauncherApps == null) { + Log.d(TAG, "System services are null"); + return null; + } + + try { + if (DEBUG) Log.d(TAG, "Retrieving Tile from storage: " + key.toString()); + ConversationChannel channel = mIPeopleManager.getConversation( + key.getPackageName(), key.getUserId(), key.getShortcutId()); + if (channel == null) { + Log.d(TAG, "Could not retrieve conversation from storage"); + return null; + } + + return new PeopleSpaceTile.Builder(channel, mLauncherApps).build(); + } catch (Exception e) { + Log.e(TAG, "Failed to retrieve conversation for tile: " + e); + return null; + } + } + + /** * Check if any existing People tiles match the incoming notification change, and store the * change in the tile if so. */ @@ -201,8 +288,7 @@ public class PeopleSpaceWidgetManager { */ private void updateStorageAndViewWithConversationData(ConversationChannel conversation, int appWidgetId) { - PeopleSpaceTile storedTile = getPeopleSpaceTile( - mContext, appWidgetId, mAppWidgetManager, mIPeopleManager); + PeopleSpaceTile storedTile = getTileForExistingWidget(appWidgetId); if (storedTile == null) { if (DEBUG) Log.d(TAG, "Could not find stored tile to add conversation to"); return; @@ -234,8 +320,7 @@ public class PeopleSpaceWidgetManager { StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction, int appWidgetId) { - PeopleSpaceTile storedTile = getPeopleSpaceTile( - mContext, appWidgetId, mAppWidgetManager, mIPeopleManager); + PeopleSpaceTile storedTile = getTileForExistingWidget(appWidgetId); if (storedTile == null) { if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to"); return; @@ -332,28 +417,51 @@ public class PeopleSpaceWidgetManager { Log.d(TAG, "PeopleTileKey was present in Options, shortcutId: " + optionsKey.getShortcutId()); } - addNewWidget(appWidgetId, optionsKey); AppWidgetOptionsHelper.removePeopleTileKey(mAppWidgetManager, appWidgetId); + addNewWidget(appWidgetId, optionsKey); } // Update views for new widget dimensions. updateWidgets(new int[]{appWidgetId}); } - /** Adds{@code tile} mapped to {@code appWidgetId}. */ + /** Adds a widget based on {@code key} mapped to {@code appWidgetId}. */ public void addNewWidget(int appWidgetId, PeopleTileKey key) { + if (DEBUG) Log.d(TAG, "addNewWidget called with key for appWidgetId: " + appWidgetId); + PeopleSpaceTile tile = getTileFromPersistentStorage(key); + tile = PeopleSpaceUtils.augmentSingleTileFromVisibleNotifications( + mContext, tile, mNotificationEntryManager); + if (tile != null) { + addNewWidget(appWidgetId, tile); + } + } + + /** + * Adds a widget based on {@code tile} mapped to {@code appWidgetId}. + * The tile provided should already be augmented. + */ + public void addNewWidget(int appWidgetId, PeopleSpaceTile tile) { + if (DEBUG) Log.d(TAG, "addNewWidget called for appWidgetId: " + appWidgetId); + if (tile == null) { + return; + } + mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_ADDED); synchronized (mLock) { - if (DEBUG) Log.d(TAG, "Add storage for : " + key.getShortcutId()); + if (DEBUG) Log.d(TAG, "Add storage for : " + tile.getId()); + PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId); } try { - if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + key.getShortcutId()); - mLauncherApps.cacheShortcuts(key.getPackageName(), - Collections.singletonList(key.getShortcutId()), - UserHandle.of(key.getUserId()), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); + 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); } + + PeopleSpaceUtils.updateAppWidgetOptionsAndView( + mAppWidgetManager, mContext, appWidgetId, tile); PeopleSpaceWidgetProvider provider = new PeopleSpaceWidgetProvider(); provider.onUpdate(mContext, mAppWidgetManager, new int[]{appWidgetId}); } @@ -452,4 +560,28 @@ public class PeopleSpaceWidgetManager { Log.d(TAG, "Exception uncaching shortcut:" + e); } } + + /** + * Builds a request to pin a People Tile app widget, with a preview and storing necessary + * information as the callback. + */ + public boolean requestPinAppWidget(ShortcutInfo shortcutInfo) { + if (DEBUG) Log.d(TAG, "Requesting pin widget, shortcutId: " + shortcutInfo.getId()); + + RemoteViews widgetPreview = PeopleSpaceUtils.getPreview(mContext, mIPeopleManager, + mLauncherApps, mNotificationEntryManager, shortcutInfo.getId(), + shortcutInfo.getUserHandle(), shortcutInfo.getPackage()); + if (widgetPreview == null) { + Log.w(TAG, "Skipping pinning widget: no tile for shortcutId: " + shortcutInfo.getId()); + return false; + } + Bundle extras = new Bundle(); + extras.putParcelable(AppWidgetManager.EXTRA_APPWIDGET_PREVIEW, widgetPreview); + + PendingIntent successCallback = + PeopleSpaceWidgetPinnedReceiver.getPendingIntent(mContext, shortcutInfo); + + ComponentName componentName = new ComponentName(mContext, PeopleSpaceWidgetProvider.class); + return mAppWidgetManager.requestPinAppWidget(componentName, extras, successCallback); + } } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetPinnedReceiver.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetPinnedReceiver.java new file mode 100644 index 000000000000..a28da43a80b6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetPinnedReceiver.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.people.widget; + +import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; + +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ShortcutInfo; +import android.util.Log; + +import com.android.systemui.people.PeopleSpaceUtils; + +import javax.inject.Inject; + +/** Called when a People Tile widget is added via {@link AppWidgetManager.requestPinAppWidget()}. */ +public class PeopleSpaceWidgetPinnedReceiver extends BroadcastReceiver { + private static final String TAG = "PeopleSpaceWgtPinReceiver"; + private static final int BROADCAST_ID = 0; + private static final int INVALID_WIDGET_ID = -1; + private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; + + private final PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; + + @Inject + public PeopleSpaceWidgetPinnedReceiver(PeopleSpaceWidgetManager peopleSpaceWidgetManager) { + mPeopleSpaceWidgetManager = peopleSpaceWidgetManager; + } + + /** Creates a {@link PendingIntent} that is passed onto this receiver when a widget is added. */ + public static PendingIntent getPendingIntent(Context context, ShortcutInfo shortcutInfo) { + Intent intent = new Intent(context, PeopleSpaceWidgetPinnedReceiver.class) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + intent.putExtra(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId()); + intent.putExtra(Intent.EXTRA_USER_ID, shortcutInfo.getUserId()); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, shortcutInfo.getPackage()); + + // Intent needs to be mutable because App Widget framework populates it with app widget id. + return PendingIntent.getBroadcast(context, BROADCAST_ID, intent, + PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); + } + + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) Log.d(TAG, "Add widget broadcast received"); + if (context == null || intent == null) { + if (DEBUG) Log.w(TAG, "Skipping: context or intent are null"); + return; + } + + int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, INVALID_WIDGET_ID); + if (widgetId == INVALID_WIDGET_ID) { + if (DEBUG) Log.w(TAG, "Skipping: invalid widgetId"); + return; + } + + String shortcutId = intent.getStringExtra(Intent.EXTRA_SHORTCUT_ID); + String packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); + int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, INVALID_USER_ID); + PeopleTileKey key = new PeopleTileKey(shortcutId, userId, packageName); + if (!key.isValid()) { + if (DEBUG) Log.w(TAG, "Skipping: key is not valid: " + key.toString()); + return; + } + + if (DEBUG) Log.d(TAG, "Adding widget: " + widgetId + ", key:" + key.toString()); + mPeopleSpaceWidgetManager.addNewWidget(widgetId, key); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java index ac42cb08090a..319df85b4872 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java @@ -19,6 +19,7 @@ package com.android.systemui.people.widget; import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING; import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; +import android.app.people.PeopleSpaceTile; import android.text.TextUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -37,6 +38,12 @@ public class PeopleTileKey { mPackageName = packageName; } + public PeopleTileKey(PeopleSpaceTile tile) { + mShortcutId = tile.getId(); + mUserId = tile.getUserHandle().getIdentifier(); + mPackageName = tile.getPackageName(); + } + public PeopleTileKey(NotificationEntry entry) { mShortcutId = entry.getRanking() != null && entry.getRanking().getConversationShortcutInfo() != null @@ -81,7 +88,7 @@ public class PeopleTileKey { */ @Override public String toString() { - if (!isValid()) return null; + if (!isValid()) return EMPTY_STRING; return mShortcutId + "/" + mUserId + "/" + mPackageName; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 8ab17432524d..6386365c2966 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -67,6 +67,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr private final DumpManager mDumpManager; private final FeatureFlags mFeatureFlags; protected final ArrayList<TileRecord> mRecords = new ArrayList<>(); + private boolean mShouldUseSplitNotificationShade; private int mLastOrientation; private String mCachedSpecs = ""; @@ -81,6 +82,8 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr new QSPanel.OnConfigurationChangedListener() { @Override public void onConfigurationChange(Configuration newConfig) { + mShouldUseSplitNotificationShade = + Utils.shouldUseSplitNotificationShade(mFeatureFlags, getResources()); if (newConfig.orientation != mLastOrientation) { mLastOrientation = newConfig.orientation; switchTileLayout(false); @@ -119,6 +122,8 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr mDumpManager = dumpManager; mFeatureFlags = featureFlags; mQSLabelFlag = featureFlags.isQSLabelsEnabled(); + mShouldUseSplitNotificationShade = + Utils.shouldUseSplitNotificationShade(mFeatureFlags, getResources()); } @Override @@ -345,7 +350,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr } boolean shouldUseHorizontalLayout() { - if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, getResources())) { + if (mShouldUseSplitNotificationShade) { return false; } return mUsingMediaPlayer && mMediaHost.getVisible() diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 29b9e64d1659..ba349c6273d9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -47,6 +47,7 @@ import com.android.systemui.qs.tiles.LocationTile; import com.android.systemui.qs.tiles.MicrophoneToggleTile; import com.android.systemui.qs.tiles.NfcTile; import com.android.systemui.qs.tiles.NightDisplayTile; +import com.android.systemui.qs.tiles.QuickAccessWalletTile; import com.android.systemui.qs.tiles.ReduceBrightColorsTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.qs.tiles.ScreenRecordTile; @@ -93,6 +94,7 @@ public class QSFactoryImpl implements QSFactory { private final Provider<MicrophoneToggleTile> mMicrophoneToggleTileProvider; private final Provider<DeviceControlsTile> mDeviceControlsTileProvider; private final Provider<AlarmTile> mAlarmTileProvider; + private final Provider<QuickAccessWalletTile> mQuickAccessWalletTileProvider; private final Lazy<QSHost> mQsHostLazy; private final Provider<CustomTile.Builder> mCustomTileBuilderProvider; @@ -129,7 +131,8 @@ public class QSFactoryImpl implements QSFactory { Provider<CameraToggleTile> cameraToggleTileProvider, Provider<MicrophoneToggleTile> microphoneToggleTileProvider, Provider<DeviceControlsTile> deviceControlsTileProvider, - Provider<AlarmTile> alarmTileProvider) { + Provider<AlarmTile> alarmTileProvider, + Provider<QuickAccessWalletTile> quickAccessWalletTileProvider) { mQsHostLazy = qsHostLazy; mCustomTileBuilderProvider = customTileBuilderProvider; @@ -161,6 +164,7 @@ public class QSFactoryImpl implements QSFactory { mMicrophoneToggleTileProvider = microphoneToggleTileProvider; mDeviceControlsTileProvider = deviceControlsTileProvider; mAlarmTileProvider = alarmTileProvider; + mQuickAccessWalletTileProvider = quickAccessWalletTileProvider; } public QSTile createTile(String tileSpec) { @@ -224,6 +228,8 @@ public class QSFactoryImpl implements QSFactory { return mDeviceControlsTileProvider.get(); case "alarm": return mAlarmTileProvider.get(); + case "wallet": + return mQuickAccessWalletTileProvider.get(); } // Custom tiles diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java new file mode 100644 index 000000000000..60c5d1cafde9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -0,0 +1,136 @@ +/* + * 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.qs.tiles; + +import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.os.Looper; +import android.service.quickaccesswallet.QuickAccessWalletClient; +import android.service.quicksettings.Tile; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.SecureSettings; + +import javax.inject.Inject; + +/** Quick settings tile: Quick access wallet **/ +public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { + + private static final String FEATURE_CHROME_OS = "org.chromium.arc"; + private final CharSequence mLabel = mContext.getString(R.string.wallet_title); + // TODO(b/180959290): Re-create the QAW Client when the default NFC payment app changes. + private final QuickAccessWalletClient mQuickAccessWalletClient; + private final KeyguardStateController mKeyguardStateController; + private final PackageManager mPackageManager; + private final SecureSettings mSecureSettings; + private final FeatureFlags mFeatureFlags; + + @Inject + public QuickAccessWalletTile( + QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + QuickAccessWalletClient quickAccessWalletClient, + KeyguardStateController keyguardStateController, + PackageManager packageManager, + SecureSettings secureSettings, + FeatureFlags featureFlags) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + mQuickAccessWalletClient = quickAccessWalletClient; + mKeyguardStateController = keyguardStateController; + mPackageManager = packageManager; + mSecureSettings = secureSettings; + mFeatureFlags = featureFlags; + } + + + @Override + public State newTileState() { + State state = new State(); + state.handlesLongClick = false; + return state; + } + + @Override + protected void handleClick() { + mActivityStarter.postStartActivityDismissingKeyguard( + mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0); + } + + @Override + protected void handleUpdateState(State state, Object arg) { + CharSequence qawLabel = mQuickAccessWalletClient.getServiceLabel(); + state.label = qawLabel == null ? mLabel : qawLabel; + state.contentDescription = state.label; + state.icon = ResourceIcon.get(R.drawable.ic_qs_wallet); + boolean isDeviceLocked = !mKeyguardStateController.isUnlocked(); + if (mQuickAccessWalletClient.isWalletFeatureAvailable()) { + state.state = isDeviceLocked ? Tile.STATE_INACTIVE : Tile.STATE_ACTIVE; + state.secondaryLabel = isDeviceLocked + ? null + : mContext.getString(R.string.wallet_secondary_label); + state.stateDescription = state.secondaryLabel; + } else { + state.state = Tile.STATE_UNAVAILABLE; + } + } + + @Override + public int getMetricsCategory() { + return 0; + } + + @Override + public boolean isAvailable() { + return mFeatureFlags.isQuickAccessWalletEnabled() + && mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION) + && !mPackageManager.hasSystemFeature(FEATURE_CHROME_OS) + && mSecureSettings.getString(NFC_PAYMENT_DEFAULT_COMPONENT) != null; + } + + @Override + public Intent getLongClickIntent() { + return null; + } + + @Override + public CharSequence getTileLabel() { + CharSequence qawLabel = mQuickAccessWalletClient.getServiceLabel(); + return qawLabel == null ? mLabel : qawLabel; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java index 07adc7bd7053..730702ec8685 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java @@ -34,6 +34,8 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import javax.inject.Inject; + /** * Owns a series of partial screen captures (tiles). * <p> @@ -47,6 +49,7 @@ class ImageTileSet { private CallbackRegistry<OnBoundsChangedListener, ImageTileSet, Rect> mOnBoundsListeners; private CallbackRegistry<OnContentChangedListener, ImageTileSet, Rect> mContentListeners; + @Inject ImageTileSet(@UiThread Handler handler) { mHandler = handler; } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index 3ac884b98136..31cdadab070d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -29,11 +29,9 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; -import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; -import android.view.IScrollCaptureConnection; import android.view.IWindowManager; import android.view.ScrollCaptureResponse; import android.view.View; @@ -101,12 +99,12 @@ public class LongScreenshotActivity extends Activity { @Inject public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter, @Main Executor mainExecutor, @Background Executor bgExecutor, IWindowManager wms, - Context context) { + Context context, ScrollCaptureController scrollCaptureController) { mUiEventLogger = uiEventLogger; mUiExecutor = mainExecutor; mBackgroundExecutor = bgExecutor; mImageExporter = imageExporter; - mScrollCaptureController = new ScrollCaptureController(context, bgExecutor, wms); + mScrollCaptureController = scrollCaptureController; } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 798a063f15b9..c1ae29230e61 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -25,6 +25,7 @@ import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM; import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK; import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT; +import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL; import static com.android.systemui.screenshot.LogConfig.DEBUG_UI; import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW; import static com.android.systemui.screenshot.LogConfig.logTag; @@ -90,6 +91,7 @@ import com.android.systemui.util.DeviceConfigProxy; import com.google.common.util.concurrent.ListenableFuture; import java.util.List; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -570,6 +572,15 @@ public class ScreenshotController { mLastScrollCaptureResponse.close(); } mLastScrollCaptureResponse = responseFuture.get(); + if (!mLastScrollCaptureResponse.isConnected()) { + // No connection means that the target window wasn't found + // or that it cannot support scroll capture. + Log.d(TAG, "ScrollCapture: " + mLastScrollCaptureResponse.getDescription() + " [" + + mLastScrollCaptureResponse.getWindowTitle() + "]"); + return; + } + Log.d(TAG, "ScrollCapture: connected to window [" + + mLastScrollCaptureResponse.getWindowTitle() + "]"); final Intent intent = new Intent(mContext, LongScreenshotActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.putExtra(LongScreenshotActivity.EXTRA_CAPTURE_RESPONSE, @@ -580,6 +591,8 @@ public class ScreenshotController { mContext.startActivity(intent); dismissScreenshot(false); }); + } catch (CancellationException e) { + // Ignore } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "requestScrollCapture failed", e); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 4f699041fdb3..d3dd048a989e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -18,19 +18,16 @@ package com.android.systemui.screenshot; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.HardwareRenderer; -import android.graphics.RecordingCanvas; import android.graphics.Rect; -import android.graphics.RenderNode; import android.graphics.drawable.Drawable; import android.provider.Settings; import android.util.Log; -import android.view.IWindowManager; import android.view.ScrollCaptureResponse; import androidx.concurrent.futures.CallbackToFutureAdapter; import androidx.concurrent.futures.CallbackToFutureAdapter.Completer; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult; import com.android.systemui.screenshot.ScrollCaptureClient.Session; @@ -39,6 +36,8 @@ import com.google.common.util.concurrent.ListenableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; +import javax.inject.Inject; + /** * Interaction controller between the UI and ScrollCaptureClient. */ @@ -131,11 +130,13 @@ public class ScrollCaptureController { } } - ScrollCaptureController(Context context, Executor bgExecutor, IWindowManager wms) { + @Inject + ScrollCaptureController(Context context, @Background Executor bgExecutor, + ScrollCaptureClient client, ImageTileSet imageTileSet) { mContext = context; mBgExecutor = bgExecutor; - mImageTileSet = new ImageTileSet(context.getMainThreadHandler()); - mClient = new ScrollCaptureClient(mContext, wms); + mClient = client; + mImageTileSet = imageTileSet; } /** @@ -252,8 +253,10 @@ public class ScrollCaptureController { return; } - int nextTop = (mScrollingUp) - ? result.captured.top - mSession.getTileHeight() : result.captured.bottom; + // Partial or empty results caused the direction the flip, so we can reliably use the + // requested edges to determine the next top. + int nextTop = (mScrollingUp) ? result.requested.top - mSession.getTileHeight() + : result.requested.bottom; requestNextTile(nextTop); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 059903961eae..1ff30a32c4ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -37,6 +37,7 @@ import android.content.Context; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; +import android.hardware.fingerprint.IUdfpsHbmListener; import android.inputmethodservice.InputMethodService.BackDispositionMode; import android.os.Bundle; import android.os.Handler; @@ -142,6 +143,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< //TODO(b/169175022) Update name and when feature name is locked. private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE = 58 << MSG_SHIFT; private static final int MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED = 59 << MSG_SHIFT; + private static final int MSG_SET_UDFPS_HBM_LISTENER = 60 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -286,21 +288,38 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< IBiometricSysuiReceiver receiver, int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation, int userId, String opPackageName, - long operationId) { } - default void onBiometricAuthenticated() { } - default void onBiometricHelp(String message) { } - default void onBiometricError(int modality, int error, int vendorCode) { } - default void hideAuthenticationDialog() { } + long operationId) { + } + + default void onBiometricAuthenticated() { + } + + default void onBiometricHelp(String message) { + } + + default void onBiometricError(int modality, int error, int vendorCode) { + } + + default void hideAuthenticationDialog() { + } + + /** + * @see IStatusBar#setUdfpsHbmListener(IUdfpsHbmListener) + */ + default void setUdfpsHbmListener(IUdfpsHbmListener listener) { + } /** * @see IStatusBar#onDisplayReady(int) */ - default void onDisplayReady(int displayId) { } + default void onDisplayReady(int displayId) { + } /** * @see DisplayManager.DisplayListener#onDisplayRemoved(int) */ - default void onDisplayRemoved(int displayId) { } + default void onDisplayRemoved(int displayId) { + } /** * @see IStatusBar#onRecentsAnimationStateChanged(boolean) @@ -893,6 +912,13 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override + public void setUdfpsHbmListener(IUdfpsHbmListener listener) { + synchronized (mLock) { + mHandler.obtainMessage(MSG_SET_UDFPS_HBM_LISTENER, listener).sendToTarget(); + } + } + + @Override public void onDisplayReady(int displayId) { synchronized (mLock) { mHandler.obtainMessage(MSG_DISPLAY_READY, displayId, 0).sendToTarget(); @@ -1286,7 +1312,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< mCallbacks.get(i).onBiometricHelp((String) msg.obj); } break; - case MSG_BIOMETRIC_ERROR: + case MSG_BIOMETRIC_ERROR: { SomeArgs someArgs = (SomeArgs) msg.obj; for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).onBiometricError( @@ -1297,11 +1323,17 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } someArgs.recycle(); break; + } case MSG_BIOMETRIC_HIDE: for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).hideAuthenticationDialog(); } break; + case MSG_SET_UDFPS_HBM_LISTENER: + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).setUdfpsHbmListener((IUdfpsHbmListener) msg.obj); + } + break; case MSG_SHOW_CHARGING_ANIMATION: for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).showWirelessChargingAnimation(msg.arg1); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index c8e0d60ce304..c3de81c3c66a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -83,6 +83,10 @@ public class FeatureFlags { return mFlagReader.isEnabled(R.bool.flag_monet); } + public boolean isQuickAccessWalletEnabled() { + return mFlagReader.isEnabled(R.bool.flag_wallet); + } + public boolean isNavigationBarOverlayEnabled() { return mFlagReader.isEnabled(R.bool.flag_navigation_bar_overlay); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandRegistry.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandRegistry.kt index ce0a08cd4ccf..1da42a705311 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandRegistry.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandRegistry.kt @@ -192,9 +192,9 @@ private class PrefsCommand(val context: Context) : Command { val pref = args[0] when (pref) { - Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING -> { + Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S -> { val value = Integer.parseInt(args[1]) - Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, value != 0) + Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, value != 0) } else -> { pw.println("Cannot set pref ($pref)") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 0957f7818daa..617dadb42594 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -31,6 +31,7 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.FeatureFlags; @@ -71,6 +72,7 @@ import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController; import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.leak.LeakDetector; @@ -133,6 +135,8 @@ public interface NotificationsModule { AccessibilityManager accessibilityManager, HighPriorityProvider highPriorityProvider, INotificationManager notificationManager, + NotificationEntryManager notificationEntryManager, + PeopleSpaceWidgetManager peopleSpaceWidgetManager, LauncherApps launcherApps, ShortcutManager shortcutManager, ChannelEditorDialogController channelEditorDialogController, @@ -141,7 +145,8 @@ public interface NotificationsModule { AssistantFeedbackController assistantFeedbackController, Optional<BubblesManager> bubblesManagerOptional, UiEventLogger uiEventLogger, - OnUserInteractionCallback onUserInteractionCallback) { + OnUserInteractionCallback onUserInteractionCallback, + ShadeController shadeController) { return new NotificationGutsManager( context, statusBarLazy, @@ -150,6 +155,8 @@ public interface NotificationsModule { accessibilityManager, highPriorityProvider, notificationManager, + notificationEntryManager, + peopleSpaceWidgetManager, launcherApps, shortcutManager, channelEditorDialogController, @@ -158,7 +165,8 @@ public interface NotificationsModule { assistantFeedbackController, bubblesManagerOptional, uiEventLogger, - onUserInteractionCallback); + onUserInteractionCallback, + shadeController); } /** Provides an instance of {@link VisualStabilityManager} */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index adeba9078c52..b4ab8cf817dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -69,9 +69,11 @@ import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.statusbar.notification.NotificationChannelHelper; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.wmshell.BubblesManager; import java.lang.annotation.Retention; @@ -86,15 +88,16 @@ public class NotificationConversationInfo extends LinearLayout implements NotificationGuts.GutsContent { private static final String TAG = "ConversationGuts"; - private INotificationManager mINotificationManager; private ShortcutManager mShortcutManager; private PackageManager mPm; + private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; private ConversationIconFactory mIconFactory; private OnUserInteractionCallback mOnUserInteractionCallback; private Handler mMainHandler; private Handler mBgHandler; private Optional<BubblesManager> mBubblesManagerOptional; + private ShadeController mShadeController; private String mPackageName; private String mAppName; private int mAppUid; @@ -169,9 +172,16 @@ public class NotificationConversationInfo extends LinearLayout implements private OnClickListener mOnDone = v -> { mPressedApply = true; - // If the user selected Priority, maybe show the priority onboarding + + // If the user selected Priority, maybe show the priority onboarding. + // If the user selected Priority and the previous selection was not priority, show a + // People Tile add request. If showing the priority onboarding, however, delay the request + // to when the onboarding dialog closes. if (mSelectedAction == ACTION_FAVORITE && shouldShowPriorityOnboarding()) { showPriorityOnboarding(); + } else if (mSelectedAction == ACTION_FAVORITE && getPriority() != mSelectedAction) { + mShadeController.animateCollapsePanels(); + mPeopleSpaceWidgetManager.requestPinAppWidget(mShortcutInfo); } mGutsContainer.closeControls(v, true); }; @@ -209,6 +219,7 @@ public class NotificationConversationInfo extends LinearLayout implements @Action int selectedAction, ShortcutManager shortcutManager, PackageManager pm, + PeopleSpaceWidgetManager peopleSpaceWidgetManager, INotificationManager iNotificationManager, OnUserInteractionCallback onUserInteractionCallback, String pkg, @@ -224,10 +235,12 @@ public class NotificationConversationInfo extends LinearLayout implements @Main Handler mainHandler, @Background Handler bgHandler, OnConversationSettingsClickListener onConversationSettingsClickListener, - Optional<BubblesManager> bubblesManagerOptional) { + Optional<BubblesManager> bubblesManagerOptional, + ShadeController shadeController) { mPressedApply = false; mSelectedAction = selectedAction; mINotificationManager = iNotificationManager; + mPeopleSpaceWidgetManager = peopleSpaceWidgetManager; mOnUserInteractionCallback = onUserInteractionCallback; mPackageName = pkg; mEntry = entry; @@ -245,6 +258,7 @@ public class NotificationConversationInfo extends LinearLayout implements mUserContext = userContext; mBubbleMetadata = bubbleMetadata; mBubblesManagerOptional = bubblesManagerOptional; + mShadeController = shadeController; mBuilderProvider = builderProvider; mMainHandler = mainHandler; mBgHandler = bgHandler; @@ -527,7 +541,7 @@ public class NotificationConversationInfo extends LinearLayout implements } private boolean shouldShowPriorityOnboarding() { - return !Prefs.getBoolean(mUserContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, false); + return !Prefs.getBoolean(mUserContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, false); } private void showPriorityOnboarding() { @@ -566,9 +580,11 @@ public class NotificationConversationInfo extends LinearLayout implements .setBadge(mIconFactory.getAppBadge( mPackageName, UserHandle.getUserId(mSbn.getUid()))) .setOnSettingsClick(mOnConversationSettingsClickListener) + .setPeopleSpaceWidgetManager(mPeopleSpaceWidgetManager) + .setShadeController(mShadeController) .build(); - controller.init(); + controller.init(mShortcutInfo); controller.show(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 6a873b678a93..1a7f5b0bf5af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -21,6 +21,7 @@ import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import android.app.INotificationManager; import android.app.NotificationChannel; +import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; @@ -49,6 +50,7 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserContextProvider; @@ -59,11 +61,13 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.wmshell.BubblesManager; @@ -120,11 +124,15 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private final Optional<BubblesManager> mBubblesManagerOptional; private Runnable mOpenRunnable; private final INotificationManager mNotificationManager; + private final NotificationEntryManager mNotificationEntryManager; + private final PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; private final LauncherApps mLauncherApps; private final ShortcutManager mShortcutManager; private final UserContextProvider mContextTracker; private final Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider; private final UiEventLogger mUiEventLogger; + private final ShadeController mShadeController; + private final AppWidgetManager mAppWidgetManager; /** * Injected constructor. See {@link NotificationsModule}. @@ -136,6 +144,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx AccessibilityManager accessibilityManager, HighPriorityProvider highPriorityProvider, INotificationManager notificationManager, + NotificationEntryManager notificationEntryManager, + PeopleSpaceWidgetManager peopleSpaceWidgetManager, LauncherApps launcherApps, ShortcutManager shortcutManager, ChannelEditorDialogController channelEditorDialogController, @@ -144,7 +154,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx AssistantFeedbackController assistantFeedbackController, Optional<BubblesManager> bubblesManagerOptional, UiEventLogger uiEventLogger, - OnUserInteractionCallback onUserInteractionCallback) { + OnUserInteractionCallback onUserInteractionCallback, + ShadeController shadeController) { mContext = context; mStatusBarLazy = statusBarLazy; mMainHandler = mainHandler; @@ -152,6 +163,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mAccessibilityManager = accessibilityManager; mHighPriorityProvider = highPriorityProvider; mNotificationManager = notificationManager; + mNotificationEntryManager = notificationEntryManager; + mPeopleSpaceWidgetManager = peopleSpaceWidgetManager; mLauncherApps = launcherApps; mShortcutManager = shortcutManager; mContextTracker = contextTracker; @@ -161,6 +174,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mBubblesManagerOptional = bubblesManagerOptional; mUiEventLogger = uiEventLogger; mOnUserInteractionCallback = onUserInteractionCallback; + mShadeController = shadeController; + mAppWidgetManager = AppWidgetManager.getInstance(context); } public void setUpWithPresenter(NotificationPresenter presenter, @@ -477,6 +492,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx notificationInfoView.getSelectedAction(), mShortcutManager, pmUser, + mPeopleSpaceWidgetManager, mNotificationManager, mOnUserInteractionCallback, packageName, @@ -492,7 +508,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mMainHandler, mBgHandler, onConversationSettingsListener, - mBubblesManagerOptional); + mBubblesManagerOptional, + mShadeController); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt index fab367df8fde..ae1285d3a5d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt @@ -22,6 +22,7 @@ import android.animation.AnimatorSet import android.animation.ValueAnimator import android.app.Dialog import android.content.Context +import android.content.pm.ShortcutInfo import android.graphics.Color import android.graphics.PixelFormat import android.graphics.drawable.ColorDrawable @@ -31,7 +32,6 @@ import android.text.SpannableStringBuilder import android.text.style.BulletSpan import android.view.Gravity import android.view.View -import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.Window @@ -44,31 +44,36 @@ import android.widget.TextView import com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN import com.android.systemui.Prefs import com.android.systemui.R +import com.android.systemui.people.widget.PeopleSpaceWidgetManager import com.android.systemui.statusbar.notification.row.NotificationConversationInfo.OnConversationSettingsClickListener +import com.android.systemui.statusbar.phone.ShadeController import javax.inject.Inject - /** * Controller to handle presenting the priority conversations onboarding dialog */ class PriorityOnboardingDialogController @Inject constructor( - val view: View, - val context: Context, - private val ignoresDnd: Boolean, - private val showsAsBubble: Boolean, - val icon : Drawable, - private val onConversationSettingsClickListener : OnConversationSettingsClickListener, - val badge : Drawable + val view: View, + val context: Context, + private val ignoresDnd: Boolean, + private val showsAsBubble: Boolean, + val icon: Drawable, + private val onConversationSettingsClickListener: OnConversationSettingsClickListener, + val badge: Drawable, + private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager, + private val shadeController: ShadeController ) { private lateinit var dialog: Dialog + private lateinit var shortcutInfo: ShortcutInfo private val OVERSHOOT: Interpolator = PathInterpolator(0.4f, 0f, 0.2f, 1.4f) private val IMPORTANCE_ANIM_DELAY = 150L private val IMPORTANCE_ANIM_GROW_DURATION = 250L private val IMPORTANCE_ANIM_SHRINK_DURATION = 200L private val IMPORTANCE_ANIM_SHRINK_DELAY = 25L - fun init() { + fun init(info: ShortcutInfo) { + shortcutInfo = info initDialog() } @@ -78,13 +83,15 @@ class PriorityOnboardingDialogController @Inject constructor( private fun done() { // Log that the user has seen the onboarding - Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, true) + Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, true) dialog.dismiss() + shadeController.animateCollapsePanels() + peopleSpaceWidgetManager.requestPinAppWidget(shortcutInfo) } private fun settings() { // Log that the user has seen the onboarding - Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, true) + Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, true) dialog.dismiss() onConversationSettingsClickListener?.onClick() } @@ -95,9 +102,11 @@ class PriorityOnboardingDialogController @Inject constructor( private var ignoresDnd = false private var showAsBubble = false private lateinit var icon: Drawable - private lateinit var onConversationSettingsClickListener - : OnConversationSettingsClickListener - private lateinit var badge : Drawable + private lateinit var onConversationSettingsClickListener: + OnConversationSettingsClickListener + private lateinit var badge: Drawable + private lateinit var peopleSpaceWidgetManager: PeopleSpaceWidgetManager + private lateinit var shadeController: ShadeController fun setView(v: View): Builder { view = v @@ -119,24 +128,36 @@ class PriorityOnboardingDialogController @Inject constructor( return this } - fun setIcon(draw : Drawable) : Builder { + fun setIcon(draw: Drawable): Builder { icon = draw return this } - fun setBadge(badge : Drawable) : Builder { + fun setBadge(badge: Drawable): Builder { this.badge = badge return this } - fun setOnSettingsClick(onClick : OnConversationSettingsClickListener) : Builder { + fun setOnSettingsClick(onClick: OnConversationSettingsClickListener): Builder { onConversationSettingsClickListener = onClick return this } + fun setShadeController(shadeController: ShadeController): Builder { + this.shadeController = shadeController + return this + } + + fun setPeopleSpaceWidgetManager(peopleSpaceWidgetManager: PeopleSpaceWidgetManager): + Builder { + this.peopleSpaceWidgetManager = peopleSpaceWidgetManager + return this + } + fun build(): PriorityOnboardingDialogController { val controller = PriorityOnboardingDialogController( view, context, ignoresDnd, showAsBubble, icon, - onConversationSettingsClickListener, badge) + onConversationSettingsClickListener, badge, peopleSpaceWidgetManager, + shadeController) return controller } } @@ -185,8 +206,8 @@ class PriorityOnboardingDialogController @Inject constructor( val bgSize = context.resources.getDimensionPixelSize( com.android.internal.R.dimen.conversation_icon_size_badged) - val animatorUpdateListener: ValueAnimator.AnimatorUpdateListener - = ValueAnimator.AnimatorUpdateListener { animation -> + val animatorUpdateListener: ValueAnimator.AnimatorUpdateListener = + ValueAnimator.AnimatorUpdateListener { animation -> val strokeWidth = animation.animatedValue as Int ring.setStroke(strokeWidth, ringColor) val newSize = baseSize + strokeWidth * 2 @@ -199,8 +220,8 @@ class PriorityOnboardingDialogController @Inject constructor( growAnimation.duration = IMPORTANCE_ANIM_GROW_DURATION growAnimation.addUpdateListener(animatorUpdateListener) - val shrinkAnimation: ValueAnimator - = ValueAnimator.ofInt(largeThickness, standardThickness) + val shrinkAnimation: ValueAnimator = + ValueAnimator.ofInt(largeThickness, standardThickness) shrinkAnimation.duration = IMPORTANCE_ANIM_SHRINK_DURATION shrinkAnimation.startDelay = IMPORTANCE_ANIM_SHRINK_DELAY shrinkAnimation.interpolator = OVERSHOOT @@ -208,15 +229,14 @@ class PriorityOnboardingDialogController @Inject constructor( shrinkAnimation.addListener(object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator?) { // Shrink the badge bg so that it doesn't peek behind the animation - bg.setSize(baseSize, baseSize); - conversationIconBadgeBg.invalidate(); + bg.setSize(baseSize, baseSize) + conversationIconBadgeBg.invalidate() } override fun onAnimationEnd(animation: Animator?) { // Reset bg back to normal size - bg.setSize(bgSize, bgSize); - conversationIconBadgeBg.invalidate(); - + bg.setSize(bgSize, bgSize) + conversationIconBadgeBg.invalidate() } }) @@ -228,20 +248,20 @@ class PriorityOnboardingDialogController @Inject constructor( R.dimen.conversation_onboarding_bullet_gap_width) val description = SpannableStringBuilder() description.append(context.getText(R.string.priority_onboarding_show_at_top_text), - BulletSpan(gapWidth), /* flags */0) + BulletSpan(gapWidth), /* flags */0) description.append(System.lineSeparator()) description.append(context.getText(R.string.priority_onboarding_show_avatar_text), - BulletSpan(gapWidth), /* flags */0) + BulletSpan(gapWidth), /* flags */0) if (showsAsBubble) { description.append(System.lineSeparator()) description.append(context.getText( R.string.priority_onboarding_appear_as_bubble_text), - BulletSpan(gapWidth), /* flags */0) + BulletSpan(gapWidth), /* flags */0) } if (ignoresDnd) { description.append(System.lineSeparator()) description.append(context.getText(R.string.priority_onboarding_ignores_dnd_text), - BulletSpan(gapWidth), /* flags */0) + BulletSpan(gapWidth), /* flags */0) } findViewById<TextView>(R.id.behaviors).setText(description) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 970efd5cbfe2..f6c1b1c50fee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -456,6 +456,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private long mNumHeadsUp; private NotificationStackScrollLayoutController.TouchHandler mTouchHandler; private final FeatureFlags mFeatureFlags; + private boolean mShouldUseSplitNotificationShade; private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener = new ExpandableView.OnHeightChangedListener() { @@ -500,7 +501,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable super(context, attrs, 0, 0); Resources res = getResources(); mSectionsManager = notificationSectionsManager; - + mFeatureFlags = featureFlags; + mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res); mSectionsManager.initialize(this, LayoutInflater.from(context)); mSections = mSectionsManager.createSectionsForBuckets(); @@ -533,7 +535,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mGroupMembershipManager = groupMembershipManager; mGroupExpansionManager = groupExpansionManager; mStatusbarStateController = statusbarStateController; - mFeatureFlags = featureFlags; } void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) { @@ -1164,7 +1165,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (stackStartPosition <= stackEndPosition) { stackHeight = stackEndPosition; } else { - if (shouldUseSplitNotificationShade(mFeatureFlags, getResources())) { + if (mShouldUseSplitNotificationShade) { // This prevents notifications from being collapsed when QS is expanded. stackHeight = (int) height; } else { @@ -1552,8 +1553,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height); - float densityScale = getResources().getDisplayMetrics().density; + Resources res = getResources(); + mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res); + mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height); + float densityScale = res.getDisplayMetrics().density; mSwipeHelper.setDensityScale(densityScale); float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop(); mSwipeHelper.setPagingTouchSlop(pagingTouchSlop); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 713daaa9fd4a..6b69103f6030 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -533,6 +533,17 @@ public class KeyguardBouncer { } } + /** + * Apply keyguard configuration from the currently active resources. This can be called when the + * device configuration changes, to re-apply some resources that are qualified on the device + * configuration. + */ + public void updateResources() { + if (mKeyguardViewController != null) { + mKeyguardViewController.updateResources(); + } + } + public void dump(PrintWriter pw) { pw.println("KeyguardBouncer"); pw.println(" isShowing(): " + isShowing()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index cd712044a60e..4ef6668acfb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -305,6 +305,7 @@ public class NotificationPanelViewController extends PanelViewController { // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card. // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications private final int mMaxKeyguardNotifications; + private boolean mShouldUseSplitNotificationShade; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; @@ -598,6 +599,8 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardUserSwitcherEnabled && mResources.getBoolean( R.bool.config_keyguard_user_switch_opens_qs_details); keyguardUpdateMonitor.setKeyguardQsUserSwitchEnabled(mKeyguardQsUserSwitchEnabled); + mShouldUseSplitNotificationShade = + Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources); mView.setWillNotDraw(!DEBUG); mLayoutInflater = layoutInflater; mFalsingManager = falsingManager; @@ -736,7 +739,7 @@ public class NotificationPanelViewController extends PanelViewController { mView.setAccessibilityDelegate(mAccessibilityDelegate); // dynamically apply the split shade value overrides. - if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) { + if (mShouldUseSplitNotificationShade) { updateResources(); } } @@ -835,12 +838,13 @@ public class NotificationPanelViewController extends PanelViewController { public void updateResources() { int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width); int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); - + mShouldUseSplitNotificationShade = + Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources); // To change the constraints at runtime, all children of the ConstraintLayout must have ids ensureAllViewsHaveIds(mNotificationContainerParent); ConstraintSet constraintSet = new ConstraintSet(); constraintSet.clone(mNotificationContainerParent); - if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) { + if (mShouldUseSplitNotificationShade) { // width = 0 to take up all available space within constraints qsWidth = 0; panelWidth = 0; @@ -1915,7 +1919,7 @@ public class NotificationPanelViewController extends PanelViewController { mBarState != KEYGUARD && (!mQsExpanded || mQsExpansionFromOverscroll - || Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources))); + || mShouldUseSplitNotificationShade)); if (mKeyguardUserSwitcherController != null && mQsExpanded && !mStackScrollerOverscrolling) { @@ -1987,7 +1991,7 @@ public class NotificationPanelViewController extends PanelViewController { private float calculateQsTopPadding() { // in split shade mode we want notifications to be directly below status bar - if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources) && !mKeyguardShowing) { + if (mShouldUseSplitNotificationShade && !mKeyguardShowing) { return 0f; } if (mKeyguardShowing && (mQsExpandImmediate @@ -2203,8 +2207,7 @@ public class NotificationPanelViewController extends PanelViewController { return true; } - return !Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources) - && (isInSettings() || mIsPanelCollapseOnQQS); + return !mShouldUseSplitNotificationShade && (isInSettings() || mIsPanelCollapseOnQQS); } @Override @@ -2631,7 +2634,7 @@ public class NotificationPanelViewController extends PanelViewController { super.onTrackingStarted(); if (mQsFullyExpanded) { mQsExpandImmediate = true; - if (!Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) { + if (!mShouldUseSplitNotificationShade) { mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); } } @@ -2882,7 +2885,7 @@ public class NotificationPanelViewController extends PanelViewController { */ protected void updateHorizontalPanelPosition(float x) { if (mNotificationStackScrollLayoutController.getWidth() * 1.75f > mView.getWidth() - || Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) { + || mShouldUseSplitNotificationShade) { resetHorizontalPanelPosition(); return; } @@ -3573,7 +3576,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onOverscrollTopChanged(float amount, boolean isRubberbanded) { // When in split shade, overscroll shouldn't carry through to QS - if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) { + if (mShouldUseSplitNotificationShade) { return; } cancelQsAnimation(); 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 5045e95dd53f..c6ef8a3d7ba4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -2963,6 +2963,9 @@ public class StatusBar extends SystemUI implements DemoMode, if (mBrightnessMirrorController != null) { mBrightnessMirrorController.updateResources(); } + if (mStatusBarKeyguardViewManager != null) { + mStatusBarKeyguardViewManager.updateResources(); + } mPowerButtonReveal = new PowerButtonReveal(mContext.getResources().getDimensionPixelSize( R.dimen.global_actions_top_padding)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 81e24cc25aa6..c1f300b49d24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -1084,6 +1084,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb || mBouncer.isFullscreenBouncer(); } + /** + * Apply keyguard configuration from the currently active resources. This can be called when the + * device configuration changes, to re-apply some resources that are qualified on the device + * configuration. + */ + public void updateResources() { + if (mBouncer != null) { + mBouncer.updateResources(); + } + } + public void dump(PrintWriter pw) { pw.println("StatusBarKeyguardViewManager:"); pw.println(" mShowing: " + mShowing); diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java index fd19528e6d55..3892f310e669 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java @@ -26,6 +26,7 @@ import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.UserHandle; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; @@ -43,6 +44,7 @@ import com.android.systemui.plugins.ToastPlugin; * directly. Instead, use {@link ToastFactory#createToast}. */ public class SystemUIToast implements ToastPlugin.Toast { + static final String TAG = "SystemUIToast"; final Context mContext; final CharSequence mText; final ToastPlugin.Toast mPluginToast; @@ -225,8 +227,12 @@ public class SystemUIToast implements ToastPlugin.Toast { int userId) { final ApplicationsState appState = ApplicationsState.getInstance((Application) context.getApplicationContext()); + if (!appState.isUserAdded(userId)) { + Log.d(TAG, "user hasn't been fully initialized, not showing an app icon for " + + "packageName=" + packageName); + return null; + } final AppEntry appEntry = appState.getEntry(packageName, userId); - if (!ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(appEntry)) { return null; } @@ -237,6 +243,5 @@ public class SystemUIToast implements ToastPlugin.Toast { Bitmap iconBmp = iconFactory.createBadgedIconBitmap( appInfo.loadUnbadgedIcon(context.getPackageManager()), user, true).icon; return new BitmapDrawable(context.getResources(), iconBmp); - } } diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt index 6aadd1020bce..dc86d5893adb 100644 --- a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt @@ -17,8 +17,6 @@ package com.android.systemui.util import android.content.res.Resources -import android.graphics.Canvas -import android.graphics.Path import android.graphics.Rect import android.graphics.drawable.Drawable import android.graphics.drawable.DrawableWrapper @@ -43,53 +41,25 @@ class RoundedCornerProgressDrawable @JvmOverloads constructor( private const val MAX_LEVEL = 10000 // Taken from Drawable } - private var clipPath: Path = Path() - - init { - setClipPath(Rect()) - } - override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean { onLevelChange(level) return super.onLayoutDirectionChanged(layoutDirection) } override fun onBoundsChange(bounds: Rect) { - setClipPath(bounds) super.onBoundsChange(bounds) onLevelChange(level) } - private fun setClipPath(bounds: Rect) { - clipPath.reset() - clipPath.addRoundRect( - bounds.left.toFloat(), - bounds.top.toFloat(), - bounds.right.toFloat(), - bounds.bottom.toFloat(), - bounds.height().toFloat() / 2, - bounds.height().toFloat() / 2, - Path.Direction.CW - ) - } - override fun onLevelChange(level: Int): Boolean { val db = drawable?.bounds!! - val width = bounds.width() * level / MAX_LEVEL - // Extra space on the left to keep the rounded shape on the right end - val leftBound = bounds.left - bounds.height() - drawable?.setBounds(leftBound, db.top, bounds.left + width, db.bottom) + // On 0, the width is bounds.height (a circle), and on MAX_LEVEL, the width is bounds.width + val width = bounds.height() + (bounds.width() - bounds.height()) * level / MAX_LEVEL + drawable?.setBounds(bounds.left, db.top, bounds.left + width, db.bottom) return super.onLevelChange(level) } - override fun draw(canvas: Canvas) { - canvas.save() - canvas.clipPath(clipPath) - super.draw(canvas) - canvas.restore() - } - - override fun getConstantState(): ConstantState? { + override fun getConstantState(): ConstantState { // This should not be null as it was created with a state in the constructor. return RoundedCornerState(super.getConstantState()!!) } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java index 64632afe9bfa..714f1f235c52 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java @@ -16,6 +16,9 @@ package com.android.keyguard; +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.Mockito.mock; import static org.mockito.Mockito.verify; @@ -26,11 +29,14 @@ import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.Gravity; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -46,7 +52,7 @@ public class KeyguardHostViewControllerTest extends SysuiTestCase { @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock + private KeyguardHostView mKeyguardHostView; @Mock private AudioManager mAudioManager; @@ -66,6 +72,10 @@ public class KeyguardHostViewControllerTest extends SysuiTestCase { @Before public void setup() { + mContext.ensureTestableResources(); + + mKeyguardHostView = new KeyguardHostView(mContext); + when(mKeyguardSecurityContainerControllerFactory.create(any( KeyguardSecurityContainer.SecurityCallback.class))) .thenReturn(mKeyguardSecurityContainerController); @@ -76,10 +86,10 @@ public class KeyguardHostViewControllerTest extends SysuiTestCase { @Test public void testHasDismissActions() { - Assert.assertFalse("Action not set yet", mKeyguardHostViewController.hasDismissActions()); + assertFalse("Action not set yet", mKeyguardHostViewController.hasDismissActions()); mKeyguardHostViewController.setOnDismissAction(mock(OnDismissAction.class), null /* cancelAction */); - Assert.assertTrue("Action should exist", mKeyguardHostViewController.hasDismissActions()); + assertTrue("Action should exist", mKeyguardHostViewController.hasDismissActions()); } @Test @@ -87,4 +97,31 @@ public class KeyguardHostViewControllerTest extends SysuiTestCase { mKeyguardHostViewController.onStartingToHide(); verify(mKeyguardSecurityContainerController).onStartingToHide(); } + + @Test + public void testGravityReappliedOnConfigurationChange() { + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mKeyguardHostView.setLayoutParams(lp); + + // Set initial gravity + mContext.getOrCreateTestableResources().addOverride(R.integer.keyguard_host_view_gravity, + Gravity.CENTER); + + // Kick off the initial pass... + mKeyguardHostViewController.init(); + assertEquals( + ((FrameLayout.LayoutParams) mKeyguardHostView.getLayoutParams()).gravity, + Gravity.CENTER); + + // Now simulate a config change + mContext.getOrCreateTestableResources().addOverride(R.integer.keyguard_host_view_gravity, + Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); + + mKeyguardHostViewController.updateResources(); + assertEquals( + ((FrameLayout.LayoutParams) mKeyguardHostView.getLayoutParams()).gravity, + Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java index e1ddaada44cf..daa896cc6a8b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java @@ -41,6 +41,7 @@ import android.view.DisplayInfo; import android.view.SurfaceHolder; import com.android.systemui.glwallpaper.ImageWallpaperRenderer; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import org.junit.Before; import org.junit.Ignore; @@ -99,7 +100,7 @@ public class ImageWallpaperTest extends SysuiTestCase { } private ImageWallpaper createImageWallpaper() { - return new ImageWallpaper() { + return new ImageWallpaper(mock(StatusBarStateController.class)) { @Override public Engine onCreateEngine() { return new GLEngine(mHandler) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java index d015f51055b5..67c1d075d03e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyCollection; import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -78,17 +79,21 @@ public class BrightLineClassifierTest extends SysuiTestCase { private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); - private final FalsingClassifier.Result mFalsedResult = FalsingClassifier.Result.falsed(1, ""); + private final FalsingClassifier.Result mFalsedResult = + FalsingClassifier.Result.falsed(1, getClass().getSimpleName(), ""); private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1); private GestureCompleteListener mGestureCompleteListener; @Before public void setup() { MockitoAnnotations.initMocks(this); - when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mPassedResult); - when(mClassifierB.classifyGesture(anyDouble(), anyDouble())).thenReturn(mPassedResult); + when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mPassedResult); + when(mClassifierB.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mPassedResult); when(mSingleTapClassfier.isTap(any(List.class))).thenReturn(mPassedResult); - when(mDoubleTapClassifier.classifyGesture()).thenReturn(mPassedResult); + when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mPassedResult); mClassifiers.add(mClassifierA); mClassifiers.add(mClassifierB); when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList); @@ -131,20 +136,23 @@ public class BrightLineClassifierTest extends SysuiTestCase { @Test public void testIsFalseTouch_ClassifierARejects() { - when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mFalsedResult); + when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mFalsedResult); assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isTrue(); } @Test public void testIsFalseTouch_ClassifierBRejects() { - when(mClassifierB.classifyGesture(anyDouble(), anyDouble())).thenReturn(mFalsedResult); + when(mClassifierB.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mFalsedResult); assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isTrue(); } @Test public void testIsFalseTouch_FaceAuth() { // Even when the classifiers report a false, we should allow. - when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mPassedResult); + when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mPassedResult); when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true); assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); @@ -153,7 +161,8 @@ public class BrightLineClassifierTest extends SysuiTestCase { @Test public void testIsFalseTouch_Docked() { // Even when the classifiers report a false, we should allow. - when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mPassedResult); + when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mPassedResult); mDockManager.setIsDocked(true); assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); @@ -173,7 +182,8 @@ public class BrightLineClassifierTest extends SysuiTestCase { @Test public void testIsFalseTap_RobustCheck_NoFaceAuth() { when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult); - when(mDoubleTapClassifier.classifyGesture()).thenReturn(mFalsedResult); + when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mFalsedResult); when(mHistoryTracker.falseBelief()).thenReturn(1.0); mFalsingDataProvider.setJustUnlockedWithFace(false); assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isTrue(); @@ -188,11 +198,13 @@ public class BrightLineClassifierTest extends SysuiTestCase { @Test public void testIsFalseDoubleTap() { - when(mDoubleTapClassifier.classifyGesture()).thenReturn(mPassedResult); + when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mPassedResult); assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse(); - when(mDoubleTapClassifier.classifyGesture()).thenReturn(mFalsedResult); + when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mFalsedResult); assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isTrue(); } @@ -243,13 +255,15 @@ public class BrightLineClassifierTest extends SysuiTestCase { public void testNoFalsingUnlocked() { when(mKeyguardStateController.isShowing()).thenReturn(false); - when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mFalsedResult); + when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mFalsedResult); assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult); assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse(); - when(mDoubleTapClassifier.classifyGesture()).thenReturn(mFalsedResult); + when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) + .thenReturn(mFalsedResult); assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java index ca0a4aa2b04d..7d6ff34bb954 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java @@ -16,8 +16,6 @@ package com.android.systemui.classifier; -import static com.android.systemui.classifier.Classifier.UNLOCK; - import android.util.DisplayMetrics; import android.view.MotionEvent; @@ -45,7 +43,6 @@ public class ClassifierTest extends LeakCheckedTest { displayMetrics.heightPixels = 1000; mFakeBatteryController = new FakeBatteryController(getLeakCheck()); mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController); - mDataProvider.setInteractionType(UNLOCK); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java index dafc87133d39..14dcd58e40a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java @@ -16,11 +16,12 @@ package com.android.systemui.classifier; +import static com.android.systemui.classifier.Classifier.GENERIC; import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; @@ -69,106 +70,104 @@ public class DiagonalClassifierTest extends ClassifierTest { @Test public void testPass_UnknownAngle() { when(mDataProvider.getAngle()).thenReturn(Float.MAX_VALUE); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); } @Test public void testPass_VerticalSwipe() { when(mDataProvider.getAngle()).thenReturn(UP_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn(DOWN_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); } @Test public void testPass_MostlyVerticalSwipe() { when(mDataProvider.getAngle()).thenReturn(UP_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn(UP_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn(DOWN_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn(DOWN_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS * 2); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); } @Test public void testPass_BarelyVerticalSwipe() { when(mDataProvider.getAngle()).thenReturn( UP_IN_RADIANS - FORTY_FIVE_DEG_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn( UP_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn( DOWN_IN_RADIANS - FORTY_FIVE_DEG_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn( DOWN_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS * 2); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); } @Test public void testPass_HorizontalSwipe() { when(mDataProvider.getAngle()).thenReturn(RIGHT_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn(LEFT_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); } @Test public void testPass_MostlyHorizontalSwipe() { when(mDataProvider.getAngle()).thenReturn(RIGHT_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn(RIGHT_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn(LEFT_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn(LEFT_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); } @Test public void testPass_BarelyHorizontalSwipe() { when(mDataProvider.getAngle()).thenReturn( RIGHT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn( LEFT_IN_RADIANS - FORTY_FIVE_DEG_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn( LEFT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.getAngle()).thenReturn( RIGHT_IN_RADIANS - FORTY_FIVE_DEG_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS * 2); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse(); } @Test public void testPass_AffordanceSwipe() { - when(mDataProvider.getInteractionType()).thenReturn(LEFT_AFFORDANCE); when(mDataProvider.getAngle()).thenReturn( RIGHT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(LEFT_AFFORDANCE, 0.5, 0).isFalse()).isFalse(); - when(mDataProvider.getInteractionType()).thenReturn(RIGHT_AFFORDANCE); when(mDataProvider.getAngle()).thenReturn( LEFT_IN_RADIANS - FORTY_FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(RIGHT_AFFORDANCE, 0.5, 0).isFalse()).isFalse(); // This classifier may return false for other angles, but these are the only // two that actually matter, as affordances generally only travel in these two directions. @@ -182,37 +181,37 @@ public class DiagonalClassifierTest extends ClassifierTest { when(mDataProvider.isVertical()).thenReturn(false); when(mDataProvider.getAngle()).thenReturn( RIGHT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.getAngle()).thenReturn( UP_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS + FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.getAngle()).thenReturn( LEFT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.getAngle()).thenReturn( DOWN_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS + FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue(); // Vertical Swipes when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.getAngle()).thenReturn( RIGHT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS + FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.getAngle()).thenReturn( UP_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.getAngle()).thenReturn( LEFT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS + FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.getAngle()).thenReturn( DOWN_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - FIVE_DEG_IN_RADIANS); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java index f6c14240c8f4..db0619928635 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java @@ -16,8 +16,7 @@ package com.android.systemui.classifier; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; import android.testing.AndroidTestingRunner; @@ -51,32 +50,32 @@ public class DistanceClassifierTest extends ClassifierTest { @Test public void testPass_noPointer() { - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test public void testPass_fling() { mClassifier.onTouchEvent(appendDownEvent(1, 1)); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); mClassifier.onTouchEvent(appendMoveEvent(1, 40)); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); mClassifier.onTouchEvent(appendUpEvent(1, 80)); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); } @Test public void testFail_flingShort() { mClassifier.onTouchEvent(appendDownEvent(1, 1)); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); mClassifier.onTouchEvent(appendMoveEvent(1, 2)); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); mClassifier.onTouchEvent(appendUpEvent(1, 10)); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -84,26 +83,26 @@ public class DistanceClassifierTest extends ClassifierTest { // These events, in testing, result in a fling that falls just short of the threshold. mClassifier.onTouchEvent(appendDownEvent(1, 1, 1)); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); mClassifier.onTouchEvent(appendMoveEvent(1, 15, 2)); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); mClassifier.onTouchEvent(appendMoveEvent(1, 16, 3)); mClassifier.onTouchEvent(appendMoveEvent(1, 17, 300)); mClassifier.onTouchEvent(appendMoveEvent(1, 18, 301)); mClassifier.onTouchEvent(appendUpEvent(1, 19, 501)); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test public void testPass_swipe() { mClassifier.onTouchEvent(appendDownEvent(1, 1)); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); mClassifier.onTouchEvent(appendMoveEvent(1, mDataProvider.getYdpi() * 3, 3)); mClassifier.onTouchEvent(appendUpEvent(1, mDataProvider.getYdpi() * 3, 300)); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java index 4e1742497d90..f726cbead1f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java @@ -16,8 +16,8 @@ package com.android.systemui.classifier; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.when; @@ -52,7 +52,8 @@ public class DoubleTapClassifierTest extends ClassifierTest { private SingleTapClassifier mSingleTapClassifier; private DoubleTapClassifier mClassifier; - private final FalsingClassifier.Result mFalsedResult = FalsingClassifier.Result.falsed(1, ""); + private final FalsingClassifier.Result mFalsedResult = + FalsingClassifier.Result.falsed(1, getClass().getSimpleName(), ""); private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1); @Before @@ -81,8 +82,8 @@ public class DoubleTapClassifierTest extends ClassifierTest { addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP, 1); - boolean result = mClassifier.classifyGesture().isFalse(); - assertThat("Single tap recognized as a valid double tap", result, is(true)); + boolean result = mClassifier.classifyGesture(0, 0.5, 1).isFalse(); + assertThat(result).isTrue(); } @Test @@ -97,8 +98,8 @@ public class DoubleTapClassifierTest extends ClassifierTest { addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, TOUCH_SLOP, TOUCH_SLOP); addMotionEvent(2, 3, MotionEvent.ACTION_UP, TOUCH_SLOP, TOUCH_SLOP); - FalsingClassifier.Result result = mClassifier.classifyGesture(); - assertThat(result.getReason(), result.isFalse(), is(false)); + FalsingClassifier.Result result = mClassifier.classifyGesture(0, 0.5, 1); + assertThat(result.isFalse()).isFalse(); } @Test @@ -113,8 +114,8 @@ public class DoubleTapClassifierTest extends ClassifierTest { addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(2, 3, MotionEvent.ACTION_UP, 1, 1); - boolean result = mClassifier.classifyGesture().isFalse(); - assertThat("Bad first touch allowed", result, is(true)); + boolean result = mClassifier.classifyGesture(0, 0.5, 1).isFalse(); + assertThat(result).isTrue(); } @Test @@ -129,8 +130,8 @@ public class DoubleTapClassifierTest extends ClassifierTest { addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(2, 3, MotionEvent.ACTION_UP, 1, 1); - boolean result = mClassifier.classifyGesture().isFalse(); - assertThat("Bad second touch allowed", result, is(true)); + boolean result = mClassifier.classifyGesture(0, 0.5, 1).isFalse(); + assertThat(result).isTrue(); } @Test @@ -145,8 +146,8 @@ public class DoubleTapClassifierTest extends ClassifierTest { addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, TOUCH_SLOP + 1, TOUCH_SLOP); addMotionEvent(2, 3, MotionEvent.ACTION_UP, TOUCH_SLOP, TOUCH_SLOP + 1); - boolean result = mClassifier.classifyGesture().isFalse(); - assertThat("Sloppy second touch allowed", result, is(true)); + boolean result = mClassifier.classifyGesture(0, 0.5, 1).isFalse(); + assertThat(result).isTrue(); } @Test @@ -163,8 +164,8 @@ public class DoubleTapClassifierTest extends ClassifierTest { addMotionEvent(DOUBLE_TAP_TIMEOUT_MS + 1, DOUBLE_TAP_TIMEOUT_MS + 2, MotionEvent.ACTION_UP, 1, 1); - boolean result = mClassifier.classifyGesture().isFalse(); - assertThat("Slow second tap allowed", result, is(true)); + boolean result = mClassifier.classifyGesture(0, 0.5, 1).isFalse(); + assertThat(result).isTrue(); } private void addMotionEvent(long downMs, long eventMs, int action, int x, int y) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java index bb7545f93b4b..38355c74a4a7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java @@ -121,7 +121,8 @@ public class HistoryTrackerTest extends SysuiTestCase { private void addResult(boolean falsed, double confidence) { mHistoryTracker.addResults(Collections.singletonList( falsed - ? FalsingClassifier.Result.falsed(confidence, "test") + ? FalsingClassifier.Result.falsed( + confidence, getClass().getSimpleName(), "test") : FalsingClassifier.Result.passed(confidence)), mSystemClock.uptimeMillis()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java index 76802f4de14f..b8ea062eedc8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java @@ -18,8 +18,10 @@ package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.ArgumentMatchers.anyInt; import android.testing.AndroidTestingRunner; import android.view.MotionEvent; @@ -50,13 +52,15 @@ public class PointerCountClassifierTest extends ClassifierTest { @Test public void testPass_noPointer() { - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()).isFalse()) + .isFalse(); } @Test public void testPass_singlePointer() { mClassifier.onTouchEvent(appendDownEvent(1, 1)); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()).isFalse()) + .isFalse(); } @Test @@ -72,7 +76,8 @@ public class PointerCountClassifierTest extends ClassifierTest { 0, 0); mClassifier.onTouchEvent(motionEvent); motionEvent.recycle(); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(Classifier.GENERIC, 0.5, 1).isFalse()) + .isTrue(); } @Test @@ -88,7 +93,6 @@ public class PointerCountClassifierTest extends ClassifierTest { 0, 0); mClassifier.onTouchEvent(motionEvent); motionEvent.recycle(); - getDataProvider().setInteractionType(QUICK_SETTINGS); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse()).isFalse(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java index ba8ca9abc2d7..52423bdac85f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java @@ -51,14 +51,13 @@ public class ProximityClassifierTest extends ClassifierTest { private FalsingClassifier mClassifier; private final FalsingClassifier.Result mFalsedResult = - FalsingClassifier.Result.falsed(1, "test"); + FalsingClassifier.Result.falsed(1, getClass().getSimpleName() , "test"); private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1); @Before public void setup() { super.setup(); MockitoAnnotations.initMocks(this); - when(mDataProvider.getInteractionType()).thenReturn(GENERIC); when(mDistanceClassifier.isLongSwipe()).thenReturn(mFalsedResult); mClassifier = new ProximityClassifier( mDistanceClassifier, mDataProvider, new DeviceConfigProxyFake()); @@ -73,7 +72,7 @@ public class ProximityClassifierTest extends ClassifierTest { public void testPass_uncovered() { touchDown(); touchUp(10); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false)); } @Test @@ -82,17 +81,16 @@ public class ProximityClassifierTest extends ClassifierTest { mClassifier.onProximityEvent(createSensorEvent(true, 1)); mClassifier.onProximityEvent(createSensorEvent(false, 2)); touchUp(20); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false)); } @Test public void testPass_quickSettings() { touchDown(); - when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS); mClassifier.onProximityEvent(createSensorEvent(true, 1)); mClassifier.onProximityEvent(createSensorEvent(false, 11)); touchUp(10); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse(), is(false)); } @Test @@ -101,7 +99,7 @@ public class ProximityClassifierTest extends ClassifierTest { mClassifier.onProximityEvent(createSensorEvent(true, 1)); mClassifier.onProximityEvent(createSensorEvent(false, 11)); touchUp(10); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(true)); } @Test @@ -112,7 +110,7 @@ public class ProximityClassifierTest extends ClassifierTest { mClassifier.onProximityEvent(createSensorEvent(true, 96)); mClassifier.onProximityEvent(createSensorEvent(false, 100)); touchUp(100); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(true)); } @Test @@ -122,7 +120,7 @@ public class ProximityClassifierTest extends ClassifierTest { mClassifier.onProximityEvent(createSensorEvent(false, 11)); touchUp(10); when(mDistanceClassifier.isLongSwipe()).thenReturn(mPassedResult); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false)); } private void touchDown() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java index 62c876f99a15..15241073d191 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java @@ -16,8 +16,8 @@ package com.android.systemui.classifier; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; @@ -70,14 +70,14 @@ public class SingleTapClassifierTest extends ClassifierTest { addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP, 1); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); mMotionEvents.clear(); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, -TOUCH_SLOP + 2, 1); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); } @@ -86,14 +86,14 @@ public class SingleTapClassifierTest extends ClassifierTest { addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); mMotionEvents.clear(); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, -TOUCH_SLOP + 2); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); } @@ -102,14 +102,14 @@ public class SingleTapClassifierTest extends ClassifierTest { addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP + 1, 1); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); mMotionEvents.clear(); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, -TOUCH_SLOP - 1, 1); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @@ -118,14 +118,14 @@ public class SingleTapClassifierTest extends ClassifierTest { addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP + 1); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); mMotionEvents.clear(); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, -TOUCH_SLOP - 1); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -134,7 +134,7 @@ public class SingleTapClassifierTest extends ClassifierTest { addMotionEvent(0, 1, MotionEvent.ACTION_MOVE, 1, TOUCH_SLOP + 1); addMotionEvent(0, 2, MotionEvent.ACTION_UP, 1, 1); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -142,12 +142,12 @@ public class SingleTapClassifierTest extends ClassifierTest { addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); - assertThat(mClassifier.isTap(mMotionEvents).isFalse(), is(false)); + assertThat(mClassifier.isTap(mMotionEvents).isFalse()).isFalse(); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP + 1); - assertThat(mClassifier.isTap(mMotionEvents).isFalse(), is(true)); + assertThat(mClassifier.isTap(mMotionEvents).isFalse()).isTrue(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java index 4a896a8574b9..068a1e553cbf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java @@ -25,8 +25,8 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.UNLOCK; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; @@ -56,248 +56,225 @@ public class TypeClassifierTest extends ClassifierTest { @Test public void testPass_QuickSettings() { - when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS); when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(false); when(mDataProvider.isRight()).thenReturn(false); // right should cause no effect. - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse()).isFalse(); } @Test public void testFalse_QuickSettings() { - when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS); - when(mDataProvider.isVertical()).thenReturn(false); when(mDataProvider.isUp()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse()).isTrue(); } @Test public void testPass_PulseExpand() { - when(mDataProvider.getInteractionType()).thenReturn(PULSE_EXPAND); when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(false); when(mDataProvider.isRight()).thenReturn(false); // right should cause no effect. - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(PULSE_EXPAND, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(PULSE_EXPAND, 0.5, 0).isFalse()).isFalse(); } @Test public void testFalse_PulseExpand() { - when(mDataProvider.getInteractionType()).thenReturn(PULSE_EXPAND); - when(mDataProvider.isVertical()).thenReturn(false); when(mDataProvider.isUp()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(PULSE_EXPAND, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(PULSE_EXPAND, 0.5, 0).isFalse()).isTrue(); } @Test public void testPass_NotificationDragDown() { - when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DRAG_DOWN); when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(false); when(mDataProvider.isRight()).thenReturn(false); // right should cause no effect. - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DRAG_DOWN, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DRAG_DOWN, 0.5, 0).isFalse()).isFalse(); } @Test public void testFalse_NotificationDragDown() { - when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DRAG_DOWN); - when(mDataProvider.isVertical()).thenReturn(false); when(mDataProvider.isUp()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DRAG_DOWN, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DRAG_DOWN, 0.5, 0).isFalse()).isTrue(); } @Test public void testPass_NotificationDismiss() { - when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DISMISS); when(mDataProvider.isVertical()).thenReturn(false); when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect. when(mDataProvider.isRight()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DISMISS, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DISMISS, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isUp()).thenReturn(false); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DISMISS, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DISMISS, 0.5, 0).isFalse()).isFalse(); } @Test public void testFalse_NotificationDismiss() { - when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DISMISS); when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect. when(mDataProvider.isRight()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DISMISS, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DISMISS, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isUp()).thenReturn(false); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DISMISS, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(NOTIFICATION_DISMISS, 0.5, 0).isFalse()).isTrue(); } @Test public void testPass_Unlock() { - when(mDataProvider.getInteractionType()).thenReturn(UNLOCK); when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(false); // right should cause no effect. - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(UNLOCK, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(UNLOCK, 0.5, 0).isFalse()).isFalse(); } @Test public void testFalse_Unlock() { - when(mDataProvider.getInteractionType()).thenReturn(UNLOCK); - when(mDataProvider.isVertical()).thenReturn(false); when(mDataProvider.isUp()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(UNLOCK, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(UNLOCK, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isVertical()).thenReturn(false); when(mDataProvider.isUp()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(UNLOCK, 0.5, 0).isFalse()).isTrue(); } @Test public void testPass_BouncerUnlock() { - when(mDataProvider.getInteractionType()).thenReturn(BOUNCER_UNLOCK); when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(false); // right should cause no effect. - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(BOUNCER_UNLOCK, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(BOUNCER_UNLOCK, 0.5, 0).isFalse()).isFalse(); } @Test public void testFalse_BouncerUnlock() { - when(mDataProvider.getInteractionType()).thenReturn(BOUNCER_UNLOCK); - when(mDataProvider.isVertical()).thenReturn(false); when(mDataProvider.isUp()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(BOUNCER_UNLOCK, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isVertical()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(BOUNCER_UNLOCK, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isVertical()).thenReturn(false); when(mDataProvider.isUp()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(BOUNCER_UNLOCK, 0.5, 0).isFalse()).isTrue(); } @Test public void testPass_LeftAffordance() { - when(mDataProvider.getInteractionType()).thenReturn(LEFT_AFFORDANCE); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(true); when(mDataProvider.isVertical()).thenReturn(false); // vertical should cause no effect. - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(LEFT_AFFORDANCE, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isVertical()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(LEFT_AFFORDANCE, 0.5, 0).isFalse()).isFalse(); } @Test public void testFalse_LeftAffordance() { - when(mDataProvider.getInteractionType()).thenReturn(LEFT_AFFORDANCE); - when(mDataProvider.isRight()).thenReturn(false); when(mDataProvider.isUp()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(LEFT_AFFORDANCE, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isRight()).thenReturn(true); when(mDataProvider.isUp()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(LEFT_AFFORDANCE, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isRight()).thenReturn(false); when(mDataProvider.isUp()).thenReturn(false); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(LEFT_AFFORDANCE, 0.5, 0).isFalse()).isTrue(); } @Test public void testPass_RightAffordance() { - when(mDataProvider.getInteractionType()).thenReturn(RIGHT_AFFORDANCE); when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(false); when(mDataProvider.isVertical()).thenReturn(false); // vertical should cause no effect. - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(RIGHT_AFFORDANCE, 0.5, 0).isFalse()).isFalse(); when(mDataProvider.isVertical()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(RIGHT_AFFORDANCE, 0.5, 0).isFalse()).isFalse(); } @Test public void testFalse_RightAffordance() { - when(mDataProvider.getInteractionType()).thenReturn(RIGHT_AFFORDANCE); - when(mDataProvider.isUp()).thenReturn(true); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(RIGHT_AFFORDANCE, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isUp()).thenReturn(false); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(RIGHT_AFFORDANCE, 0.5, 0).isFalse()).isTrue(); when(mDataProvider.isUp()).thenReturn(false); when(mDataProvider.isRight()).thenReturn(true); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(RIGHT_AFFORDANCE, 0.5, 0).isFalse()).isTrue(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java index 09bee128c673..e004c30eae89 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java @@ -16,8 +16,7 @@ package com.android.systemui.classifier; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; import android.testing.AndroidTestingRunner; @@ -51,11 +50,11 @@ public class ZigZagClassifierTest extends ClassifierTest { @Test public void testPass_fewTouchesVertical() { - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); appendMoveEvent(0, 0); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); appendMoveEvent(0, 100); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); } @Test @@ -63,16 +62,16 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(0, 100); appendMoveEvent(0, 200); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); } @Test public void testPass_fewTouchesHorizontal() { - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); appendMoveEvent(0, 0); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); appendMoveEvent(100, 0); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); } @Test @@ -80,7 +79,7 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(100, 0); appendMoveEvent(200, 0); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); } @@ -89,7 +88,7 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(0, 100); appendMoveEvent(0, 1); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -97,7 +96,7 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(100, 0); appendMoveEvent(1, 0); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -105,7 +104,7 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(10, 10); appendMoveEvent(20, 20); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); } @Test @@ -115,7 +114,7 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(5, 100); appendMoveEvent(-5, 200); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); } @Test @@ -125,7 +124,7 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(100, 5); appendMoveEvent(200, -5); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); } @Test @@ -135,7 +134,7 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(6, 10); appendMoveEvent(-6, 20); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -145,7 +144,7 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(10, 5); appendMoveEvent(20, -5); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -153,25 +152,25 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(100, 5); appendMoveEvent(200, 10); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(100, 0); appendMoveEvent(200, 10); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(100, -10); appendMoveEvent(200, 10); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(100, -10); appendMoveEvent(200, 50); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -179,25 +178,25 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(10, 50); appendMoveEvent(8, 100); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(1, 800); appendMoveEvent(2, 900); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-10, 600); appendMoveEvent(30, 700); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(40, 100); appendMoveEvent(0, 101); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -205,25 +204,25 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(-10, 50); appendMoveEvent(-24, 100); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-20, 800); appendMoveEvent(-20, 900); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(30, 600); appendMoveEvent(-10, 700); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-80, 100); appendMoveEvent(-10, 101); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -231,25 +230,25 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(-120, 10); appendMoveEvent(-200, 20); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-20, 8); appendMoveEvent(-40, 2); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-500, -2); appendMoveEvent(-600, 70); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-80, 100); appendMoveEvent(-100, 1); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -257,25 +256,25 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(-120, -10); appendMoveEvent(-200, -20); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-20, -8); appendMoveEvent(-40, -2); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-500, 2); appendMoveEvent(-600, -70); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-80, -100); appendMoveEvent(-100, -1); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -283,25 +282,25 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(-12, -20); appendMoveEvent(-20, -40); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-20, -130); appendMoveEvent(-40, -260); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(1, -100); appendMoveEvent(-6, -200); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-80, -100); appendMoveEvent(-10, -110); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -309,25 +308,25 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(12, -20); appendMoveEvent(20, -40); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(20, -130); appendMoveEvent(40, -260); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(-1, -100); appendMoveEvent(6, -200); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(80, -100); appendMoveEvent(10, -110); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test @@ -335,25 +334,25 @@ public class ZigZagClassifierTest extends ClassifierTest { appendMoveEvent(0, 0); appendMoveEvent(120, -20); appendMoveEvent(200, -40); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(200, -13); appendMoveEvent(400, -30); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(100, 10); appendMoveEvent(600, -20); - assertThat(mClassifier.classifyGesture().isFalse(), is(false)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse(); resetDataProvider(); appendMoveEvent(0, 0); appendMoveEvent(80, -100); appendMoveEvent(100, -1); - assertThat(mClassifier.classifyGesture().isFalse(), is(true)); + assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java index b3ad6ef8da6e..24a63e7a2c73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java @@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.shared.system.PeopleProviderUtils; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import junit.framework.Assert; @@ -73,6 +74,8 @@ public class PeopleProviderTest extends SysuiTestCase { private PackageManager mPackageManager; @Mock private IPeopleManager mPeopleManager; + @Mock + private NotificationEntryManager mNotificationEntryManager; @Before public void setUp() throws Exception { @@ -84,6 +87,7 @@ public class PeopleProviderTest extends SysuiTestCase { mContext, PeopleProviderUtils.PEOPLE_PROVIDER_AUTHORITY); provider.setLauncherApps(mLauncherApps); provider.setPeopleManager(mPeopleManager); + provider.setNotificationEntryManager(mNotificationEntryManager); mContext.getContentResolver().addProvider( PeopleProviderUtils.PEOPLE_PROVIDER_AUTHORITY, provider); diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTestable.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTestable.java index ac1893413c50..6834fa54a084 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTestable.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTestable.java @@ -21,6 +21,8 @@ import android.content.Context; import android.content.pm.LauncherApps; import android.content.pm.ProviderInfo; +import com.android.systemui.statusbar.notification.NotificationEntryManager; + public class PeopleProviderTestable extends PeopleProvider { public void initializeForTesting(Context context, String authority) { @@ -37,4 +39,8 @@ public class PeopleProviderTestable extends PeopleProvider { void setPeopleManager(IPeopleManager peopleManager) { mPeopleManager = peopleManager; } + + void setNotificationEntryManager(NotificationEntryManager notificationEntryManager) { + mNotificationEntryManager = notificationEntryManager; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index 410a809bd394..9185cd6426f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -25,7 +25,6 @@ import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH; import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; import static com.android.systemui.people.PeopleSpaceUtils.REQUIRED_WIDTH_FOR_MEDIUM; -import static com.android.systemui.people.PeopleSpaceUtils.getPeopleTileFromPersistentStorage; import static com.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE; import static com.google.common.truth.Truth.assertThat; @@ -71,12 +70,13 @@ import android.provider.ContactsContract; import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; import android.service.notification.StatusBarNotification; -import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.view.View; import android.widget.RemoteViews; import android.widget.TextView; +import androidx.test.filters.SmallTest; + import com.android.internal.appwidget.IAppWidgetService; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; @@ -98,8 +98,8 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -@SmallTest @RunWith(AndroidTestingRunner.class) +@SmallTest public class PeopleSpaceUtilsTest extends SysuiTestCase { private static final int WIDGET_ID_WITH_SHORTCUT = 1; @@ -578,6 +578,25 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { } @Test + public void testAugmentSingleTileFromVisibleNotificationsSingleTile() { + PeopleSpaceTile tile = + new PeopleSpaceTile + .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) + .setPackageName(PACKAGE_NAME) + .setUserHandle(new UserHandle(0)) + .build(); + PeopleSpaceTile augmentedTile = PeopleSpaceUtils + .augmentSingleTileFromVisibleNotifications( + mContext, tile, mNotificationEntryManager); + + assertThat(augmentedTile).isNotNull(); + assertThat(augmentedTile.getNotificationContent().toString()) + .isEqualTo(NOTIFICATION_TEXT_2); + + verify(mNotificationEntryManager, times(1)).getVisibleNotifications(); + } + + @Test public void testDoNotUpdateSingleConversationAppWidgetWhenNotBirthday() { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT}; when(mMockCursor.moveToNext()).thenReturn(true, false); @@ -890,23 +909,6 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { smallResult.findViewById(R.id.person_icon).getVisibility()); } - @Test - public void testGetPeopleTileFromPersistentStorageExistingConversation() - throws Exception { - when(mPeopleManager.getConversation(PACKAGE_NAME, 0, SHORTCUT_ID_1)).thenReturn( - getConversationChannelWithoutTimestamp(SHORTCUT_ID_1)); - PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID_1, 0, PACKAGE_NAME); - PeopleSpaceTile tile = getPeopleTileFromPersistentStorage(mContext, key, mPeopleManager); - assertThat(tile.getId()).isEqualTo(key.getShortcutId()); - } - - @Test - public void testGetPeopleTileFromPersistentStorageNoConversation() { - PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID_2, 0, PACKAGE_NAME); - PeopleSpaceTile tile = getPeopleTileFromPersistentStorage(mContext, key, mPeopleManager); - assertThat(tile).isNull(); - } - private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId, boolean importantConversation, long lastInteractionTimestamp) throws Exception { ConversationChannelWrapper convo = new ConversationChannelWrapper(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java index fce02174a1e0..0ef3ca29bdf6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java @@ -20,11 +20,14 @@ 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.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.Intent; +import android.os.Bundle; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.testing.AndroidTestingRunner; @@ -37,6 +40,7 @@ import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.wmshell.BubblesManager; import org.junit.Before; import org.junit.Test; @@ -46,6 +50,8 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Optional; + @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper @@ -55,7 +61,7 @@ public class LaunchConversationActivityTest extends SysuiTestCase { private static final String NOTIF_KEY = "notifKey"; private static final String NOTIF_KEY_NO_ENTRY = "notifKeyNoEntry"; private static final String NOTIF_KEY_NO_RANKING = "notifKeyNoRanking"; - + private static final String NOTIF_KEY_CAN_BUBBLE = "notifKeyCanBubble"; private static final UserHandle USER_HANDLE = UserHandle.of(0); private static final int NOTIF_COUNT = 10; @@ -72,16 +78,27 @@ public class LaunchConversationActivityTest extends SysuiTestCase { @Mock private NotificationEntry mNotifEntryNoRanking; @Mock + private NotificationEntry mNotifEntryCanBubble; + @Mock + private BubblesManager mBubblesManager; + @Mock private NotificationListenerService.Ranking mRanking; @Captor private ArgumentCaptor<NotificationVisibility> mNotificationVisibilityCaptor; + private Intent mIntent; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mActivity = new LaunchConversationActivity(mNotificationEntryManager); + mActivity = new LaunchConversationActivity(mNotificationEntryManager, + Optional.of(mBubblesManager)); + mActivity.setIsForTesting(true, mIStatusBarService); + mIntent = new Intent(); + mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, "tile ID"); + mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, PACKAGE_NAME); + mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE, USER_HANDLE); when(mNotificationEntryManager.getActiveNotificationsCount()).thenReturn(NOTIF_COUNT); when(mNotificationEntryManager.getPendingOrActiveNotif(NOTIF_KEY)).thenReturn(mNotifEntry); @@ -89,15 +106,21 @@ public class LaunchConversationActivityTest extends SysuiTestCase { .thenReturn(null); when(mNotificationEntryManager.getPendingOrActiveNotif(NOTIF_KEY_NO_RANKING)) .thenReturn(mNotifEntryNoRanking); + when(mNotificationEntryManager.getPendingOrActiveNotif(NOTIF_KEY_CAN_BUBBLE)) + .thenReturn(mNotifEntryCanBubble); when(mNotifEntry.getRanking()).thenReturn(mRanking); + when(mNotifEntryCanBubble.getRanking()).thenReturn(mRanking); + when(mNotifEntryCanBubble.canBubble()).thenReturn(true); when(mNotifEntryNoRanking.getRanking()).thenReturn(null); when(mRanking.getRank()).thenReturn(NOTIF_RANK); } @Test public void testDoNotClearNotificationIfNoKey() throws Exception { - mActivity.clearNotificationIfPresent(mIStatusBarService, - EMPTY_STRING, PACKAGE_NAME, USER_HANDLE); + mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, + EMPTY_STRING); + mActivity.setIntent(mIntent); + mActivity.onCreate(new Bundle()); verify(mIStatusBarService, never()).onNotificationClear( any(), anyInt(), any(), anyInt(), anyInt(), any()); @@ -105,8 +128,10 @@ public class LaunchConversationActivityTest extends SysuiTestCase { @Test public void testDoNotClearNotificationIfNoNotificationEntry() throws Exception { - mActivity.clearNotificationIfPresent(mIStatusBarService, - NOTIF_KEY_NO_ENTRY, PACKAGE_NAME, USER_HANDLE); + mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, + NOTIF_KEY_NO_ENTRY); + mActivity.setIntent(mIntent); + mActivity.onCreate(new Bundle()); verify(mIStatusBarService, never()).onNotificationClear( any(), anyInt(), any(), anyInt(), anyInt(), any()); @@ -114,24 +139,41 @@ public class LaunchConversationActivityTest extends SysuiTestCase { @Test public void testDoNotClearNotificationIfNoRanking() throws Exception { - mActivity.clearNotificationIfPresent(mIStatusBarService, - NOTIF_KEY_NO_RANKING, PACKAGE_NAME, USER_HANDLE); + mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, + NOTIF_KEY_NO_RANKING); + mActivity.setIntent(mIntent); + mActivity.onCreate(new Bundle()); verify(mIStatusBarService, never()).onNotificationClear( any(), anyInt(), any(), anyInt(), anyInt(), any()); } @Test - public void testClearNotification() throws Exception { - mActivity.clearNotificationIfPresent(mIStatusBarService, - NOTIF_KEY, PACKAGE_NAME, USER_HANDLE); + public void testEntryClearsNotificationAndDoesNotOpenBubble() throws Exception { + mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, + NOTIF_KEY); + mActivity.setIntent(mIntent); + mActivity.onCreate(new Bundle()); verify(mIStatusBarService, times(1)).onNotificationClear(any(), anyInt(), any(), anyInt(), anyInt(), mNotificationVisibilityCaptor.capture()); + verify(mBubblesManager, never()).expandStackAndSelectBubble(any()); NotificationVisibility nv = mNotificationVisibilityCaptor.getValue(); assertThat(nv.count).isEqualTo(NOTIF_COUNT); assertThat(nv.rank).isEqualTo(NOTIF_RANK); } + @Test + public void testBubbleEntryOpensBubbleAndDoesNotClearNotification() throws Exception { + mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, + NOTIF_KEY_CAN_BUBBLE); + mActivity.setIntent(mIntent); + mActivity.onCreate(new Bundle()); + + // Don't clear the notification for bubbles. + verify(mIStatusBarService, never()).onNotificationClear(any(), + anyInt(), any(), anyInt(), anyInt(), any()); + verify(mBubblesManager, times(1)).expandStackAndSelectBubble(eq(mNotifEntryCanBubble)); + } } 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 096e51b56263..5834aef4ba78 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 @@ -73,6 +73,7 @@ import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; import com.android.systemui.statusbar.SbnBuilder; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NoManSimulator; import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -134,6 +135,9 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private PeopleSpaceWidgetManager mManager; @Mock + private Context mMockContext; + + @Mock private NotificationListener mListenerService; @Mock @@ -144,6 +148,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private PeopleManager mPeopleManager; @Mock private LauncherApps mLauncherApps; + @Mock + private NotificationEntryManager mNotificationEntryManager; @Captor private ArgumentCaptor<NotificationHandler> mListenerCaptor; @@ -159,10 +165,10 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mLauncherApps = mock(LauncherApps.class); - mManager = - new PeopleSpaceWidgetManager(mContext); + mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager); + mManager = new PeopleSpaceWidgetManager(mContext); mManager.setAppWidgetManager(mAppWidgetManager, mIPeopleManager, mPeopleManager, - mLauncherApps); + mLauncherApps, mNotificationEntryManager); mManager.attach(mListenerService); mProvider = new PeopleSpaceWidgetProvider(); mProvider.setPeopleSpaceWidgetManager(mManager); @@ -668,17 +674,22 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions); - verify(mAppWidgetManager, times(1)).updateAppWidgetOptions( + verify(mAppWidgetManager, times(2)).updateAppWidgetOptions( eq(SECOND_WIDGET_ID_WITH_SHORTCUT), mBundleArgumentCaptor.capture()); - Bundle bundle = mBundleArgumentCaptor.getValue(); - assertThat(bundle.getString(PeopleSpaceUtils.SHORTCUT_ID, EMPTY_STRING)) + List<Bundle> bundles = mBundleArgumentCaptor.getAllValues(); + Bundle first = bundles.get(0); + assertThat(first.getString(PeopleSpaceUtils.SHORTCUT_ID, EMPTY_STRING)) .isEqualTo(EMPTY_STRING); - assertThat(bundle.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID); - assertThat(bundle.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(EMPTY_STRING); + assertThat(first.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID); + assertThat(first.getString(PACKAGE_NAME, EMPTY_STRING)).isEqualTo(EMPTY_STRING); verify(mLauncherApps, times(1)).cacheShortcuts(eq(TEST_PACKAGE_A), eq(Arrays.asList(SHORTCUT_ID)), eq(UserHandle.of(0)), eq(LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS)); + Bundle second = bundles.get(1); + PeopleSpaceTile tile = second.getParcelable(OPTIONS_PEOPLE_TILE); + assertThat(tile.getId()).isEqualTo(SHORTCUT_ID); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); assertThat(sp.getStringSet(KEY.toString(), new HashSet<>())).contains( String.valueOf(SECOND_WIDGET_ID_WITH_SHORTCUT)); @@ -691,6 +702,52 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { assertThat(widgetSp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(0); } + @Test + public void testGetPeopleTileFromPersistentStorageExistingConversation() + throws Exception { + when(mIPeopleManager.getConversation(PACKAGE_NAME, 0, SHORTCUT_ID)).thenReturn( + getConversationWithShortcutId(SHORTCUT_ID)); + PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, PACKAGE_NAME); + PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key); + assertThat(tile.getId()).isEqualTo(key.getShortcutId()); + } + + @Test + public void testGetPeopleTileFromPersistentStorageNoConversation() { + PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, PACKAGE_NAME); + PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key); + assertThat(tile).isNull(); + } + + @Test + public void testRequestPinAppWidgetExistingConversation() throws Exception { + when(mMockContext.getPackageName()).thenReturn(PACKAGE_NAME); + when(mMockContext.getUserId()).thenReturn(0); + when(mIPeopleManager.getConversation(PACKAGE_NAME, 0, SHORTCUT_ID)) + .thenReturn(getConversationWithShortcutId(SHORTCUT_ID)); + when(mAppWidgetManager.requestPinAppWidget(any(), any(), any())).thenReturn(true); + + ShortcutInfo info = new ShortcutInfo.Builder(mMockContext, SHORTCUT_ID).build(); + boolean valid = mManager.requestPinAppWidget(info); + + assertThat(valid).isTrue(); + verify(mAppWidgetManager, times(1)).requestPinAppWidget( + any(), any(), any()); + } + + @Test + public void testRequestPinAppWidgetNoConversation() throws Exception { + when(mMockContext.getPackageName()).thenReturn(PACKAGE_NAME); + when(mMockContext.getUserId()).thenReturn(0); + when(mIPeopleManager.getConversation(PACKAGE_NAME, 0, SHORTCUT_ID)).thenReturn(null); + + ShortcutInfo info = new ShortcutInfo.Builder(mMockContext, SHORTCUT_ID).build(); + boolean valid = mManager.requestPinAppWidget(info); + + assertThat(valid).isFalse(); + verify(mAppWidgetManager, never()).requestPinAppWidget(any(), any(), any()); + } + /** * Adds another widget for {@code PERSON_TILE} with widget ID: {@code * SECOND_WIDGET_ID_WITH_SHORTCUT}. diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index fb817eac3e10..a2113b94740b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -242,10 +242,18 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { when(mMediaHost.getVisible()).thenReturn(true); when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false); + mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost, + mQSCustomizerController, mMediaHost, + mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags); + assertThat(mController.shouldUseHorizontalLayout()).isTrue(); when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); + mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost, + mQSCustomizerController, mMediaHost, + mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags); + assertThat(mController.shouldUseHorizontalLayout()).isFalse(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java new file mode 100644 index 000000000000..33166ccadc27 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -0,0 +1,207 @@ +/* + * 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.qs.tiles; + +import static android.content.pm.PackageManager.FEATURE_NFC_HOST_CARD_EMULATION; +import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNull; +import static junit.framework.TestCase.assertTrue; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.service.quickaccesswallet.QuickAccessWalletClient; +import android.service.quicksettings.Tile; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.testing.UiEventLoggerFake; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSTileHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.SecureSettings; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +public class QuickAccessWalletTileTest extends SysuiTestCase { + + @Mock + private QSTileHost mHost; + @Mock + private MetricsLogger mMetricsLogger; + @Mock + private StatusBarStateController mStatusBarStateController; + @Mock + private ActivityStarter mActivityStarter; + @Mock + private QSLogger mQSLogger; + private UiEventLogger mUiEventLogger = new UiEventLoggerFake(); + @Mock + private QuickAccessWalletClient mQuickAccessWalletClient; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private PackageManager mPackageManager; + @Mock + private SecureSettings mSecureSettings; + @Mock + private FeatureFlags mFeatureFlags; + + private TestableLooper mTestableLooper; + private QuickAccessWalletTile mTile; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mTestableLooper = TestableLooper.get(this); + + when(mHost.getContext()).thenReturn(mContext); + when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger); + when(mFeatureFlags.isQuickAccessWalletEnabled()).thenReturn(true); + + mTile = new QuickAccessWalletTile( + mHost, + mTestableLooper.getLooper(), + new Handler(mTestableLooper.getLooper()), + new FalsingManagerFake(), + mMetricsLogger, + mStatusBarStateController, + mActivityStarter, + mQSLogger, + mQuickAccessWalletClient, + mKeyguardStateController, + mPackageManager, + mSecureSettings, + mFeatureFlags); + } + + @Test + public void testNewTile() { + assertFalse(mTile.newTileState().handlesLongClick); + } + + @Test + public void testIsAvailable_featureFlagIsOff() { + when(mFeatureFlags.isQuickAccessWalletEnabled()).thenReturn(false); + assertFalse(mTile.isAvailable()); + } + + @Test + public void testIsAvailable_qawServiceNotAvailable() { + when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false); + assertFalse(mTile.isAvailable()); + } + + @Test + public void testIsAvailable_qawServiceAvailable() { + when(mPackageManager.hasSystemFeature(FEATURE_NFC_HOST_CARD_EMULATION)).thenReturn(true); + when(mPackageManager.hasSystemFeature("org.chromium.arc")).thenReturn(false); + when(mSecureSettings.getString(NFC_PAYMENT_DEFAULT_COMPONENT)).thenReturn("Component"); + + assertTrue(mTile.isAvailable()); + } + + @Test + public void testHandleClick_openGPay() { + Intent intent = new Intent("WalletIntent"); + when(mQuickAccessWalletClient.createWalletIntent()).thenReturn(intent); + mTile.handleClick(); + + verify(mActivityStarter, times(1)) + .postStartActivityDismissingKeyguard(eq(intent), anyInt()); + } + + @Test + public void testHandleUpdateState_updateLabelAndIcon() { + QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_wallet); + QSTile.State state = new QSTile.State(); + when(mQuickAccessWalletClient.getServiceLabel()).thenReturn("QuickAccessWallet"); + + mTile.handleUpdateState(state, new Object()); + + assertEquals("QuickAccessWallet", state.label.toString()); + assertTrue(state.label.toString().contentEquals(state.contentDescription)); + assertEquals(icon, state.icon); + } + + @Test + public void testHandleUpdateState_deviceLocked_tileInactive() { + QSTile.State state = new QSTile.State(); + when(mKeyguardStateController.isUnlocked()).thenReturn(false); + when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true); + + mTile.handleUpdateState(state, new Object()); + + assertEquals(Tile.STATE_INACTIVE, state.state); + assertNull(state.stateDescription); + } + + @Test + public void testHandleUpdateState_deviceLocked_tileActive() { + QSTile.State state = new QSTile.State(); + when(mKeyguardStateController.isUnlocked()).thenReturn(true); + when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true); + + mTile.handleUpdateState(state, new Object()); + + assertEquals(Tile.STATE_ACTIVE, state.state); + assertTrue(state.secondaryLabel.toString().contentEquals(state.stateDescription)); + assertEquals( + getContext().getString(R.string.wallet_secondary_label), + state.secondaryLabel.toString()); + } + + @Test + public void testHandleUpdateState_qawFeatureUnavailable_tileUnavailable() { + QSTile.State state = new QSTile.State(); + when(mKeyguardStateController.isUnlocked()).thenReturn(true); + when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false); + + mTile.handleUpdateState(state, new Object()); + + assertEquals(Tile.STATE_UNAVAILABLE, state.state); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java new file mode 100644 index 000000000000..410d9de9e0ac --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.screenshot; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.graphics.Rect; +import android.hardware.HardwareBuffer; +import android.media.Image; +import android.testing.AndroidTestingRunner; +import android.view.ScrollCaptureResponse; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.systemui.SysuiTestCase; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.ExecutionException; + +/** + * Tests for ScrollCaptureController which manages sequential image acquisition for long + * screenshots. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class ScrollCaptureControllerTest extends SysuiTestCase { + + private static class FakeSession implements ScrollCaptureClient.Session { + public int availableTop = Integer.MIN_VALUE; + public int availableBottom = Integer.MAX_VALUE; + // If true, return an empty rect any time a partial result would have been returned. + public boolean emptyInsteadOfPartial = false; + + @Override + public ListenableFuture<ScrollCaptureClient.CaptureResult> requestTile(int top) { + Rect requested = new Rect(0, top, getPageWidth(), top + getTileHeight()); + Rect fullContent = new Rect(0, availableTop, getPageWidth(), availableBottom); + Rect captured = new Rect(requested); + captured.intersect(fullContent); + if (emptyInsteadOfPartial && captured.height() != getTileHeight()) { + captured = new Rect(); + } + Image image = mock(Image.class); + when(image.getHardwareBuffer()).thenReturn(mock(HardwareBuffer.class)); + ScrollCaptureClient.CaptureResult result = + new ScrollCaptureClient.CaptureResult(image, requested, captured); + return Futures.immediateFuture(result); + } + + public int getMaxHeight() { + return getTileHeight() * getMaxTiles(); + } + + @Override + public int getMaxTiles() { + return 10; + } + + @Override + public int getTileHeight() { + return 50; + } + + @Override + public int getPageHeight() { + return 100; + } + + @Override + public int getPageWidth() { + return 100; + } + + @Override + public Rect getWindowBounds() { + return null; + } + + @Override + public ListenableFuture<Void> end() { + return Futures.immediateVoidFuture(); + } + + @Override + public void release() { + } + } + + private ScrollCaptureController mController; + private FakeSession mSession; + private ScrollCaptureClient mScrollCaptureClient; + + @Before + public void setUp() { + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + mSession = new FakeSession(); + mScrollCaptureClient = mock(ScrollCaptureClient.class); + when(mScrollCaptureClient.request(anyInt(), anyInt())).thenReturn( + Futures.immediateFuture(new ScrollCaptureResponse.Builder().build())); + when(mScrollCaptureClient.start(any(), anyFloat())).thenReturn( + Futures.immediateFuture(mSession)); + mController = new ScrollCaptureController(context, context.getMainExecutor(), + mScrollCaptureClient, new ImageTileSet(context.getMainThreadHandler())); + } + + @Test + public void testInfinite() throws ExecutionException, InterruptedException { + ScrollCaptureController.LongScreenshot screenshot = + mController.run(new ScrollCaptureResponse.Builder().build()).get(); + assertEquals(mSession.getMaxHeight(), screenshot.getHeight()); + // TODO: the top and bottom ratio in the infinite case should be extracted and tested. + assertEquals(-150, screenshot.getTop()); + assertEquals(350, screenshot.getBottom()); + } + + @Test + public void testLimitedBottom() throws ExecutionException, InterruptedException { + // We hit the bottom of the content, so expect it to scroll back up and go above the -150 + // default top position + mSession.availableBottom = 275; + ScrollCaptureController.LongScreenshot screenshot = + mController.run(new ScrollCaptureResponse.Builder().build()).get(); + // Bottom tile will be 25px tall, 10 tiles total + assertEquals(mSession.getMaxHeight() - 25, screenshot.getHeight()); + assertEquals(-200, screenshot.getTop()); + assertEquals(mSession.availableBottom, screenshot.getBottom()); + } + + @Test + public void testLimitedTopAndBottom() throws ExecutionException, InterruptedException { + mSession.availableBottom = 275; + mSession.availableTop = -200; + ScrollCaptureController.LongScreenshot screenshot = + mController.run(new ScrollCaptureResponse.Builder().build()).get(); + assertEquals(mSession.availableBottom - mSession.availableTop, screenshot.getHeight()); + assertEquals(mSession.availableTop, screenshot.getTop()); + assertEquals(mSession.availableBottom, screenshot.getBottom()); + } + + @Test + public void testVeryLimitedTopInfiniteBottom() throws ExecutionException, InterruptedException { + // Hit the boundary before the "headroom" is hit in the up direction, then go down + // infinitely. + mSession.availableTop = -55; + ScrollCaptureController.LongScreenshot screenshot = + mController.run(new ScrollCaptureResponse.Builder().build()).get(); + // The top tile will be 5px tall, so subtract 45px from the theoretical max. + assertEquals(mSession.getMaxHeight() - 45, screenshot.getHeight()); + assertEquals(mSession.availableTop, screenshot.getTop()); + assertEquals(mSession.availableTop + mSession.getMaxHeight() - 45, screenshot.getBottom()); + } + + @Test + public void testVeryLimitedTopLimitedBottom() throws ExecutionException, InterruptedException { + mSession.availableBottom = 275; + mSession.availableTop = -55; + ScrollCaptureController.LongScreenshot screenshot = + mController.run(new ScrollCaptureResponse.Builder().build()).get(); + assertEquals(mSession.availableBottom - mSession.availableTop, screenshot.getHeight()); + assertEquals(mSession.availableTop, screenshot.getTop()); + assertEquals(mSession.availableBottom, screenshot.getBottom()); + } + + @Test + public void testLimitedTopAndBottomWithEmpty() throws ExecutionException, InterruptedException { + mSession.emptyInsteadOfPartial = true; + mSession.availableBottom = 275; + mSession.availableTop = -167; + ScrollCaptureController.LongScreenshot screenshot = + mController.run(new ScrollCaptureResponse.Builder().build()).get(); + // Expecting output from -150 to 250 + assertEquals(400, screenshot.getHeight()); + assertEquals(-150, screenshot.getTop()); + assertEquals(250, screenshot.getBottom()); + } + + @Test + public void testVeryLimitedTopWithEmpty() throws ExecutionException, InterruptedException { + // Hit the boundary before the "headroom" is hit in the up direction, then go down + // infinitely. + mSession.availableTop = -55; + mSession.emptyInsteadOfPartial = true; + ScrollCaptureController.LongScreenshot screenshot = + mController.run(new ScrollCaptureResponse.Builder().build()).get(); + assertEquals(mSession.getMaxHeight(), screenshot.getHeight()); + assertEquals(-50, screenshot.getTop()); + assertEquals(-50 + mSession.getMaxHeight(), screenshot.getBottom()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index 8ec03d76cfea..58738e734a06 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -29,6 +29,7 @@ import android.content.ComponentName; import android.graphics.Rect; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; +import android.hardware.fingerprint.IUdfpsHbmListener; import android.os.Bundle; import android.view.WindowInsetsController.Appearance; import android.view.WindowInsetsController.Behavior; @@ -464,6 +465,14 @@ public class CommandQueueTest extends SysuiTestCase { } @Test + public void testSetUdfpsHbmListener() { + final IUdfpsHbmListener listener = mock(IUdfpsHbmListener.class); + mCommandQueue.setUdfpsHbmListener(listener); + waitForIdleSync(); + verify(mCallbacks).setUdfpsHbmListener(eq(listener)); + } + + @Test public void testSuppressAmbientDisplay() { mCommandQueue.suppressAmbientDisplay(true); waitForIdleSync(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 5c37656d2cf1..d4b21c6f6949 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -76,6 +76,7 @@ import com.android.settingslib.notification.ConversationIconFactory; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -141,6 +142,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Mock private LauncherApps mLauncherApps; @Mock + private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; + @Mock private ShortcutManager mShortcutManager; @Mock private NotificationGuts mNotificationGuts; @@ -236,6 +239,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { .thenReturn(mock(NotificationManager.Policy.class)); when(mBuilder.build()).thenReturn(mock(PriorityOnboardingDialogController.class)); + + when(mPeopleSpaceWidgetManager.requestPinAppWidget(any())).thenReturn(true); } @Test @@ -244,6 +249,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -257,7 +263,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon); assertEquals(mIconDrawable, view.getDrawable()); } @@ -269,6 +276,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -282,7 +290,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -322,6 +331,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -335,7 +345,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); assertTrue(textView.getText().toString().contains(group.getName())); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -348,6 +359,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -361,7 +373,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); assertEquals(GONE, textView.getVisibility()); @@ -373,6 +386,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -386,7 +400,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(GONE, nameView.getVisibility()); } @@ -409,6 +424,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -422,7 +438,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(VISIBLE, nameView.getVisibility()); assertTrue(nameView.getText().toString().contains("Proxied")); @@ -435,6 +452,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -451,7 +469,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -465,6 +484,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -478,7 +498,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -490,6 +511,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -506,7 +528,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, false, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -519,6 +542,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -532,7 +556,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); View view = mNotificationInfo.findViewById(R.id.silence); assertThat(view.isSelected()).isTrue(); } @@ -548,6 +573,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -561,7 +587,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); View view = mNotificationInfo.findViewById(R.id.default_behavior); assertThat(view.isSelected()).isTrue(); assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo( @@ -580,6 +607,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -593,7 +621,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); View view = mNotificationInfo.findViewById(R.id.default_behavior); assertThat(view.isSelected()).isTrue(); assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo( @@ -611,6 +640,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -624,7 +654,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -656,6 +687,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -669,7 +701,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mTestableLooper.processAllMessages(); @@ -700,6 +733,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -713,7 +747,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); View silence = mNotificationInfo.findViewById(R.id.silence); @@ -745,6 +780,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -758,7 +794,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -783,6 +820,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -796,7 +834,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -820,6 +859,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -833,7 +873,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -861,6 +902,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, // no action selected by default mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -874,7 +916,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); // THEN the selected action is -1, so the selected option is "Default" priority assertEquals(mNotificationInfo.getSelectedAction(), -1); @@ -892,6 +935,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { NotificationConversationInfo.ACTION_FAVORITE, // "Favorite" selected by default mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -905,7 +949,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); // THEN the selected action is "Favorite", so the selected option is "priority" priority assertEquals(mNotificationInfo.getSelectedAction(), @@ -922,6 +967,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -935,7 +981,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -959,6 +1006,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -972,7 +1020,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -996,6 +1045,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -1009,7 +1059,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -1032,6 +1083,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -1045,7 +1097,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); View silence = mNotificationInfo.findViewById(R.id.silence); silence.performClick(); @@ -1067,6 +1120,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -1080,7 +1134,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage( anyString(), anyInt(), any(), eq(CONVERSATION_ID)); @@ -1093,6 +1148,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -1106,7 +1162,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage( anyString(), anyInt(), any(), eq(CONVERSATION_ID)); @@ -1115,7 +1172,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Test public void testSelectPriorityPresentsOnboarding_firstTime() { // GIVEN pref is false - Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, false); + Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, false); // GIVEN the priority onboarding screen is present PriorityOnboardingDialogController.Builder b = @@ -1129,6 +1186,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -1142,7 +1200,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { () -> b, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); // WHEN user clicks "priority" mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); @@ -1158,7 +1217,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Test public void testSelectPriorityDoesNotShowOnboarding_secondTime() { //WHEN pref is true - Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, true); + Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, true); PriorityOnboardingDialogController.Builder b = mock(PriorityOnboardingDialogController.Builder.class, Answers.RETURNS_SELF); @@ -1170,6 +1229,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, @@ -1183,12 +1243,128 @@ public class NotificationConversationInfoTest extends SysuiTestCase { () -> b, true, mTestHandler, - mTestHandler, null, Optional.of(mBubblesManager)); + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); // WHEN user clicks "priority" mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); + verify(controller, never()).show(); + + // and then done + mNotificationInfo.findViewById(R.id.done).performClick(); // THEN the user is presented with the priority onboarding screen verify(controller, never()).show(); } + + @Test + public void testSelectPriorityRequestsPinPeopleTile() { + //WHEN pref is true and channel is default importance + Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, true); + mNotificationChannel.setImportantConversation(false); + mNotificationInfo.bindNotification( + -1, + mShortcutManager, + mMockPackageManager, + mPeopleSpaceWidgetManager, + mMockINotificationManager, + mOnUserInteractionCallback, + TEST_PACKAGE_NAME, + mNotificationChannel, + mEntry, + mBubbleMetadata, + null, + null, + mIconFactory, + mContext, + mBuilderProvider, + true, + mTestHandler, + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); + + // WHEN user clicks "priority" + mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); + + // and then done + mNotificationInfo.findViewById(R.id.done).performClick(); + + // THEN the user is presented with the People Tile pinning request + verify(mPeopleSpaceWidgetManager, times(1)).requestPinAppWidget(any()); + } + + @Test + public void testSelectDefaultDoesNotRequestPinPeopleTile() { + //WHEN pref is true + Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, true); + + mNotificationInfo.bindNotification( + -1, + mShortcutManager, + mMockPackageManager, + mPeopleSpaceWidgetManager, + mMockINotificationManager, + mOnUserInteractionCallback, + TEST_PACKAGE_NAME, + mNotificationChannel, + mEntry, + mBubbleMetadata, + null, + null, + mIconFactory, + mContext, + mBuilderProvider, + true, + mTestHandler, + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); + + // WHEN user clicks "default" + mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_DEFAULT); + + // and then done + mNotificationInfo.findViewById(R.id.done).performClick(); + + // THEN the user is not presented with the People Tile pinning request + verify(mPeopleSpaceWidgetManager, never()).requestPinAppWidget(mShortcutInfo); + } + + @Test + public void testSelectPriority_AlreadyPriority_DoesNotRequestPinPeopleTile() { + //WHEN pref is true and channel is priority + Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, true); + mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH); + mConversationChannel.setImportance(IMPORTANCE_HIGH); + mConversationChannel.setImportantConversation(true); + + mNotificationInfo.bindNotification( + -1, + mShortcutManager, + mMockPackageManager, + mPeopleSpaceWidgetManager, + mMockINotificationManager, + mOnUserInteractionCallback, + TEST_PACKAGE_NAME, + mNotificationChannel, + mEntry, + mBubbleMetadata, + null, + null, + mIconFactory, + mContext, + mBuilderProvider, + true, + mTestHandler, + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); + + // WHEN user clicks "priority" + mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); + + // and then done + mNotificationInfo.findViewById(R.id.done).performClick(); + + // THEN the user is not presented with the People Tile pinning request + verify(mPeopleSpaceWidgetManager, never()).requestPinAppWidget(mShortcutInfo); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 458a058cc6ec..18481bca1aa6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -67,17 +67,20 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.SysuiTestCase; +import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; +import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.wmshell.BubblesManager; @@ -126,12 +129,15 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Mock private AccessibilityManager mAccessibilityManager; @Mock private HighPriorityProvider mHighPriorityProvider; @Mock private INotificationManager mINotificationManager; + @Mock private NotificationEntryManager mNotificationEntryManager; @Mock private LauncherApps mLauncherApps; @Mock private ShortcutManager mShortcutManager; @Mock private ChannelEditorDialogController mChannelEditorDialogController; @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier; @Mock private UserContextProvider mContextTracker; @Mock private BubblesManager mBubblesManager; + @Mock private ShadeController mShadeController; + @Mock private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; @Mock(answer = Answers.RETURNS_SELF) private PriorityOnboardingDialogController.Builder mBuilder; private Provider<PriorityOnboardingDialogController.Builder> mProvider = () -> mBuilder; @@ -154,10 +160,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase { mGutsManager = new NotificationGutsManager(mContext, () -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider, - mINotificationManager, mLauncherApps, mShortcutManager, - mChannelEditorDialogController, mContextTracker, mProvider, - mAssistantFeedbackController, Optional.of(mBubblesManager), - new UiEventLoggerFake(), mOnUserInteractionCallback); + mINotificationManager, mNotificationEntryManager, mPeopleSpaceWidgetManager, + mLauncherApps, mShortcutManager, mChannelEditorDialogController, mContextTracker, + mProvider, mAssistantFeedbackController, Optional.of(mBubblesManager), + new UiEventLoggerFake(), mOnUserInteractionCallback, mShadeController); mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer, mCheckSaveListener, mOnSettingsClickListener); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index bc014ec16103..d9e938948f5e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -431,4 +431,14 @@ public class KeyguardBouncerTest extends SysuiTestCase { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); assertThat(mBouncer.inTransit()).isFalse(); } + + @Test + public void testUpdateResources_delegatesToRootView() { + mBouncer.ensureView(); + mBouncer.updateResources(); + + // This is mocked, so won't pick up on the call to updateResources via + // mKeyguardViewController.init(), only updateResources above. + verify(mKeyguardHostViewController).updateResources(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index ec5114e181e8..91f3611d548d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -536,8 +536,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Test public void testCanCollapsePanelOnTouch_falseInDualPaneShade() { mStatusBarStateController.setState(SHADE); - when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); - when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); + enableSplitShade(); mNotificationPanelViewController.setQsExpanded(true); assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse(); @@ -562,6 +561,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { private void enableSplitShade() { when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); + mNotificationPanelViewController.updateResources(); } private void onTouchEvent(MotionEvent ev) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 83030eccedd1..45b791715904 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -276,4 +276,11 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { verify(action).onDismiss(); verify(cancelAction, never()).run(); } + + @Test + public void testUpdateResources_delegatesToBouncer() { + mStatusBarKeyguardViewManager.updateResources(); + + verify(mBouncer).updateResources(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 253460db0d07..18bdd41dada0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -884,6 +884,13 @@ public class StatusBarTest extends SysuiTestCase { verify(mDozeServiceHost).setDozeSuppressed(false); } + @Test + public void testUpdateResources_updatesBouncer() { + mStatusBar.updateResources(); + + verify(mStatusBarKeyguardViewManager).updateResources(); + } + public static class TestableNotificationInterruptStateProviderImpl extends NotificationInterruptStateProviderImpl { diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java index d40eecffb29e..6b6894676e94 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java @@ -34,10 +34,6 @@ public class SyncExecutor implements ShellExecutor { } @Override - public void removeAllCallbacks() { - } - - @Override public void removeCallbacks(Runnable runnable) { } diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index 5dd271c9dbb1..f06a94004110 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -340,5 +340,9 @@ message SystemMessage { // Notify the user that window magnification is available. // package: android NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE = 1004; + + // Notify the user that some accessibility service has view and control permissions. + // package: android + NOTE_A11Y_VIEW_AND_CONTROL_ACCESS = 1005; } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index b3be0448edaf..e7ffb1a64d4f 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -309,13 +309,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ public AccessibilityManagerService(Context context) { mContext = context; - mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mPowerManager = context.getSystemService(PowerManager.class); mWindowManagerService = LocalServices.getService(WindowManagerInternal.class); mA11yController = mWindowManagerService.getAccessibilityController(); mMainHandler = new MainHandler(mContext.getMainLooper()); mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class); mPackageManager = mContext.getPackageManager(); - mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this); + PolicyWarningUIController policyWarningUIController; + if (AccessibilitySecurityPolicy.POLICY_WARNING_ENABLED) { + policyWarningUIController = new PolicyWarningUIController(mMainHandler, context, + new PolicyWarningUIController.NotificationController(context)); + } + mSecurityPolicy = new AccessibilitySecurityPolicy(policyWarningUIController, mContext, + this); mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler, mWindowManagerService, this, mSecurityPolicy, this); mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler); @@ -351,6 +357,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (isA11yTracingEnabled()) { logTrace(LOG_TAG + ".onServiceInfoChangedLocked", "userState=" + userState); } + mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId, + userState.mBoundServices); scheduleNotifyClientsOfServicesStateChangeLocked(userState); } @@ -1302,6 +1310,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub AccessibilityUserState userState = getCurrentUserStateLocked(); readConfigurationForUserStateLocked(userState); + mSecurityPolicy.onSwitchUserLocked(mCurrentUserId, userState.mEnabledServices); // Even if reading did not yield change, we have to update // the state since the context in which the current user // state was used has changed since it was inactive. @@ -3665,6 +3674,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } else if (mEnabledAccessibilityServicesUri.equals(uri)) { if (readEnabledAccessibilityServicesLocked(userState)) { + mSecurityPolicy.onEnabledServicesChangedLocked(userState.mUserId, + userState.mEnabledServices); userState.updateCrashedServicesIfNeededLocked(); onUserStateChangedLocked(userState); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java index bef6d3e950c1..fd355d8da341 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java @@ -41,21 +41,19 @@ import com.android.internal.util.ArrayUtils; import libcore.util.EmptyArray; +import java.util.ArrayList; +import java.util.Set; + /** * This class provides APIs of accessibility security policies for accessibility manager - * to grant accessibility capabilities or events access right to accessibility service. + * to grant accessibility capabilities or events access right to accessibility services. And also + * monitors the current bound accessibility services to prompt permission warnings for + * not accessibility-categorized ones. */ public class AccessibilitySecurityPolicy { private static final int OWN_PROCESS_ID = android.os.Process.myPid(); private static final String LOG_TAG = "AccessibilitySecurityPolicy"; - private final Context mContext; - private final PackageManager mPackageManager; - private final UserManager mUserManager; - private final AppOpsManager mAppOpsManager; - - private AppWidgetManagerInternal mAppWidgetService; - private static final int KEEP_SOURCE_EVENT_TYPES = AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER @@ -72,6 +70,8 @@ public class AccessibilitySecurityPolicy { | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY; + public static final boolean POLICY_WARNING_ENABLED = true; + /** * Methods that should find their way into separate modules, but are still in AMS * TODO (b/111889696): Refactoring UserState to AccessibilityUserManager. @@ -84,19 +84,32 @@ public class AccessibilitySecurityPolicy { // TODO: Should include resolveProfileParentLocked, but that was already in SecurityPolicy } + private final Context mContext; + private final PackageManager mPackageManager; + private final UserManager mUserManager; + private final AppOpsManager mAppOpsManager; private final AccessibilityUserManager mAccessibilityUserManager; + private final PolicyWarningUIController mPolicyWarningUIController; + /** All bound accessibility services which don't belong to accessibility category. */ + private final ArraySet<ComponentName> mNonA11yCategoryServices = new ArraySet<>(); + + private AppWidgetManagerInternal mAppWidgetService; private AccessibilityWindowManager mAccessibilityWindowManager; + private int mCurrentUserId = UserHandle.USER_NULL; /** * Constructor for AccessibilityManagerService. */ - public AccessibilitySecurityPolicy(@NonNull Context context, + public AccessibilitySecurityPolicy(PolicyWarningUIController policyWarningUIController, + @NonNull Context context, @NonNull AccessibilityUserManager a11yUserManager) { mContext = context; mAccessibilityUserManager = a11yUserManager; mPackageManager = mContext.getPackageManager(); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + mPolicyWarningUIController = policyWarningUIController; + mPolicyWarningUIController.setAccessibilityPolicyManager(this); } /** @@ -568,4 +581,98 @@ public class AccessibilitySecurityPolicy { + permission); } } + + /** + * Called after a service was bound or unbound. Checks the current bound accessibility + * services and updates alarms. + * + * @param userId The user id + * @param boundServices The bound services + */ + public void onBoundServicesChangedLocked(int userId, + ArrayList<AccessibilityServiceConnection> boundServices) { + if (!POLICY_WARNING_ENABLED) { + return; + } + if (mAccessibilityUserManager.getCurrentUserIdLocked() != userId) { + return; + } + + ArraySet<ComponentName> tempNonA11yCategoryServices = new ArraySet<>(); + for (int i = 0; i < boundServices.size(); i++) { + final AccessibilityServiceInfo a11yServiceInfo = boundServices.get( + i).getServiceInfo(); + final ComponentName service = a11yServiceInfo.getComponentName().clone(); + if (!isA11yCategoryService(a11yServiceInfo)) { + tempNonA11yCategoryServices.add(service); + if (mNonA11yCategoryServices.contains(service)) { + mNonA11yCategoryServices.remove(service); + } else { + mPolicyWarningUIController.onNonA11yCategoryServiceBound(userId, service); + } + } + } + + for (int i = 0; i < mNonA11yCategoryServices.size(); i++) { + final ComponentName service = mNonA11yCategoryServices.valueAt(i); + mPolicyWarningUIController.onNonA11yCategoryServiceUnbound(userId, service); + } + mNonA11yCategoryServices.clear(); + mNonA11yCategoryServices.addAll(tempNonA11yCategoryServices); + } + + /** + * Called after switching to another user. Resets data and cancels old alarms after + * switching to another user. + * + * @param userId The user id + * @param enabledServices The enabled services + */ + public void onSwitchUserLocked(int userId, Set<ComponentName> enabledServices) { + if (!POLICY_WARNING_ENABLED) { + return; + } + if (mCurrentUserId == userId) { + return; + } + + mPolicyWarningUIController.onSwitchUserLocked(userId, enabledServices); + + for (int i = 0; i < mNonA11yCategoryServices.size(); i++) { + mPolicyWarningUIController.onNonA11yCategoryServiceUnbound(mCurrentUserId, + mNonA11yCategoryServices.valueAt(i)); + } + mNonA11yCategoryServices.clear(); + mCurrentUserId = userId; + } + + /** + * Called after the enabled accessibility services changed. + * + * @param userId The user id + * @param enabledServices The enabled services + */ + public void onEnabledServicesChangedLocked(int userId, + Set<ComponentName> enabledServices) { + if (!POLICY_WARNING_ENABLED) { + return; + } + if (mAccessibilityUserManager.getCurrentUserIdLocked() != userId) { + return; + } + + mPolicyWarningUIController.onEnabledServicesChangedLocked(userId, enabledServices); + } + + /** + * Identifies whether the accessibility service is true and designed for accessibility. An + * accessibility service is considered as accessibility category if + * {@link AccessibilityServiceInfo#isAccessibilityTool} is true. + * + * @param serviceInfo The accessibility service's serviceInfo. + * @return Returns true if it is a true accessibility service. + */ + public boolean isA11yCategoryService(AccessibilityServiceInfo serviceInfo) { + return serviceInfo.isAccessibilityTool(); + } } diff --git a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java new file mode 100644 index 000000000000..ea3e650a564a --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java @@ -0,0 +1,370 @@ +/* + * 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.app.AlarmManager.RTC_WAKEUP; + +import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_VIEW_AND_CONTROL_ACCESS; +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + +import android.Manifest; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.annotation.MainThread; +import android.annotation.NonNull; +import android.app.ActivityOptions; +import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.StatusBarManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.ArraySet; +import android.view.accessibility.AccessibilityManager; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.util.ImageUtils; + +import java.util.Calendar; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * The class handles permission warning notifications for not accessibility-categorized + * accessibility services from {@link AccessibilitySecurityPolicy}. And also maintains the setting + * {@link Settings.Secure#NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES} in order not to + * resend notifications to the same service. + */ +public class PolicyWarningUIController { + private static final String TAG = PolicyWarningUIController.class.getSimpleName(); + @VisibleForTesting + protected static final String ACTION_SEND_NOTIFICATION = TAG + ".ACTION_SEND_NOTIFICATION"; + @VisibleForTesting + protected static final String ACTION_A11Y_SETTINGS = TAG + ".ACTION_A11Y_SETTINGS"; + @VisibleForTesting + protected static final String ACTION_DISMISS_NOTIFICATION = + TAG + ".ACTION_DISMISS_NOTIFICATION"; + private static final int SEND_NOTIFICATION_DELAY_HOURS = 24; + + /** Current enabled accessibility services. */ + private final ArraySet<ComponentName> mEnabledA11yServices = new ArraySet<>(); + + private final Handler mMainHandler; + private final AlarmManager mAlarmManager; + private final Context mContext; + private final NotificationController mNotificationController; + + public PolicyWarningUIController(@NonNull Handler handler, @NonNull Context context, + NotificationController notificationController) { + mMainHandler = handler; + mContext = context; + mNotificationController = notificationController; + mAlarmManager = mContext.getSystemService(AlarmManager.class); + final IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_SEND_NOTIFICATION); + filter.addAction(ACTION_A11Y_SETTINGS); + filter.addAction(ACTION_DISMISS_NOTIFICATION); + mContext.registerReceiver(mNotificationController, filter, + Manifest.permission.MANAGE_ACCESSIBILITY, mMainHandler); + + } + + protected void setAccessibilityPolicyManager( + AccessibilitySecurityPolicy accessibilitySecurityPolicy) { + mNotificationController.setAccessibilityPolicyManager(accessibilitySecurityPolicy); + } + + /** + * Updates enabled accessibility services and notified accessibility services after switching + * to another user. + * + * @param enabledServices The current enabled services + */ + public void onSwitchUserLocked(int userId, Set<ComponentName> enabledServices) { + mEnabledA11yServices.clear(); + mEnabledA11yServices.addAll(enabledServices); + mMainHandler.sendMessage(obtainMessage(mNotificationController::onSwitchUser, userId)); + } + + /** + * Computes the newly disabled services and removes its record from the setting + * {@link Settings.Secure#NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES} after detecting the + * setting {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} changed. + * + * @param userId The user id + * @param enabledServices The enabled services + */ + public void onEnabledServicesChangedLocked(int userId, + Set<ComponentName> enabledServices) { + final ArraySet<ComponentName> disabledServices = new ArraySet<>(mEnabledA11yServices); + disabledServices.removeAll(enabledServices); + mEnabledA11yServices.clear(); + mEnabledA11yServices.addAll(enabledServices); + mMainHandler.sendMessage( + obtainMessage(mNotificationController::onServicesDisabled, userId, + disabledServices)); + } + + /** + * Called when the target service is bound. Sets an 24 hours alarm to the service which is not + * notified yet to execute action {@code ACTION_SEND_NOTIFICATION}. + * + * @param userId The user id + * @param service The service's component name + */ + public void onNonA11yCategoryServiceBound(int userId, ComponentName service) { + mMainHandler.sendMessage(obtainMessage(this::setAlarm, userId, service)); + } + + /** + * Called when the target service is unbound. Cancels the old alarm with intent action + * {@code ACTION_SEND_NOTIFICATION} from the service. + * + * @param userId The user id + * @param service The service's component name + */ + public void onNonA11yCategoryServiceUnbound(int userId, ComponentName service) { + mMainHandler.sendMessage(obtainMessage(this::cancelAlarm, userId, service)); + } + + private void setAlarm(int userId, ComponentName service) { + final Calendar cal = Calendar.getInstance(); + cal.add(Calendar.HOUR, SEND_NOTIFICATION_DELAY_HOURS); + mAlarmManager.set(RTC_WAKEUP, cal.getTimeInMillis(), + createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION, + service.flattenToShortString())); + } + + private void cancelAlarm(int userId, ComponentName service) { + mAlarmManager.cancel( + createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION, + service.flattenToShortString())); + } + + protected static PendingIntent createPendingIntent(Context context, int userId, String action, + String serviceComponentName) { + final Intent intent = new Intent(action); + intent.setPackage(context.getPackageName()) + .setIdentifier(serviceComponentName) + .putExtra(Intent.EXTRA_USER_ID, userId); + return PendingIntent.getBroadcast(context, 0, intent, + PendingIntent.FLAG_IMMUTABLE); + } + + /** A sub class to handle notifications and settings on the main thread. */ + @MainThread + public static class NotificationController extends BroadcastReceiver { + private static final char RECORD_SEPARATOR = ':'; + + /** All accessibility services which are notified to the user by the policy warning rule. */ + private final ArraySet<ComponentName> mNotifiedA11yServices = new ArraySet<>(); + private final NotificationManager mNotificationManager; + private final Context mContext; + + private int mCurrentUserId; + private AccessibilitySecurityPolicy mAccessibilitySecurityPolicy; + + public NotificationController(Context context) { + mContext = context; + mNotificationManager = mContext.getSystemService(NotificationManager.class); + } + + protected void setAccessibilityPolicyManager( + AccessibilitySecurityPolicy accessibilitySecurityPolicy) { + mAccessibilitySecurityPolicy = accessibilitySecurityPolicy; + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + final String service = intent.getIdentifier(); + final ComponentName componentName = ComponentName.unflattenFromString(service); + if (TextUtils.isEmpty(action) || TextUtils.isEmpty(service) + || componentName == null) { + return; + } + final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_SYSTEM); + if (ACTION_SEND_NOTIFICATION.equals(action)) { + trySendNotification(userId, componentName); + } else if (ACTION_A11Y_SETTINGS.equals(action)) { + launchSettings(userId, componentName); + mNotificationManager.cancel(service, NOTE_A11Y_VIEW_AND_CONTROL_ACCESS); + onNotificationCanceled(userId, componentName); + } else if (ACTION_DISMISS_NOTIFICATION.equals(action)) { + onNotificationCanceled(userId, componentName); + } + } + + protected void onSwitchUser(int userId) { + mCurrentUserId = userId; + mNotifiedA11yServices.clear(); + mNotifiedA11yServices.addAll(readNotifiedServiceList(userId)); + } + + protected void onServicesDisabled(int userId, + ArraySet<ComponentName> disabledServices) { + if (mNotifiedA11yServices.removeAll(disabledServices)) { + writeNotifiedServiceList(userId, mNotifiedA11yServices); + } + } + + private void trySendNotification(int userId, ComponentName componentName) { + if (!AccessibilitySecurityPolicy.POLICY_WARNING_ENABLED) { + return; + } + if (userId != mCurrentUserId) { + return; + } + + List<AccessibilityServiceInfo> enabledServiceInfos = getEnabledServiceInfos(); + for (int i = 0; i < enabledServiceInfos.size(); i++) { + final AccessibilityServiceInfo a11yServiceInfo = enabledServiceInfos.get(i); + if (componentName.flattenToShortString().equals( + a11yServiceInfo.getComponentName().flattenToShortString())) { + if (!mAccessibilitySecurityPolicy.isA11yCategoryService(a11yServiceInfo) + && !mNotifiedA11yServices.contains(componentName)) { + final CharSequence displayName = + a11yServiceInfo.getResolveInfo().serviceInfo.loadLabel( + mContext.getPackageManager()); + final Drawable drawable = a11yServiceInfo.getResolveInfo().loadIcon( + mContext.getPackageManager()); + final int size = mContext.getResources().getDimensionPixelSize( + android.R.dimen.app_icon_size); + sendNotification(userId, componentName.flattenToShortString(), + displayName, + ImageUtils.buildScaledBitmap(drawable, size, size)); + } + break; + } + } + } + + private void launchSettings(int userId, ComponentName componentName) { + if (userId != mCurrentUserId) { + return; + } + final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName.flattenToShortString()); + final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId( + mContext.getDisplayId()).toBundle(); + mContext.startActivityAsUser(intent, bundle, UserHandle.of(userId)); + mContext.getSystemService(StatusBarManager.class).collapsePanels(); + } + + protected void onNotificationCanceled(int userId, ComponentName componentName) { + if (userId != mCurrentUserId) { + return; + } + + if (mNotifiedA11yServices.add(componentName)) { + writeNotifiedServiceList(userId, mNotifiedA11yServices); + } + } + + private void sendNotification(int userId, String serviceComponentName, CharSequence name, + Bitmap bitmap) { + final Notification.Builder notificationBuilder = new Notification.Builder(mContext, + SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY); + notificationBuilder.setSmallIcon(R.drawable.ic_accessibility_24dp) + .setContentTitle( + mContext.getString(R.string.view_and_control_notification_title)) + .setContentText( + mContext.getString(R.string.view_and_control_notification_content, + name)) + .setStyle(new Notification.BigTextStyle() + .bigText( + mContext.getString( + R.string.view_and_control_notification_content, + name))) + .setTicker(mContext.getString(R.string.view_and_control_notification_title)) + .setOnlyAlertOnce(true) + .setDeleteIntent( + createPendingIntent(mContext, userId, ACTION_DISMISS_NOTIFICATION, + serviceComponentName)) + .setContentIntent( + createPendingIntent(mContext, userId, ACTION_A11Y_SETTINGS, + serviceComponentName)); + if (bitmap != null) { + notificationBuilder.setLargeIcon(bitmap); + } + mNotificationManager.notify(serviceComponentName, NOTE_A11Y_VIEW_AND_CONTROL_ACCESS, + notificationBuilder.build()); + } + + private ArraySet<ComponentName> readNotifiedServiceList(int userId) { + final String notifiedServiceSetting = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES, + userId); + if (TextUtils.isEmpty(notifiedServiceSetting)) { + return new ArraySet<>(); + } + + final TextUtils.StringSplitter componentNameSplitter = + new TextUtils.SimpleStringSplitter(RECORD_SEPARATOR); + componentNameSplitter.setString(notifiedServiceSetting); + + final ArraySet<ComponentName> notifiedServices = new ArraySet<>(); + final Iterator<String> it = componentNameSplitter.iterator(); + while (it.hasNext()) { + final String componentNameString = it.next(); + final ComponentName notifiedService = ComponentName.unflattenFromString( + componentNameString); + if (notifiedService != null) { + notifiedServices.add(notifiedService); + } + } + return notifiedServices; + } + + private void writeNotifiedServiceList(int userId, ArraySet<ComponentName> services) { + StringBuilder notifiedServicesBuilder = new StringBuilder(); + for (int i = 0; i < services.size(); i++) { + if (i > 0) { + notifiedServicesBuilder.append(RECORD_SEPARATOR); + } + final ComponentName notifiedService = services.valueAt(i); + notifiedServicesBuilder.append(notifiedService.flattenToShortString()); + } + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES, + notifiedServicesBuilder.toString(), userId); + } + + @VisibleForTesting + protected List<AccessibilityServiceInfo> getEnabledServiceInfos() { + final AccessibilityManager accessibilityManager = AccessibilityManager.getInstance( + mContext); + return accessibilityManager.getEnabledAccessibilityServiceList( + AccessibilityServiceInfo.FEEDBACK_ALL_MASK); + } + } +} diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java index b15d07b0f1a9..9d4c9ebbf592 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -16,10 +16,13 @@ package com.android.server.autofill.ui; +import static android.view.inputmethod.InlineSuggestionInfo.TYPE_SUGGESTION; + import static com.android.server.autofill.Helper.sDebug; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.IntentSender; import android.service.autofill.Dataset; import android.service.autofill.FillResponse; import android.service.autofill.InlinePresentation; @@ -49,6 +52,9 @@ final class InlineSuggestionFactory { InlineSuggestionInfo.TYPE_ACTION, () -> uiCallback.authenticate(requestId, AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED), mergedInlinePresentation(inlineFillUiInfo.mInlineRequest, 0, inlineAuthentication), + createInlineSuggestionTooltip(inlineFillUiInfo.mInlineRequest, + inlineFillUiInfo, InlineSuggestionInfo.SOURCE_AUTOFILL, + response.getInlineTooltipPresentation()), uiCallback); } @@ -66,6 +72,8 @@ final class InlineSuggestionFactory { final InlineSuggestionsRequest request = inlineFillUiInfo.mInlineRequest; SparseArray<Pair<Dataset, InlineSuggestion>> response = new SparseArray<>(datasets.size()); + + boolean hasTooltip = false; for (int datasetIndex = 0; datasetIndex < datasets.size(); datasetIndex++) { final Dataset dataset = datasets.get(datasetIndex); final int fieldIndex = dataset.getFieldIds().indexOf(inlineFillUiInfo.mFocusedId); @@ -82,14 +90,25 @@ final class InlineSuggestionFactory { } final String suggestionType = - dataset.getAuthentication() == null ? InlineSuggestionInfo.TYPE_SUGGESTION + dataset.getAuthentication() == null ? TYPE_SUGGESTION : InlineSuggestionInfo.TYPE_ACTION; final int index = datasetIndex; + InlineSuggestion inlineSuggestionTooltip = null; + if (!hasTooltip) { + // Only available for first one inline suggestion tooltip. + inlineSuggestionTooltip = createInlineSuggestionTooltip(request, + inlineFillUiInfo, suggestionSource, + dataset.getFieldInlineTooltipPresentation(fieldIndex)); + if (inlineSuggestionTooltip != null) { + hasTooltip = true; + } + } InlineSuggestion inlineSuggestion = createInlineSuggestion( inlineFillUiInfo, suggestionSource, suggestionType, () -> uiCallback.autofill(dataset, index), mergedInlinePresentation(request, datasetIndex, inlinePresentation), + inlineSuggestionTooltip, uiCallback); response.append(datasetIndex, Pair.create(dataset, inlineSuggestion)); } @@ -103,11 +122,13 @@ final class InlineSuggestionFactory { @NonNull @InlineSuggestionInfo.Type String suggestionType, @NonNull Runnable onClickAction, @NonNull InlinePresentation inlinePresentation, + @Nullable InlineSuggestion tooltip, @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) { + final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( inlinePresentation.getInlinePresentationSpec(), suggestionSource, inlinePresentation.getAutofillHints(), suggestionType, - inlinePresentation.isPinned()); + inlinePresentation.isPinned(), tooltip); return new InlineSuggestion(inlineSuggestionInfo, createInlineContentProvider(inlineFillUiInfo, inlinePresentation, @@ -135,6 +156,60 @@ final class InlineSuggestionFactory { inlinePresentation.isPinned()); } + // TODO(182306770): creates new class instead of the InlineSuggestion. + private static InlineSuggestion createInlineSuggestionTooltip( + @NonNull InlineSuggestionsRequest request, + @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo, + String suggestionSource, + @NonNull InlinePresentation tooltipPresentation) { + if (tooltipPresentation == null) { + return null; + } + + final InlinePresentationSpec spec = request.getInlineTooltipPresentationSpec(); + InlinePresentationSpec mergedSpec; + if (spec == null) { + mergedSpec = tooltipPresentation.getInlinePresentationSpec(); + } else { + mergedSpec = new InlinePresentationSpec.Builder( + tooltipPresentation.getInlinePresentationSpec().getMinSize(), + tooltipPresentation.getInlinePresentationSpec().getMaxSize()).setStyle( + spec.getStyle()).build(); + } + + InlineFillUi.InlineSuggestionUiCallback uiCallback = + new InlineFillUi.InlineSuggestionUiCallback() { + @Override + public void autofill(Dataset dataset, int datasetIndex) { + /* nothing */ + } + + @Override + public void authenticate(int requestId, int datasetIndex) { + /* nothing */ + } + + @Override + public void startIntentSender(IntentSender intentSender) { + /* nothing */ + } + + @Override + public void onError() { + Slog.w(TAG, "An error happened on the tooltip"); + } + }; + + InlinePresentation tooltipInline = new InlinePresentation(tooltipPresentation.getSlice(), + mergedSpec, false); + IInlineContentProvider tooltipContentProvider = createInlineContentProvider( + inlineFillUiInfo, tooltipInline, () -> { /* no operation */ }, uiCallback); + final InlineSuggestionInfo tooltipInlineSuggestionInfo = new InlineSuggestionInfo( + mergedSpec, suggestionSource, /* autofillHints */ null, TYPE_SUGGESTION, + /* pinned */ false, /* tooltip */ null); + return new InlineSuggestion(tooltipInlineSuggestionInfo, tooltipContentProvider); + } + private static IInlineContentProvider createInlineContentProvider( @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo, @NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction, diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 92af0804955f..9ac93d92713b 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -1078,6 +1078,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind Date lastSeen = mDevicesLastNearby.get(address); if (isDeviceDisappeared(lastSeen)) { onDeviceDisappeared(address); + unscheduleTriggerDeviceDisappearedRunnable(address); } } @@ -1213,7 +1214,18 @@ public class CompanionDeviceManagerService extends SystemService implements Bind @Override public void run() { Slog.d(LOG_TAG, "TriggerDeviceDisappearedRunnable.run(address = " + mAddress + ")"); - onDeviceDisappeared(mAddress); + if (!mCurrentlyConnectedDevices.contains(mAddress)) { + onDeviceDisappeared(mAddress); + } + } + } + + private void unscheduleTriggerDeviceDisappearedRunnable(String address) { + Runnable r = mTriggerDeviceDisappearedRunnables.get(address); + if (r != null) { + Slog.d(LOG_TAG, + "unscheduling TriggerDeviceDisappearedRunnable(address = " + address + ")"); + mMainHandler.removeCallbacks(r); } } diff --git a/services/core/Android.bp b/services/core/Android.bp index 0fe874c0840a..ed2e62513ab3 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -225,7 +225,6 @@ filegroup { "java/com/android/server/TestNetworkService.java", "java/com/android/server/connectivity/AutodestructReference.java", "java/com/android/server/connectivity/ConnectivityConstants.java", - "java/com/android/server/connectivity/ConnectivityResources.java", "java/com/android/server/connectivity/DnsManager.java", "java/com/android/server/connectivity/KeepaliveTracker.java", "java/com/android/server/connectivity/LingerMonitor.java", diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 3194bdcaad18..512cc728ccc4 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -72,8 +72,8 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.NetworkPolicyManager.RULE_NONE; -import static android.net.NetworkPolicyManager.uidRulesToString; +import static android.net.NetworkPolicyManager.BLOCKED_REASON_NONE; +import static android.net.NetworkPolicyManager.blockedReasonsToString; import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST; import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired; import static android.os.Process.INVALID_UID; @@ -106,6 +106,7 @@ import android.net.ConnectivityDiagnosticsManager.ConnectivityReport; import android.net.ConnectivityDiagnosticsManager.DataStallReport; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; +import android.net.ConnectivityManager.RestrictBackgroundStatus; import android.net.ConnectivitySettingsManager; import android.net.DataStallReportParcelable; import android.net.DnsResolverServiceManager; @@ -117,7 +118,6 @@ import android.net.INetd; import android.net.INetworkActivityListener; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; -import android.net.INetworkPolicyListener; import android.net.IOnCompleteListener; import android.net.IQosCallback; import android.net.ISocketKeepaliveCallback; @@ -135,6 +135,7 @@ import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkMonitorManager; import android.net.NetworkPolicyManager; +import android.net.NetworkPolicyManager.NetworkPolicyCallback; import android.net.NetworkProvider; import android.net.NetworkRequest; import android.net.NetworkScore; @@ -164,6 +165,7 @@ import android.net.UnderlyingNetworkInfo; import android.net.Uri; import android.net.VpnManager; import android.net.VpnTransportInfo; +import android.net.ConnectivityResources; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.netlink.InetDiagMessage; @@ -222,7 +224,6 @@ import com.android.net.module.util.LocationPermissionChecker; import com.android.net.module.util.NetworkCapabilitiesUtils; import com.android.net.module.util.PermissionUtils; import com.android.server.connectivity.AutodestructReference; -import com.android.server.connectivity.ConnectivityResources; import com.android.server.connectivity.DnsManager; import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate; import com.android.server.connectivity.KeepaliveTracker; @@ -285,7 +286,7 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * Default URL to use for {@link #getCaptivePortalServerUrl()}. This should not be changed * by OEMs for configuration purposes, as this value is overridden by - * Settings.Global.CAPTIVE_PORTAL_HTTP_URL. + * ConnectivitySettingsManager.CAPTIVE_PORTAL_HTTP_URL. * R.string.config_networkCaptivePortalServerUrl should be overridden instead for this purpose * (preferably via runtime resource overlays). */ @@ -318,7 +319,7 @@ public class ConnectivityService extends IConnectivityManager.Stub protected int mNascentDelayMs; // How long to delay to removal of a pending intent based request. - // See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS + // See ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS private final int mReleasePendingIntentDelayMs; private MockableSystemProperties mSystemProperties; @@ -331,12 +332,10 @@ public class ConnectivityService extends IConnectivityManager.Stub private volatile boolean mLockdownEnabled; /** - * Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal - * handler thread, they don't need a lock. + * Stale copy of uid blocked reasons provided by NPMS. As long as they are accessed only in + * internal handler thread, they don't need a lock. */ - private SparseIntArray mUidRules = new SparseIntArray(); - /** Flag indicating if background data is restricted. */ - private boolean mRestrictBackground; + private SparseIntArray mUidBlockedReasons = new SparseIntArray(); private final Context mContext; private final ConnectivityResources mResources; @@ -510,16 +509,6 @@ public class ConnectivityService extends IConnectivityManager.Stub // Handle private DNS validation status updates. private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38; - /** - * Used to handle onUidRulesChanged event from NetworkPolicyManagerService. - */ - private static final int EVENT_UID_RULES_CHANGED = 39; - - /** - * Used to handle onRestrictBackgroundChanged event from NetworkPolicyManagerService. - */ - private static final int EVENT_DATA_SAVER_CHANGED = 40; - /** * Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has * been tested. @@ -596,6 +585,13 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_SET_PROFILE_NETWORK_PREFERENCE = 50; /** + * Event to specify that reasons for why an uid is blocked changed. + * arg1 = uid + * arg2 = blockedReasons + */ + private static final int EVENT_UID_BLOCKED_REASON_CHANGED = 51; + + /** * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification * should be shown. */ @@ -1234,7 +1230,7 @@ public class ConnectivityService extends IConnectivityManager.Stub new ConnectivityDiagnosticsHandler(mHandlerThread.getLooper()); mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(), - Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000); + ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000); mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); // TODO: Consider making the timer customizable. @@ -1253,10 +1249,10 @@ public class ConnectivityService extends IConnectivityManager.Stub mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); mLocationPermissionChecker = new LocationPermissionChecker(mContext); - // To ensure uid rules are synchronized with Network Policy, register for + // To ensure uid state is synchronized with Network Policy, register for // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService // reading existing policy from disk. - mPolicyManager.registerListener(mPolicyListener); + mPolicyManager.registerNetworkPolicyCallback(null, mPolicyCallback); final PowerManager powerManager = (PowerManager) context.getSystemService( Context.POWER_SERVICE); @@ -1306,10 +1302,10 @@ public class ConnectivityService extends IConnectivityManager.Stub mQosCallbackTracker = new QosCallbackTracker(mHandler, mNetworkRequestCounter); final int dailyLimit = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, + ConnectivitySettingsManager.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, LingerMonitor.DEFAULT_NOTIFICATION_DAILY_LIMIT); final long rateLimit = Settings.Global.getLong(mContext.getContentResolver(), - Settings.Global.NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, + ConnectivitySettingsManager.NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, LingerMonitor.DEFAULT_NOTIFICATION_RATE_LIMIT_MILLIS); mLingerMonitor = new LingerMonitor(mContext, mNotifier, dailyLimit, rateLimit); @@ -1427,10 +1423,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void handleConfigureAlwaysOnNetworks() { - handleAlwaysOnNetworkRequest( - mDefaultMobileDataRequest, Settings.Global.MOBILE_DATA_ALWAYS_ON, true); - handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED, - false); + handleAlwaysOnNetworkRequest(mDefaultMobileDataRequest, + ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON, true /* defaultValue */); + handleAlwaysOnNetworkRequest(mDefaultWifiRequest, + ConnectivitySettingsManager.WIFI_ALWAYS_REQUESTED, false /* defaultValue */); handleAlwaysOnNetworkRequest(mDefaultVehicleRequest, com.android.internal.R.bool.config_vehicleInternalNetworkAlwaysRequested); } @@ -1443,12 +1439,12 @@ public class ConnectivityService extends IConnectivityManager.Stub // Watch for whether or not to keep mobile data always on. mSettingsObserver.observe( - Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON), + Settings.Global.getUriFor(ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON), EVENT_CONFIGURE_ALWAYS_ON_NETWORKS); // Watch for whether or not to keep wifi always on. mSettingsObserver.observe( - Settings.Global.getUriFor(Settings.Global.WIFI_ALWAYS_REQUESTED), + Settings.Global.getUriFor(ConnectivitySettingsManager.WIFI_ALWAYS_REQUESTED), EVENT_CONFIGURE_ALWAYS_ON_NETWORKS); } @@ -2002,6 +1998,18 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + @Override + public @RestrictBackgroundStatus int getRestrictBackgroundStatusByCaller() { + enforceAccessPermission(); + final int callerUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + return mPolicyManager.getRestrictBackgroundStatus(callerUid); + } finally { + Binder.restoreCallingIdentity(token); + } + } + // TODO: Consider delete this function or turn it into a no-op method. @Override public NetworkState[] getAllNetworkState() { @@ -2237,53 +2245,17 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() { + private final NetworkPolicyCallback mPolicyCallback = new NetworkPolicyCallback() { @Override - public void onUidRulesChanged(int uid, int uidRules) { - mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_RULES_CHANGED, uid, uidRules)); - } - @Override - public void onRestrictBackgroundChanged(boolean restrictBackground) { - // caller is NPMS, since we only register with them - if (LOGD_BLOCKED_NETWORKINFO) { - log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")"); - } - mHandler.sendMessage(mHandler.obtainMessage( - EVENT_DATA_SAVER_CHANGED, restrictBackground ? 1 : 0, 0)); + public void onUidBlockedReasonChanged(int uid, int blockedReasons) { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_BLOCKED_REASON_CHANGED, + uid, blockedReasons)); } }; - void handleUidRulesChanged(int uid, int newRules) { - // skip update when we've already applied rules - final int oldRules = mUidRules.get(uid, RULE_NONE); - if (oldRules == newRules) return; - - maybeNotifyNetworkBlockedForNewUidRules(uid, newRules); - - if (newRules == RULE_NONE) { - mUidRules.delete(uid); - } else { - mUidRules.put(uid, newRules); - } - } - - void handleRestrictBackgroundChanged(boolean restrictBackground) { - if (mRestrictBackground == restrictBackground) return; - - final List<UidRange> blockedRanges = mVpnBlockedUidRanges; - for (final NetworkAgentInfo nai : mNetworkAgentInfos) { - final boolean curMetered = nai.networkCapabilities.isMetered(); - maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground, - restrictBackground, blockedRanges, blockedRanges); - } - - mRestrictBackground = restrictBackground; - } - - private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered, - boolean isBackgroundRestricted) { - return mPolicyManager.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered, - isBackgroundRestricted); + void handleUidBlockedReasonChanged(int uid, int blockedReasons) { + maybeNotifyNetworkBlockedForNewState(uid, blockedReasons); + mUidBlockedReasons.put(uid, blockedReasons); } private boolean checkAnyPermissionOf(String... permissions) { @@ -2757,19 +2729,16 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.decreaseIndent(); pw.println(); - pw.print("Restrict background: "); - pw.println(mRestrictBackground); - pw.println(); - pw.println("Status for known UIDs:"); pw.increaseIndent(); - final int size = mUidRules.size(); + final int size = mUidBlockedReasons.size(); for (int i = 0; i < size; i++) { // Don't crash if the array is modified while dumping in bugreports. try { - final int uid = mUidRules.keyAt(i); - final int uidRules = mUidRules.get(uid, RULE_NONE); - pw.println("UID=" + uid + " rules=" + uidRulesToString(uidRules)); + final int uid = mUidBlockedReasons.keyAt(i); + final int blockedReasons = mUidBlockedReasons.valueAt(i); + pw.println("UID=" + uid + " blockedReasons=" + + blockedReasonsToString(blockedReasons)); } catch (ArrayIndexOutOfBoundsException e) { pw.println(" ArrayIndexOutOfBoundsException"); } catch (ConcurrentModificationException e) { @@ -3110,7 +3079,8 @@ public class ConnectivityService extends IConnectivityManager.Stub nai.lastCaptivePortalDetected = visible; nai.everCaptivePortalDetected |= visible; if (nai.lastCaptivePortalDetected && - Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) { + ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_AVOID + == getCaptivePortalMode()) { if (DBG) log("Avoiding captive portal network: " + nai.toShortString()); nai.onPreventAutomaticReconnect(); teardownUnneededNetwork(nai); @@ -3221,8 +3191,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private int getCaptivePortalMode() { return Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.CAPTIVE_PORTAL_MODE, - Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); + ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE, + ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_PROMPT); } private boolean maybeHandleNetworkAgentInfoMessage(Message msg) { @@ -4446,7 +4416,13 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkPolicyManager netPolicyManager = mContext.getSystemService(NetworkPolicyManager.class); - final int networkPreference = netPolicyManager.getMultipathPreference(network); + final long token = Binder.clearCallingIdentity(); + final int networkPreference; + try { + networkPreference = netPolicyManager.getMultipathPreference(network); + } finally { + Binder.restoreCallingIdentity(token); + } if (networkPreference != 0) { return networkPreference; } @@ -4565,11 +4541,8 @@ public class ConnectivityService extends IConnectivityManager.Stub handlePrivateDnsValidationUpdate( (PrivateDnsValidationUpdate) msg.obj); break; - case EVENT_UID_RULES_CHANGED: - handleUidRulesChanged(msg.arg1, msg.arg2); - break; - case EVENT_DATA_SAVER_CHANGED: - handleRestrictBackgroundChanged(toBool(msg.arg1)); + case EVENT_UID_BLOCKED_REASON_CHANGED: + handleUidBlockedReasonChanged(msg.arg1, msg.arg2); break; case EVENT_SET_REQUIRE_VPN_FOR_UIDS: handleSetRequireVpnForUids(toBool(msg.arg1), (UidRange[]) msg.obj); @@ -5042,8 +5015,8 @@ public class ConnectivityService extends IConnectivityManager.Stub for (final NetworkAgentInfo nai : mNetworkAgentInfos) { final boolean curMetered = nai.networkCapabilities.isMetered(); - maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground, - mRestrictBackground, mVpnBlockedUidRanges, newVpnBlockedUidRanges); + maybeNotifyNetworkBlocked(nai, curMetered, curMetered, + mVpnBlockedUidRanges, newVpnBlockedUidRanges); } mVpnBlockedUidRanges = newVpnBlockedUidRanges; @@ -6826,8 +6799,8 @@ public class ConnectivityService extends IConnectivityManager.Stub final boolean meteredChanged = oldMetered != newMetered; if (meteredChanged) { - maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground, - mRestrictBackground, mVpnBlockedUidRanges, mVpnBlockedUidRanges); + maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, + mVpnBlockedUidRanges, mVpnBlockedUidRanges); } final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) @@ -7950,8 +7923,8 @@ public class ConnectivityService extends IConnectivityManager.Stub final boolean metered = nai.networkCapabilities.isMetered(); boolean blocked; blocked = isUidBlockedByVpn(nri.mUid, mVpnBlockedUidRanges); - blocked |= isUidBlockedByRules(nri.mUid, mUidRules.get(nri.mUid), - metered, mRestrictBackground); + blocked |= NetworkPolicyManager.isUidBlocked( + mUidBlockedReasons.get(nri.mUid, BLOCKED_REASON_NONE), metered); callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0); } @@ -7969,16 +7942,14 @@ public class ConnectivityService extends IConnectivityManager.Stub * * @param nai The target NetworkAgentInfo. * @param oldMetered True if the previous network capabilities is metered. - * @param newRestrictBackground True if data saver is enabled. */ private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered, - boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground, - List<UidRange> oldBlockedUidRanges, List<UidRange> newBlockedUidRanges) { + boolean newMetered, List<UidRange> oldBlockedUidRanges, + List<UidRange> newBlockedUidRanges) { for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest nr = nai.requestAt(i); NetworkRequestInfo nri = mNetworkRequests.get(nr); - final int uidRules = mUidRules.get(nri.mUid); final boolean oldBlocked, newBlocked, oldVpnBlocked, newVpnBlocked; oldVpnBlocked = isUidBlockedByVpn(nri.mUid, oldBlockedUidRanges); @@ -7986,10 +7957,11 @@ public class ConnectivityService extends IConnectivityManager.Stub ? isUidBlockedByVpn(nri.mUid, newBlockedUidRanges) : oldVpnBlocked; - oldBlocked = oldVpnBlocked || isUidBlockedByRules(nri.mUid, uidRules, oldMetered, - oldRestrictBackground); - newBlocked = newVpnBlocked || isUidBlockedByRules(nri.mUid, uidRules, newMetered, - newRestrictBackground); + final int blockedReasons = mUidBlockedReasons.get(nri.mUid, BLOCKED_REASON_NONE); + oldBlocked = oldVpnBlocked || NetworkPolicyManager.isUidBlocked( + blockedReasons, oldMetered); + newBlocked = newVpnBlocked || NetworkPolicyManager.isUidBlocked( + blockedReasons, newMetered); if (oldBlocked != newBlocked) { callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, @@ -7999,19 +7971,20 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** - * Notify apps with a given UID of the new blocked state according to new uid rules. + * Notify apps with a given UID of the new blocked state according to new uid state. * @param uid The uid for which the rules changed. - * @param newRules The new rules to apply. + * @param blockedReasons The reasons for why an uid is blocked. */ - private void maybeNotifyNetworkBlockedForNewUidRules(int uid, int newRules) { + private void maybeNotifyNetworkBlockedForNewState(int uid, int blockedReasons) { for (final NetworkAgentInfo nai : mNetworkAgentInfos) { final boolean metered = nai.networkCapabilities.isMetered(); final boolean vpnBlocked = isUidBlockedByVpn(uid, mVpnBlockedUidRanges); final boolean oldBlocked, newBlocked; - oldBlocked = vpnBlocked || isUidBlockedByRules( - uid, mUidRules.get(uid), metered, mRestrictBackground); - newBlocked = vpnBlocked || isUidBlockedByRules( - uid, newRules, metered, mRestrictBackground); + + oldBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked( + mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE), metered); + newBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked( + blockedReasons, metered); if (oldBlocked == newBlocked) { continue; } @@ -8159,7 +8132,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } settingUrl = Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.CAPTIVE_PORTAL_HTTP_URL); + ConnectivitySettingsManager.CAPTIVE_PORTAL_HTTP_URL); if (!TextUtils.isEmpty(settingUrl)) { return settingUrl; } @@ -8241,7 +8214,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // restore private DNS settings to default mode (opportunistic) if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS)) { Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OPPORTUNISTIC); + ConnectivitySettingsManager.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OPPORTUNISTIC); } Settings.Global.putString(mContext.getContentResolver(), @@ -8997,13 +8970,13 @@ public class ConnectivityService extends IConnectivityManager.Stub if (networkAgent.networkCapabilities.hasTransport( NetworkCapabilities.TRANSPORT_CELLULAR)) { timeout = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE, + ConnectivitySettingsManager.DATA_ACTIVITY_TIMEOUT_MOBILE, 10); type = NetworkCapabilities.TRANSPORT_CELLULAR; } else if (networkAgent.networkCapabilities.hasTransport( NetworkCapabilities.TRANSPORT_WIFI)) { timeout = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI, + ConnectivitySettingsManager.DATA_ACTIVITY_TIMEOUT_WIFI, 15); type = NetworkCapabilities.TRANSPORT_WIFI; } else { diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java index bab6deb812a7..af791cb2cfcb 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -162,7 +162,7 @@ public class GestureLauncherService extends SystemService { GESTURE_CAMERA_DOUBLE_TAP_POWER(660), @UiEvent(doc = "The user multi-tapped power quickly enough to signal an emergency.") - GESTURE_PANIC_TAP_POWER(661); + GESTURE_EMERGENCY_TAP_POWER(661); private final int mId; @@ -508,7 +508,7 @@ public class GestureLauncherService extends SystemService { } else if (launchEmergencyGesture) { Slog.i(TAG, "Emergency gesture detected, launching."); launchEmergencyGesture = handleEmergencyGesture(); - mUiEventLogger.log(GestureLauncherEvent.GESTURE_PANIC_TAP_POWER); + mUiEventLogger.log(GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER); } mMetricsLogger.histogram("power_consecutive_short_tap_count", mPowerButtonSlowConsecutiveTaps); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 8b5bb1de4d1b..cbce7203ca08 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -18,6 +18,7 @@ package com.android.server; import static android.Manifest.permission.ACCESS_MTP; import static android.Manifest.permission.INSTALL_PACKAGES; +import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.OP_LEGACY_STORAGE; @@ -4606,6 +4607,25 @@ class StorageManagerService extends IStorageManager.Stub } @Override + public boolean hasExternalStorageAccess(int uid, String packageName) { + try { + if (mIPackageManager.checkUidPermission( + MANAGE_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED) { + return true; + } + + if (mIAppOpsService.checkOperation( + OP_MANAGE_EXTERNAL_STORAGE, uid, packageName) == MODE_ALLOWED) { + return true; + } + } catch (RemoteException e) { + Slog.w("Failed to check MANAGE_EXTERNAL_STORAGE access for " + packageName, e); + } + + return false; + } + + @Override public void addResetListener(StorageManagerInternal.ResetListener listener) { synchronized (mResetListeners) { mResetListeners.add(listener); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 83cbf66ecaea..0e8644a6569e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16147,7 +16147,12 @@ public class ActivityManagerService extends IActivityManager.Stub } } - public void waitForBroadcastIdle(PrintWriter pw) { + @Override + public void waitForBroadcastIdle() { + waitForBroadcastIdle(/* printWriter= */ null); + } + + public void waitForBroadcastIdle(@Nullable PrintWriter pw) { enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()"); while (true) { boolean idle = true; @@ -16155,9 +16160,11 @@ public class ActivityManagerService extends IActivityManager.Stub for (BroadcastQueue queue : mBroadcastQueues) { if (!queue.isIdle()) { final String msg = "Waiting for queue " + queue + " to become idle..."; - pw.println(msg); - pw.println(queue.describeState()); - pw.flush(); + if (pw != null) { + pw.println(msg); + pw.println(queue.describeState()); + pw.flush(); + } Slog.v(TAG, msg); queue.cancelDeferrals(); idle = false; @@ -16167,8 +16174,10 @@ public class ActivityManagerService extends IActivityManager.Stub if (idle) { final String msg = "All broadcast queues are idle!"; - pw.println(msg); - pw.flush(); + if (pw != null) { + pw.println(msg); + pw.flush(); + } Slog.v(TAG, msg); return; } else { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 4972aa710497..442cdd922213 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1598,7 +1598,8 @@ public final class ProcessList { } } - private int[] computeGidsForProcess(int mountExternal, int uid, int[] permGids) { + private int[] computeGidsForProcess(int mountExternal, int uid, int[] permGids, + boolean externalStorageAccess) { ArrayList<Integer> gidList = new ArrayList<>(permGids.length + 5); final int sharedAppGid = UserHandle.getSharedAppGid(UserHandle.getAppId(uid)); @@ -1644,6 +1645,11 @@ public final class ProcessList { // PublicVolumes: /mnt/media_rw/<volume> gidList.add(Process.MEDIA_RW_GID); } + if (externalStorageAccess) { + // Apps with MANAGE_EXTERNAL_STORAGE PERMISSION need the external_storage gid to access + // USB OTG (unreliable) volumes on /mnt/media_rw/<vol name> + gidList.add(Process.EXTERNAL_STORAGE_GID); + } int[] gidArray = new int[gidList.size()]; for (int i = 0; i < gidArray.length; i++) { @@ -1805,6 +1811,7 @@ public final class ProcessList { int uid = app.uid; int[] gids = null; int mountExternal = Zygote.MOUNT_EXTERNAL_NONE; + boolean externalStorageAccess = false; if (!app.isolated) { int[] permGids = null; try { @@ -1816,6 +1823,8 @@ public final class ProcessList { StorageManagerInternal.class); mountExternal = storageManagerInternal.getExternalStorageMountMode(uid, app.info.packageName); + externalStorageAccess = storageManagerInternal.hasExternalStorageAccess(uid, + app.info.packageName); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -1835,7 +1844,7 @@ public final class ProcessList { } } - gids = computeGidsForProcess(mountExternal, uid, permGids); + gids = computeGidsForProcess(mountExternal, uid, permGids, externalStorageAccess); } app.setMountMode(mountExternal); checkSlow(startTime, "startProcess: building args"); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index e4fc89540b98..88dca0cd8673 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -395,6 +395,9 @@ import java.util.concurrent.atomic.AtomicBoolean; AudioSystem.STREAM_VOICE_CALL); List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(attr); if (devices.isEmpty()) { + if (mAudioService.isPlatformVoice()) { + Log.w(TAG, "getCommunicationDevice(): no device for phone strategy"); + } return null; } device = devices.get(0); @@ -746,7 +749,7 @@ import java.util.concurrent.atomic.AtomicBoolean; @GuardedBy("mDeviceStateLock") private void dispatchCommunicationDevice() { AudioDeviceInfo device = getCommunicationDevice(); - int portId = (getCommunicationDevice() == null) ? 0 : device.getId(); + int portId = (device == null) ? 0 : device.getId(); if (portId == mCurCommunicationPortId) { return; } @@ -1022,9 +1025,9 @@ import java.util.concurrent.atomic.AtomicBoolean; pw.println("\n" + prefix + "mPreferredCommunicationDevice: " + mPreferredCommunicationDevice); + AudioDeviceInfo device = getCommunicationDevice(); pw.println(prefix + "Selected Communication Device: " - + ((getCommunicationDevice() == null) ? "None" - : new AudioDeviceAttributes(getCommunicationDevice()))); + + ((device == null) ? "None" : new AudioDeviceAttributes(device))); pw.println(prefix + "mCommunicationStrategyId: " + mCommunicationStrategyId); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 1950710a36e0..2e6cfdcf141f 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -248,7 +248,7 @@ public class AudioService extends IAudioService.Stub // indicates whether the system maps all streams to a single stream. private final boolean mIsSingleVolume; - private boolean isPlatformVoice() { + /*package*/ boolean isPlatformVoice() { return mPlatformType == AudioSystem.PLATFORM_VOICE; } @@ -2258,7 +2258,7 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */ public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes( @NonNull AudioAttributes attributes) { - enforceModifyAudioRoutingPermission(); + enforceQueryStateOrModifyRoutingPermission(); return getDevicesForAttributesInt(attributes); } @@ -2900,6 +2900,16 @@ public class AudioService extends IAudioService.Stub } } + private void enforceQueryStateOrModifyRoutingPermission() { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + != PackageManager.PERMISSION_GRANTED + && mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException( + "Missing MODIFY_AUDIO_ROUTING or QUERY_AUDIO_STATE permissions"); + } + } + /** @see AudioManager#setVolumeIndexForAttributes(attr, int, int) */ public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags, String callingPackage) { @@ -5814,7 +5824,7 @@ public class AudioService extends IAudioService.Stub public @AudioManager.DeviceVolumeBehavior int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) { // verify permissions - enforceModifyAudioRoutingPermission(); + enforceQueryStateOrModifyRoutingPermission(); // translate Java device type to native device type (for the devices masks for full / fixed) final int audioSystemDeviceOut = AudioDeviceInfo.convertDeviceTypeToInternalDevice( diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 285f3185abc2..5b9fa7922e9d 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -340,6 +340,20 @@ public class AuthService extends SystemService { } @Override + public void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, + int userId, byte[] hardwareAuthToken) throws RemoteException { + checkInternalPermission(); + + final long identity = Binder.clearCallingIdentity(); + try { + mBiometricService.resetLockoutTimeBound(token, opPackageName, fromSensorId, userId, + hardwareAuthToken); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public CharSequence getButtonLabel( int userId, String opPackageName, diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index a88820988ef7..63e7b4b84366 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -754,7 +754,7 @@ public class BiometricService extends SystemService { } } - @Override + @Override // Binder call public void invalidateAuthenticatorIds(int userId, int fromSensorId, IInvalidationCallback callback) { checkInternalPermission(); @@ -790,6 +790,45 @@ public class BiometricService extends SystemService { } @Override // Binder call + public void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, + int userId, byte[] hardwareAuthToken) { + checkInternalPermission(); + + // Check originating strength + if (!Utils.isAtLeastStrength(getSensorForId(fromSensorId).getCurrentStrength(), + Authenticators.BIOMETRIC_STRONG)) { + Slog.w(TAG, "Sensor: " + fromSensorId + " is does not meet the required strength to" + + " request resetLockout"); + return; + } + + // Request resetLockout for applicable sensors + for (BiometricSensor sensor : mSensors) { + if (sensor.id == fromSensorId) { + continue; + } + try { + final SensorPropertiesInternal props = sensor.impl + .getSensorProperties(getContext().getOpPackageName()); + final boolean supportsChallengelessHat = + props.resetLockoutRequiresHardwareAuthToken + && !props.resetLockoutRequiresChallenge; + final boolean doesNotRequireHat = !props.resetLockoutRequiresHardwareAuthToken; + + if (supportsChallengelessHat || doesNotRequireHat) { + Slog.d(TAG, "resetLockout from: " + fromSensorId + + ", for: " + sensor.id + + ", userId: " + userId); + sensor.impl.resetLockout(token, opPackageName, userId, + hardwareAuthToken); + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + } + + @Override // Binder call public int getCurrentStrength(int sensorId) { checkInternalPermission(); @@ -1294,6 +1333,16 @@ public class BiometricService extends SystemService { } } + @Nullable + private BiometricSensor getSensorForId(int sensorId) { + for (BiometricSensor sensor : mSensors) { + if (sensor.id == sensorId) { + return sensor; + } + } + return null; + } + private void dumpInternal(PrintWriter pw) { pw.println("Sensors:"); for (BiometricSensor sensor : mSensors) { diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index d9e21a83e45a..f4cb38738f7e 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -176,7 +176,8 @@ public class Utils { * @param requestedStrength the strength that it must meet * @return true only if the sensor is at least as strong as the requested strength */ - public static boolean isAtLeastStrength(int sensorStrength, int requestedStrength) { + public static boolean isAtLeastStrength(@Authenticators.Types int sensorStrength, + @Authenticators.Types int requestedStrength) { // Clear out any bits that are not reserved for biometric sensorStrength &= Authenticators.BIOMETRIC_MIN_STRENGTH; diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index b31a54b8b15e..9617bb09e153 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; +import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import android.os.RemoteException; @@ -50,6 +51,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> private final boolean mIsStrongBiometric; private final boolean mRequireConfirmation; private final ActivityTaskManager mActivityTaskManager; + private final BiometricManager mBiometricManager; @Nullable private final TaskStackListener mTaskStackListener; private final LockoutTracker mLockoutTracker; private final boolean mIsRestricted; @@ -73,6 +75,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> mOperationId = operationId; mRequireConfirmation = requireConfirmation; mActivityTaskManager = ActivityTaskManager.getInstance(); + mBiometricManager = context.getSystemService(BiometricManager.class); mTaskStackListener = taskStackListener; mLockoutTracker = lockoutTracker; mIsRestricted = restricted; @@ -207,6 +210,13 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> for (int i = 0; i < hardwareAuthToken.size(); i++) { byteToken[i] = hardwareAuthToken.get(i); } + + if (mIsStrongBiometric) { + mBiometricManager.resetLockoutTimeBound(getToken(), + getContext().getOpPackageName(), + getSensorId(), getTargetUserId(), byteToken); + } + if (isBiometricPrompt() && listener != null) { // BiometricService will add the token to keystore listener.onAuthenticationSucceeded(getSensorId(), identifier, byteToken, diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java index 06b049be4501..2926260321f1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java @@ -104,4 +104,11 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub { public long getAuthenticatorId(int callingUserId) throws RemoteException { return mFaceService.getAuthenticatorId(mSensorId, callingUserId); } + + @Override + public void resetLockout(IBinder token, String opPackageName, int userId, + byte[] hardwareAuthToken) throws RemoteException { + mFaceService.resetLockout(token, mSensorId, userId, hardwareAuthToken, + opPackageName); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 6dbd590df851..a74e2da30077 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -393,14 +393,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba final IFaceServiceReceiver receiver, final String opPackageName) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); - if (provider == null) { - Slog.w(TAG, "Null provider for removeAll"); - return; + for (ServiceProvider provider : mServiceProviders) { + List<FaceSensorPropertiesInternal> props = provider.getSensorProperties(); + for (FaceSensorPropertiesInternal prop : props) { + provider.scheduleRemoveAll(prop.sensorId, token, userId, receiver, + opPackageName); + } } - - provider.second.scheduleRemoveAll(provider.first, token, userId, receiver, - opPackageName); } @Override // Binder call diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index 3434acbf73cc..c7d2f0f87b6c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -495,6 +495,15 @@ public class Sensor { Slog.w(mTag, "setTestHalEnabled: " + enabled); if (enabled != mTestHalEnabled) { // The framework should retrieve a new session from the HAL. + try { + if (mCurrentSession != null && mCurrentSession.mSession != null) { + // TODO(181984005): This should be scheduled instead of directly invoked + Slog.d(mTag, "Closing old session"); + mCurrentSession.mSession.close(888 /* cookie */); + } + } catch (RemoteException e) { + Slog.e(mTag, "RemoteException", e); + } mCurrentSession = null; } mTestHalEnabled = enabled; @@ -519,6 +528,11 @@ public class Sensor { proto.end(userToken); } + proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN, + mSensorProperties.resetLockoutRequiresHardwareAuthToken); + proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE, + mSensorProperties.resetLockoutRequiresChallenge); + proto.end(sensorToken); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index afe7f24edeaf..40c050f4838b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -798,6 +798,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { proto.end(userToken); } + proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN, + mSensorProperties.resetLockoutRequiresHardwareAuthToken); + proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE, + mSensorProperties.resetLockoutRequiresChallenge); + proto.end(sensorToken); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java index 4cdb68df70af..84aa6d9ad1f8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/TestHal.java @@ -23,11 +23,11 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback; import android.hardware.biometrics.face.V1_0.OptionalBool; import android.hardware.biometrics.face.V1_0.OptionalUint64; import android.hardware.biometrics.face.V1_0.Status; -import android.os.NativeHandle; import android.os.RemoteException; import android.util.Slog; import java.util.ArrayList; +import java.util.Arrays; public class TestHal extends IBiometricsFace.Stub { private static final String TAG = "face.hidl.TestHal"; @@ -107,8 +107,12 @@ public class TestHal extends IBiometricsFace.Stub { } @Override - public int remove(int faceId) { + public int remove(int faceId) throws RemoteException { Slog.w(TAG, "remove"); + if (mCallback != null) { + mCallback.onRemoved(0 /* deviceId */, new ArrayList<Integer>(Arrays.asList(faceId)), + 0 /* userId */); + } return 0; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java index 32e9409de4b2..9e82ffcfadc6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java @@ -105,4 +105,11 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub public long getAuthenticatorId(int callingUserId) throws RemoteException { return mFingerprintService.getAuthenticatorId(mSensorId, callingUserId); } + + @Override + public void resetLockout(IBinder token, String opPackageName, int userId, + byte[] hardwareAuthToken) throws RemoteException { + mFingerprintService.resetLockout(token, mSensorId, userId, hardwareAuthToken, + opPackageName); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 396dd5f42d4d..3f489e98d5f1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -506,13 +506,13 @@ public class FingerprintService extends SystemService implements BiometricServic final IFingerprintServiceReceiver receiver, final String opPackageName) { Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); - if (provider == null) { - Slog.w(TAG, "Null provider for removeAll"); - return; + for (ServiceProvider provider : mServiceProviders) { + List<FingerprintSensorPropertiesInternal> props = provider.getSensorProperties(); + for (FingerprintSensorPropertiesInternal prop : props) { + provider.scheduleRemoveAll(prop.sensorId, token, receiver, userId, + opPackageName); + } } - provider.second.scheduleRemoveAll(provider.first, token, receiver, userId, - opPackageName); } @Override // Binder call diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index a98e7db43f79..b9dee7d1e321 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -475,6 +475,15 @@ class Sensor { Slog.w(mTag, "setTestHalEnabled: " + enabled); if (enabled != mTestHalEnabled) { // The framework should retrieve a new session from the HAL. + try { + if (mCurrentSession != null && mCurrentSession.mSession != null) { + // TODO(181984005): This should be scheduled instead of directly invoked + Slog.d(mTag, "Closing old session"); + mCurrentSession.mSession.close(999 /* cookie */); + } + } catch (RemoteException e) { + Slog.e(mTag, "RemoteException", e); + } mCurrentSession = null; } mTestHalEnabled = enabled; @@ -499,6 +508,11 @@ class Sensor { proto.end(userToken); } + proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN, + mSensorProperties.resetLockoutRequiresHardwareAuthToken); + proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE, + mSensorProperties.resetLockoutRequiresChallenge); + proto.end(sensorToken); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 50fdc2e8a856..e737677a7f53 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -527,7 +527,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider // Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler // thread. mHandler.post(() -> { - mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId); + final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext, + userId, mContext.getOpPackageName(), sensorId, mLockoutTracker); + mScheduler.scheduleClientMonitor(client); }); } @@ -759,6 +761,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider proto.end(userToken); } + proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN, + mSensorProperties.resetLockoutRequiresHardwareAuthToken); + proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE, + mSensorProperties.resetLockoutRequiresChallenge); + proto.end(sensorToken); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java new file mode 100644 index 000000000000..a39f4f8c4d7e --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint.hidl; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.BiometricsProtoEnums; + +import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.sensors.BaseClientMonitor; + +/** + * Clears lockout, which is handled in the framework (and not the HAL) for the + * IBiometricsFingerprint@2.1 interface. + */ +public class FingerprintResetLockoutClient extends BaseClientMonitor { + + @NonNull final LockoutFrameworkImpl mLockoutTracker; + + public FingerprintResetLockoutClient(@NonNull Context context, int userId, + @NonNull String owner, int sensorId, @NonNull LockoutFrameworkImpl lockoutTracker) { + super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */, + sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + mLockoutTracker = lockoutTracker; + } + + @Override + public void start(@NonNull Callback callback) { + super.start(callback); + mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, + getTargetUserId()); + callback.onClientFinished(this, true /* success */); + } + + @Override + public int getProtoEnum() { + return BiometricsProto.CM_RESET_LOCKOUT; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java index 14fdb507b0b1..129f6a61df04 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java @@ -93,8 +93,11 @@ public class TestHal extends IBiometricsFingerprint.Stub { } @Override - public int remove(int gid, int fid) { + public int remove(int gid, int fid) throws RemoteException { Slog.w(TAG, "Remove"); + if (mCallback != null) { + mCallback.onRemoved(0 /* deviceId */, fid, gid, 0 /* remaining */); + } return 0; } diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java index f44e0691bb9d..95915b7809f3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java @@ -95,4 +95,9 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub { public long getAuthenticatorId(int callingUserId) throws RemoteException { return 0; } + + @Override + public void resetLockout(IBinder token, String opPackageName, int userId, + byte[] hardwareAuthToken) throws RemoteException { + } }
\ No newline at end of file diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index 702434ba07b7..ffeb77d1d109 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/services/core/java/com/android/server/connectivity/DnsManager.java @@ -18,15 +18,15 @@ package com.android.server.connectivity; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; +import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_MAX_SAMPLES; +import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_MIN_SAMPLES; +import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS; +import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER; import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE; import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS; -import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES; -import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES; -import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS; -import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT; -import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE; -import static android.provider.Settings.Global.PRIVATE_DNS_MODE; -import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; import android.annotation.NonNull; import android.content.ContentResolver; diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java index 8b9c83678777..f8833071d1bf 100644 --- a/services/core/java/com/android/server/connectivity/ProxyTracker.java +++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java @@ -16,10 +16,10 @@ package com.android.server.connectivity; -import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST; -import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_HOST; -import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PAC; -import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PORT; +import static android.net.ConnectivitySettingsManager.GLOBAL_HTTP_PROXY_EXCLUSION_LIST; +import static android.net.ConnectivitySettingsManager.GLOBAL_HTTP_PROXY_HOST; +import static android.net.ConnectivitySettingsManager.GLOBAL_HTTP_PROXY_PAC; +import static android.net.ConnectivitySettingsManager.GLOBAL_HTTP_PROXY_PORT; import static android.provider.Settings.Global.HTTP_PROXY; import android.annotation.NonNull; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java index 7e5e427cf0a1..8e84002dc655 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java @@ -54,7 +54,7 @@ final class InputMethodSubtypeSwitchingController { public final boolean mIsSystemLocale; public final boolean mIsSystemLanguage; - public ImeSubtypeListItem(CharSequence imeName, CharSequence subtypeName, + ImeSubtypeListItem(CharSequence imeName, CharSequence subtypeName, InputMethodInfo imi, int subtypeId, String subtypeLocale, String systemLocale) { mImeName = imeName; mSubtypeName = subtypeName; @@ -71,8 +71,8 @@ final class InputMethodSubtypeSwitchingController { // TODO: Use Locale#getLanguage or Locale#toLanguageTag final String systemLanguage = parseLanguageFromLocaleString(systemLocale); final String subtypeLanguage = parseLanguageFromLocaleString(subtypeLocale); - mIsSystemLanguage = systemLanguage.length() >= 2 && - systemLanguage.equals(subtypeLanguage); + mIsSystemLanguage = systemLanguage.length() >= 2 + && systemLanguage.equals(subtypeLanguage); } } } @@ -158,7 +158,7 @@ final class InputMethodSubtypeSwitchingController { return true; } if (o instanceof ImeSubtypeListItem) { - final ImeSubtypeListItem that = (ImeSubtypeListItem)o; + final ImeSubtypeListItem that = (ImeSubtypeListItem) o; return Objects.equals(this.mImi, that.mImi) && this.mSubtypeId == that.mSubtypeId; } return false; @@ -172,7 +172,7 @@ final class InputMethodSubtypeSwitchingController { private final String mSystemLocaleStr; private final InputMethodSettings mSettings; - public InputMethodAndSubtypeList(Context context, InputMethodSettings settings) { + InputMethodAndSubtypeList(Context context, InputMethodSettings settings) { mContext = context; mSettings = settings; mPm = context.getPackageManager(); @@ -244,7 +244,7 @@ final class InputMethodSubtypeSwitchingController { private static class StaticRotationList { private final List<ImeSubtypeListItem> mImeSubtypeList; - public StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList) { + StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList) { mImeSubtypeList = imeSubtypeList; } @@ -257,8 +257,8 @@ final class InputMethodSubtypeSwitchingController { */ private int getIndex(InputMethodInfo imi, InputMethodSubtype subtype) { final int currentSubtypeId = calculateSubtypeId(imi, subtype); - final int N = mImeSubtypeList.size(); - for (int i = 0; i < N; ++i) { + final int numSubtypes = mImeSubtypeList.size(); + for (int i = 0; i < numSubtypes; ++i) { final ImeSubtypeListItem isli = mImeSubtypeList.get(i); // Skip until the current IME/subtype is found. if (imi.equals(isli.mImi) && isli.mSubtypeId == currentSubtypeId) { @@ -280,10 +280,10 @@ final class InputMethodSubtypeSwitchingController { if (currentIndex < 0) { return null; } - final int N = mImeSubtypeList.size(); - for (int offset = 1; offset < N; ++offset) { + final int numSubtypes = mImeSubtypeList.size(); + for (int offset = 1; offset < numSubtypes; ++offset) { // Start searching the next IME/subtype from the next of the current index. - final int candidateIndex = (currentIndex + offset) % N; + final int candidateIndex = (currentIndex + offset) % numSubtypes; final ImeSubtypeListItem candidate = mImeSubtypeList.get(candidateIndex); // Skip if searching inside the current IME only, but the candidate is not // the current IME. @@ -296,8 +296,8 @@ final class InputMethodSubtypeSwitchingController { } protected void dump(final Printer pw, final String prefix) { - final int N = mImeSubtypeList.size(); - for (int i = 0; i < N; ++i) { + final int numSubtypes = mImeSubtypeList.size(); + for (int i = 0; i < numSubtypes; ++i) { final int rank = i; final ImeSubtypeListItem item = mImeSubtypeList.get(i); pw.println(prefix + "rank=" + rank + " item=" + item); @@ -313,8 +313,8 @@ final class InputMethodSubtypeSwitchingController { private DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) { mImeSubtypeList = imeSubtypeListItems; mUsageHistoryOfSubtypeListItemIndex = new int[mImeSubtypeList.size()]; - final int N = mImeSubtypeList.size(); - for (int i = 0; i < N; i++) { + final int numSubtypes = mImeSubtypeList.size(); + for (int i = 0; i < numSubtypes; i++) { mUsageHistoryOfSubtypeListItemIndex[i] = i; } } @@ -328,13 +328,13 @@ final class InputMethodSubtypeSwitchingController { */ private int getUsageRank(final InputMethodInfo imi, InputMethodSubtype subtype) { final int currentSubtypeId = calculateSubtypeId(imi, subtype); - final int N = mUsageHistoryOfSubtypeListItemIndex.length; - for (int usageRank = 0; usageRank < N; usageRank++) { + final int numItems = mUsageHistoryOfSubtypeListItemIndex.length; + for (int usageRank = 0; usageRank < numItems; usageRank++) { final int subtypeListItemIndex = mUsageHistoryOfSubtypeListItemIndex[usageRank]; final ImeSubtypeListItem subtypeListItem = mImeSubtypeList.get(subtypeListItemIndex); - if (subtypeListItem.mImi.equals(imi) && - subtypeListItem.mSubtypeId == currentSubtypeId) { + if (subtypeListItem.mImi.equals(imi) + && subtypeListItem.mSubtypeId == currentSubtypeId) { return usageRank; } } @@ -363,9 +363,9 @@ final class InputMethodSubtypeSwitchingController { } return null; } - final int N = mUsageHistoryOfSubtypeListItemIndex.length; - for (int i = 1; i < N; i++) { - final int subtypeListItemRank = (currentUsageRank + i) % N; + final int numItems = mUsageHistoryOfSubtypeListItemIndex.length; + for (int i = 1; i < numItems; i++) { + final int subtypeListItemRank = (currentUsageRank + i) % numItems; final int subtypeListItemIndex = mUsageHistoryOfSubtypeListItemIndex[subtypeListItemRank]; final ImeSubtypeListItem subtypeListItem = @@ -399,9 +399,10 @@ final class InputMethodSubtypeSwitchingController { final List<ImeSubtypeListItem> switchingAwareImeSubtypes = filterImeSubtypeList(sortedEnabledItems, true /* supportsSwitchingToNextInputMethod */); - if (currentInstance != null && - currentInstance.mSwitchingAwareRotationList != null && - Objects.equals(currentInstance.mSwitchingAwareRotationList.mImeSubtypeList, + if (currentInstance != null + && currentInstance.mSwitchingAwareRotationList != null + && Objects.equals( + currentInstance.mSwitchingAwareRotationList.mImeSubtypeList, switchingAwareImeSubtypes)) { // Can reuse the current instance. switchingAwareRotationList = currentInstance.mSwitchingAwareRotationList; @@ -415,9 +416,9 @@ final class InputMethodSubtypeSwitchingController { { final List<ImeSubtypeListItem> switchingUnawareImeSubtypes = filterImeSubtypeList( sortedEnabledItems, false /* supportsSwitchingToNextInputMethod */); - if (currentInstance != null && - currentInstance.mSwitchingUnawareRotationList != null && - Objects.equals( + if (currentInstance != null + && currentInstance.mSwitchingUnawareRotationList != null + && Objects.equals( currentInstance.mSwitchingUnawareRotationList.mImeSubtypeList, switchingUnawareImeSubtypes)) { // Can reuse the current instance. @@ -465,11 +466,11 @@ final class InputMethodSubtypeSwitchingController { final List<ImeSubtypeListItem> items, final boolean supportsSwitchingToNextInputMethod) { final ArrayList<ImeSubtypeListItem> result = new ArrayList<>(); - final int ALL_ITEMS_COUNT = items.size(); - for (int i = 0; i < ALL_ITEMS_COUNT; i++) { + final int numItems = items.size(); + for (int i = 0; i < numItems; i++) { final ImeSubtypeListItem item = items.get(i); - if (item.mImi.supportsSwitchingToNextInputMethod() == - supportsSwitchingToNextInputMethod) { + if (item.mImi.supportsSwitchingToNextInputMethod() + == supportsSwitchingToNextInputMethod) { result.add(item); } } diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java index c8c212b3109c..659874441c97 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java @@ -25,6 +25,9 @@ import android.Manifest; import android.annotation.Nullable; import android.app.AppOpsManager; import android.app.PendingIntent; +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.content.Context; import android.content.Intent; import android.hardware.contexthub.V1_0.ContextHubMsg; @@ -38,6 +41,7 @@ import android.hardware.location.IContextHubTransactionCallback; import android.hardware.location.NanoAppMessage; import android.hardware.location.NanoAppState; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; @@ -113,6 +117,14 @@ public class ContextHubClientBroker extends IContextHubClient.Stub */ private static final String RECEIVE_MSG_NOTE = "NanoappMessageDelivery "; + /** + * For clients targeting S and above, a SecurityException is thrown when they are in the denied + * authorization state and attempt to send a message to a nanoapp. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + private static final long CHANGE_ID_AUTH_STATE_DENIED = 181350407L; + /* * The context of the service. */ @@ -351,6 +363,8 @@ public class ContextHubClientBroker extends IContextHubClient.Stub * * @param message the message to send * @return the error code of sending the message + * @throws SecurityException if this client doesn't have permissions to send a message to the + * nanoapp */ @ContextHubTransaction.Result @Override @@ -362,7 +376,13 @@ public class ContextHubClientBroker extends IContextHubClient.Stub int authState = mMessageChannelNanoappIdMap.getOrDefault( message.getNanoAppId(), AUTHORIZATION_UNKNOWN); if (authState == AUTHORIZATION_DENIED) { - return ContextHubTransaction.RESULT_FAILED_PERMISSION_DENIED; + if (Compatibility.isChangeEnabled(CHANGE_ID_AUTH_STATE_DENIED)) { + throw new SecurityException("Client doesn't have valid permissions to send" + + " message to " + message.getNanoAppId()); + } + // Return a bland error code for apps targeting old SDKs since they wouldn't be able + // to use an error code added in S. + return ContextHubTransaction.RESULT_FAILED_UNKNOWN; } else if (authState == AUTHORIZATION_UNKNOWN) { // Only check permissions the first time a nanoapp is queried since nanoapp // permissions don't currently change at runtime. If the host permission changes @@ -375,7 +395,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub int contextHubId = mAttachedContextHubInfo.getId(); try { - result = mContextHubProxy.sendMessageToHub(contextHubId, messageToNanoApp); + result = mContextHubProxy.getHub().sendMessageToHub(contextHubId, messageToNanoApp); } catch (RemoteException e) { Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = " + contextHubId + ")", e); diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java index 3a5c220eeeae..3245fdfaebd6 100644 --- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java @@ -104,12 +104,6 @@ public abstract class IContextHubWrapper { int hubId, IContexthubCallback callback) throws RemoteException; /** - * Calls the appropriate sendMessageToHub function depending on the HAL version. - */ - public abstract int sendMessageToHub(int hubId, - android.hardware.contexthub.V1_0.ContextHubMsg message) throws RemoteException; - - /** * @return A valid instance of Contexthub HAL 1.0. */ public abstract android.hardware.contexthub.V1_0.IContexthub getHub(); @@ -180,11 +174,6 @@ public abstract class IContextHubWrapper { mHub.registerCallback(hubId, callback); } - public int sendMessageToHub(int hubId, - android.hardware.contexthub.V1_0.ContextHubMsg message) throws RemoteException { - return mHub.sendMessageToHub(hubId, message); - } - public android.hardware.contexthub.V1_0.IContexthub getHub() { return mHub; } @@ -234,11 +223,6 @@ public abstract class IContextHubWrapper { mHub.registerCallback(hubId, callback); } - public int sendMessageToHub(int hubId, - android.hardware.contexthub.V1_0.ContextHubMsg message) throws RemoteException { - return mHub.sendMessageToHub(hubId, message); - } - public android.hardware.contexthub.V1_0.IContexthub getHub() { return mHub; } @@ -304,14 +288,6 @@ public abstract class IContextHubWrapper { mHub.registerCallback_1_2(hubId, callback); } - public int sendMessageToHub(int hubId, - android.hardware.contexthub.V1_0.ContextHubMsg message) throws RemoteException { - android.hardware.contexthub.V1_2.ContextHubMsg newMessage = - new android.hardware.contexthub.V1_2.ContextHubMsg(); - newMessage.msg_1_0 = message; - return mHub.sendMessageToHub_1_2(hubId, newMessage); - } - public android.hardware.contexthub.V1_0.IContexthub getHub() { return mHub; } diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java index 6d250ecb9fa4..6201b9414aaf 100644 --- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java +++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java @@ -119,19 +119,11 @@ public class BiometricDeferredQueue { for (UserAuthInfo userAuthInfo : pendingResetLockuts) { Slog.d(TAG, "Resetting face lockout for sensor: " + sensorId + ", user: " + userAuthInfo.userId); - final VerifyCredentialResponse response = spManager.verifyChallengeInternal( - getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge, - userAuthInfo.userId); - if (response == null) { - Slog.wtf(TAG, "VerifyChallenge failed, null response"); - continue; + final byte[] hat = requestHatFromGatekeeperPassword(spManager, userAuthInfo, + challenge); + if (hat != null) { + faceManager.resetLockout(sensorId, userAuthInfo.userId, hat); } - if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) { - Slog.wtf(TAG, "VerifyChallenge failed, response: " - + response.getResponseCode()); - } - faceManager.resetLockout(sensorId, userAuthInfo.userId, - response.getGatekeeperHAT()); } sensorIds.remove(sensorId); @@ -146,15 +138,6 @@ public class BiometricDeferredQueue { finishCallback.onFinished(); } } - - synchronized IGateKeeperService getGatekeeperService() { - final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE); - if (service == null) { - Slog.e(TAG, "Unable to acquire GateKeeperService"); - return null; - } - return IGateKeeperService.Stub.asInterface(service); - } } @Nullable private FaceResetLockoutTask mFaceResetLockoutTask; @@ -214,10 +197,19 @@ public class BiometricDeferredQueue { mFingerprintManager.resetLockout(prop.sensorId, user.userId, null /* hardwareAuthToken */); } + } else if (!prop.resetLockoutRequiresChallenge) { + for (UserAuthInfo user : pendingResetLockouts) { + Slog.d(TAG, "Resetting fingerprint lockout for sensor: " + prop.sensorId + + ", user: " + user.userId); + final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user, + 0 /* challenge */); + if (hat != null) { + mFingerprintManager.resetLockout(prop.sensorId, user.userId, hat); + } + } } else { - Slog.e(TAG, "Fingerprint resetLockout with HAT not supported yet"); - // TODO(b/152414803): Implement this when resetLockout is implemented below - // the framework. + Slog.w(TAG, "No fingerprint HAL interface requires HAT with challenge" + + ", sensorId: " + prop.sensorId); } } } @@ -228,11 +220,6 @@ public class BiometricDeferredQueue { * in-flight challenge, we generate a single challenge to reset lockout for all profiles. This * hopefully reduces/eliminates issues such as overwritten challenge, incorrectly revoked * challenge, or other race conditions. - * - * TODO(b/162965646) This logic can be avoided if multiple in-flight challenges are supported. - * Though it will need to continue to exist to support existing HIDLs, each profile that - * requires resetLockout could have its own challenge, and the `mPendingResetLockouts` queue - * can be avoided. */ private void processPendingLockoutsForFace(List<UserAuthInfo> pendingResetLockouts) { if (mFaceManager != null) { @@ -251,10 +238,60 @@ public class BiometricDeferredQueue { mFaceResetLockoutTask = new FaceResetLockoutTask(mFaceFinishCallback, mFaceManager, mSpManager, sensorIds, pendingResetLockouts); for (final FaceSensorPropertiesInternal prop : faceSensorProperties) { - // Generate a challenge for each sensor. The challenge does not need to be - // per-user, since the HAT returned by gatekeeper contains userId. - mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask); + if (prop.resetLockoutRequiresHardwareAuthToken) { + if (prop.resetLockoutRequiresChallenge) { + // Generate a challenge for each sensor. The challenge does not need to be + // per-user, since the HAT returned by gatekeeper contains userId. + mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask); + } else { + for (UserAuthInfo user : pendingResetLockouts) { + Slog.d(TAG, "Resetting face lockout for sensor: " + prop.sensorId + + ", user: " + user.userId); + final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user, + 0 /* challenge */); + if (hat != null) { + mFaceManager.resetLockout(prop.sensorId, user.userId, hat); + } + } + } + } else { + Slog.w(TAG, "Lockout is below the HAL for all face authentication interfaces" + + ", sensorId: " + prop.sensorId); + } } } } + + @Nullable + private static byte[] requestHatFromGatekeeperPassword( + @NonNull SyntheticPasswordManager spManager, + @NonNull UserAuthInfo userAuthInfo, long challenge) { + final VerifyCredentialResponse response = spManager.verifyChallengeInternal( + getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge, + userAuthInfo.userId); + if (response == null) { + Slog.wtf(TAG, "VerifyChallenge failed, null response"); + return null; + } + if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) { + Slog.wtf(TAG, "VerifyChallenge failed, response: " + + response.getResponseCode()); + return null; + } + if (response.getGatekeeperHAT() == null) { + Slog.e(TAG, "Null HAT received from spManager"); + } + + return response.getGatekeeperHAT(); + } + + @Nullable + private static synchronized IGateKeeperService getGatekeeperService() { + final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE); + if (service == null) { + Slog.e(TAG, "Unable to acquire GateKeeperService"); + return null; + } + return IGateKeeperService.Stub.asInterface(service); + } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 43c736544ee6..22a9a47c9651 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -91,7 +91,6 @@ import android.provider.Settings.SettingNotFoundException; import android.security.AndroidKeyStoreMaintenance; import android.security.Authorization; import android.security.KeyStore; -import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; import android.security.keystore.UserNotAuthenticatedException; @@ -158,7 +157,6 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; -import java.util.Optional; import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -263,13 +261,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public void onStart() { - Optional<Boolean> keystore2_enabled = - android.sysprop.Keystore2Properties.keystore2_enabled(); - if (keystore2_enabled.isPresent() && keystore2_enabled.get()) { - android.security.keystore2.AndroidKeyStoreProvider.install(); - } else { - AndroidKeyStoreProvider.install(); - } + android.security.keystore2.AndroidKeyStoreProvider.install(); mLockSettingsService = new LockSettingsService(getContext()); publishBinderService("lock_settings", mLockSettingsService); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index da62aca70cd3..0fa6283cb59a 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -58,6 +58,23 @@ import static android.net.NetworkIdentity.OEM_NONE; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; +import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_MASK; +import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_USER_EXEMPTED; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_ALLOWLIST; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM; +import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_ADMIN_DISABLED; +import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_DATA_SAVER; +import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_MASK; +import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.NetworkPolicyManager.BLOCKED_REASON_APP_STANDBY; +import static android.net.NetworkPolicyManager.BLOCKED_REASON_BATTERY_SAVER; +import static android.net.NetworkPolicyManager.BLOCKED_REASON_DOZE; +import static android.net.NetworkPolicyManager.BLOCKED_REASON_NONE; +import static android.net.NetworkPolicyManager.BLOCKED_REASON_RESTRICTED_MODE; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS; @@ -246,6 +263,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.StatLogger; import com.android.internal.util.XmlUtils; import com.android.net.module.util.NetworkIdentityUtils; +import com.android.net.module.util.PermissionUtils; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.ServiceThread; @@ -413,6 +431,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18; private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19; private static final int MSG_STATS_PROVIDER_LIMIT_REACHED = 20; + // TODO: Add similar docs for other messages. + /** + * Message to indicate that reasons for why an uid is blocked changed. + * arg1 = uid + * arg2 = oldBlockedReasons + * obj = newBlockedReasons + */ + private static final int MSG_BLOCKED_REASON_CHANGED = 21; private static final int UID_MSG_STATE_CHANGED = 100; private static final int UID_MSG_GONE = 101; @@ -559,7 +585,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Foreground at UID granularity. */ @GuardedBy("mUidRulesFirstLock") - final SparseArray<UidState> mUidState = new SparseArray<UidState>(); + private final SparseArray<UidState> mUidState = new SparseArray<>(); + + @GuardedBy("mUidRulesFirstLock") + private final SparseArray<UidBlockedState> mUidBlockedState = new SparseArray<>(); /** Map from network ID to last observed meteredness state */ @GuardedBy("mNetworkPoliciesSecondLock") @@ -2876,15 +2905,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override - public void registerListener(INetworkPolicyListener listener) { + public void registerListener(@NonNull INetworkPolicyListener listener) { + Objects.requireNonNull(listener); // TODO: Remove CONNECTIVITY_INTERNAL and the *AnyPermissionOf methods above after all apps // have declared OBSERVE_NETWORK_POLICY. enforceAnyPermissionOf(CONNECTIVITY_INTERNAL, OBSERVE_NETWORK_POLICY); mListeners.register(listener); + // TODO: Send callbacks to the newly registered listener } @Override - public void unregisterListener(INetworkPolicyListener listener) { + public void unregisterListener(@NonNull INetworkPolicyListener listener) { + Objects.requireNonNull(listener); // TODO: Remove CONNECTIVITY_INTERNAL and the *AnyPermissionOf methods above after all apps // have declared OBSERVE_NETWORK_POLICY. enforceAnyPermissionOf(CONNECTIVITY_INTERNAL, OBSERVE_NETWORK_POLICY); @@ -3078,8 +3110,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public int getRestrictBackgroundByCaller() { mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); - final int uid = Binder.getCallingUid(); + return getRestrictBackgroundStatusInternal(Binder.getCallingUid()); + } + @Override + public int getRestrictBackgroundStatus(int uid) { + PermissionUtils.enforceNetworkStackPermission(mContext); + return getRestrictBackgroundStatusInternal(uid); + } + + private int getRestrictBackgroundStatusInternal(int uid) { synchronized (mUidRulesFirstLock) { // Must clear identity because getUidPolicy() is restricted to system. final long token = Binder.clearCallingIdentity(); @@ -3548,6 +3588,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Get multipath preference value for the given network. */ public int getMultipathPreference(Network network) { + PermissionUtils.enforceNetworkStackPermission(mContext); final Integer preference = mMultipathPolicyTracker.getMultipathPreference(network); if (preference != null) { return preference; @@ -3921,6 +3962,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidRules.put(uid, newUidRule); mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget(); } + updateBlockedReasonsForRestrictedModeUL(uid); }); if (mRestrictedNetworkingMode) { // firewall rules only need to be set when this mode is being enabled. @@ -3941,6 +3983,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidRules.put(uid, newUidRule); mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget(); } + updateBlockedReasonsForRestrictedModeUL(uid); // if restricted networking mode is on, and the app has an access exemption, the uid rule // will not change, but the firewall rule will have to be updated. @@ -3952,6 +3995,31 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private void updateBlockedReasonsForRestrictedModeUL(int uid) { + UidBlockedState uidBlockedState = mUidBlockedState.get(uid); + if (uidBlockedState == null) { + uidBlockedState = new UidBlockedState(); + mUidBlockedState.put(uid, uidBlockedState); + } + final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; + if (mRestrictedNetworkingMode) { + uidBlockedState.blockedReasons |= BLOCKED_REASON_RESTRICTED_MODE; + } else { + uidBlockedState.blockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE; + } + if (hasRestrictedModeAccess(uid)) { + uidBlockedState.allowedReasons |= ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS; + } else { + uidBlockedState.allowedReasons &= ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS; + } + uidBlockedState.updateEffectiveBlockedReasons(); + if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) { + mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid, + uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons) + .sendToTarget(); + } + } + private int getNewRestrictedModeUidRule(int uid, int oldUidRule) { int newRule = oldUidRule; newRule &= ~MASK_RESTRICTED_MODE_NETWORKS; @@ -4072,11 +4140,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId); if (!deviceIdleMode) { - isWhitelisted = isWhitelisted || mPowerSaveWhitelistExceptIdleAppIds.get(appId); + isWhitelisted = isWhitelisted || isWhitelistedFromPowerSaveExceptIdleUL(uid); } return isWhitelisted; } + /** + * Returns whether a uid is allowlisted from power saving restrictions, except Device idle + * (eg: Battery Saver and app idle). + */ + @GuardedBy("mUidRulesFirstLock") + private boolean isWhitelistedFromPowerSaveExceptIdleUL(int uid) { + final int appId = UserHandle.getAppId(uid); + return mPowerSaveWhitelistExceptIdleAppIds.get(appId); + } + // NOTE: since both fw_dozable and fw_powersave uses the same map // (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method. @GuardedBy("mUidRulesFirstLock") @@ -4521,6 +4599,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int oldUidRules = mUidRules.get(uid, RULE_NONE); final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid); final boolean isRestrictedByAdmin = isRestrictedByAdminUL(uid); + UidBlockedState uidBlockedState = mUidBlockedState.get(uid); + if (uidBlockedState == null) { + uidBlockedState = new UidBlockedState(); + mUidBlockedState.put(uid, uidBlockedState); + } final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0; final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0; @@ -4545,6 +4628,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + int newBlockedReasons = BLOCKED_REASON_NONE; + int newAllowedReasons = ALLOWED_REASON_NONE; + newBlockedReasons |= (isRestrictedByAdmin ? BLOCKED_METERED_REASON_ADMIN_DISABLED : 0); + newBlockedReasons |= (mRestrictBackground ? BLOCKED_METERED_REASON_DATA_SAVER : 0); + newBlockedReasons |= (isDenied ? BLOCKED_METERED_REASON_USER_RESTRICTED : 0); + + newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0); + newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0); + newAllowedReasons |= (isAllowed ? ALLOWED_METERED_REASON_USER_EXEMPTED : 0); + if (LOGV) { Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")" + ": isForeground=" +isForeground @@ -4616,6 +4709,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Dispatch changed rule to existing listeners. mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); + + final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; + uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons + & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons; + uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons + & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons; + uidBlockedState.updateEffectiveBlockedReasons(); + if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) { + mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid, + uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons) + .sendToTarget(); + } } } @@ -4690,6 +4795,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Copy existing uid rules and clear ALL_NETWORK rules. int newUidRules = oldUidRules & (~MASK_ALL_NETWORKS); + UidBlockedState uidBlockedState = mUidBlockedState.get(uid); + if (uidBlockedState == null) { + uidBlockedState = new UidBlockedState(); + mUidBlockedState.put(uid, uidBlockedState); + } + // First step: define the new rule based on user restrictions and foreground state. // NOTE: if statements below could be inlined, but it's easier to understand the logic @@ -4702,6 +4813,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { newUidRules |= isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL; } + int newBlockedReasons = BLOCKED_REASON_NONE; + int newAllowedReasons = ALLOWED_REASON_NONE; + newBlockedReasons |= (mRestrictPower ? BLOCKED_REASON_BATTERY_SAVER : 0); + newBlockedReasons |= (mDeviceIdleMode ? BLOCKED_REASON_DOZE : 0); + newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0); + newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE); + + newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0); + newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0); + newAllowedReasons |= (isWhitelistedFromPowerSaveUL(uid, true) + ? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0); + newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid) + ? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0); + if (LOGV) { Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")" + ", isIdle: " + isUidIdle @@ -4733,6 +4858,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); } + final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; + uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons + & BLOCKED_METERED_REASON_MASK) | newBlockedReasons; + uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons + & ALLOWED_METERED_REASON_MASK) | newAllowedReasons; + uidBlockedState.updateEffectiveBlockedReasons(); + if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) { + mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid, + uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons) + .sendToTarget(); + } + return newUidRules; } @@ -4762,61 +4899,57 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) { - if (listener != null) { - try { - listener.onUidRulesChanged(uid, uidRules); - } catch (RemoteException ignored) { - } + try { + listener.onUidRulesChanged(uid, uidRules); + } catch (RemoteException ignored) { } } private void dispatchMeteredIfacesChanged(INetworkPolicyListener listener, String[] meteredIfaces) { - if (listener != null) { - try { - listener.onMeteredIfacesChanged(meteredIfaces); - } catch (RemoteException ignored) { - } + try { + listener.onMeteredIfacesChanged(meteredIfaces); + } catch (RemoteException ignored) { } } private void dispatchRestrictBackgroundChanged(INetworkPolicyListener listener, boolean restrictBackground) { - if (listener != null) { - try { - listener.onRestrictBackgroundChanged(restrictBackground); - } catch (RemoteException ignored) { - } + try { + listener.onRestrictBackgroundChanged(restrictBackground); + } catch (RemoteException ignored) { } } private void dispatchUidPoliciesChanged(INetworkPolicyListener listener, int uid, int uidPolicies) { - if (listener != null) { - try { - listener.onUidPoliciesChanged(uid, uidPolicies); - } catch (RemoteException ignored) { - } + try { + listener.onUidPoliciesChanged(uid, uidPolicies); + } catch (RemoteException ignored) { } } private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId, int overrideMask, int overrideValue, int[] networkTypes) { - if (listener != null) { - try { - listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes); - } catch (RemoteException ignored) { - } + try { + listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes); + } catch (RemoteException ignored) { } } private void dispatchSubscriptionPlansChanged(INetworkPolicyListener listener, int subId, SubscriptionPlan[] plans) { - if (listener != null) { - try { - listener.onSubscriptionPlansChanged(subId, plans); - } catch (RemoteException ignored) { - } + try { + listener.onSubscriptionPlansChanged(subId, plans); + } catch (RemoteException ignored) { + } + } + + private void dispatchBlockedReasonChanged(INetworkPolicyListener listener, int uid, + int oldBlockedReasons, int newBlockedReasons) { + try { + listener.onBlockedReasonChanged(uid, oldBlockedReasons, newBlockedReasons); + } catch (RemoteException ignored) { } } @@ -4973,6 +5106,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mListeners.finishBroadcast(); return true; } + case MSG_BLOCKED_REASON_CHANGED: { + final int uid = msg.arg1; + final int newBlockedReasons = msg.arg2; + final int oldBlockedReasons = (int) msg.obj; + final int length = mListeners.beginBroadcast(); + for (int i = 0; i < length; i++) { + final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); + dispatchBlockedReasonChanged(listener, uid, + oldBlockedReasons, newBlockedReasons); + } + mListeners.finishBroadcast(); + return true; + } default: { return false; } @@ -5704,6 +5850,51 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue; } + private class UidBlockedState { + public int blockedReasons; + public int allowedReasons; + public int effectiveBlockedReasons; + + UidBlockedState() { + blockedReasons = BLOCKED_REASON_NONE; + allowedReasons = ALLOWED_REASON_NONE; + effectiveBlockedReasons = BLOCKED_REASON_NONE; + } + + void updateEffectiveBlockedReasons() { + effectiveBlockedReasons = blockedReasons; + // If the uid is not subject to any blocked reasons, then return early + if (blockedReasons == BLOCKED_REASON_NONE) { + return; + } + if ((allowedReasons & ALLOWED_REASON_SYSTEM) != 0) { + effectiveBlockedReasons = BLOCKED_REASON_NONE; + } + if ((allowedReasons & ALLOWED_REASON_FOREGROUND) != 0) { + effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER; + effectiveBlockedReasons &= ~BLOCKED_REASON_DOZE; + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY; + effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER; + effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_USER_RESTRICTED; + } + if ((allowedReasons & ALLOWED_REASON_POWER_SAVE_ALLOWLIST) != 0) { + effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER; + effectiveBlockedReasons &= ~BLOCKED_REASON_DOZE; + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY; + } + if ((allowedReasons & ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST) != 0) { + effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER; + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY; + } + if ((allowedReasons & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS) != 0) { + effectiveBlockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE; + } + if ((allowedReasons & ALLOWED_METERED_REASON_USER_EXEMPTED) != 0) { + effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER; + } + } + } + private class NotificationId { private final String mTag; private final int mId; diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 8023fd42edfa..a7b9e95d44d1 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -25,7 +25,16 @@ import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.NetworkIdentity.OEM_PAID; +import static android.net.NetworkIdentity.OEM_PRIVATE; +import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; +import static android.net.NetworkStats.METERED_ALL; +import static android.net.NetworkStats.ROAMING_ALL; +import static android.net.NetworkTemplate.MATCH_ETHERNET; +import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD; +import static android.net.NetworkTemplate.MATCH_WIFI_WILDCARD; import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; +import static android.net.NetworkTemplate.OEM_MANAGED_ALL; import static android.net.NetworkTemplate.buildTemplateMobileWildcard; import static android.net.NetworkTemplate.buildTemplateMobileWithRatType; import static android.net.NetworkTemplate.buildTemplateWifiWildcard; @@ -435,6 +444,7 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER: + case FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER: synchronized (mDataBytesTransferLock) { return pullDataBytesTransferLocked(atomTag, data); } @@ -867,6 +877,8 @@ public class StatsPullAtomService extends SystemService { FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED)); mNetworkStatsBaselines.addAll( collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER)); + mNetworkStatsBaselines.addAll( + collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER)); // Listen to subscription changes to record historical subscriptions that activated before // pulling, this is used by {@code DATA_USAGE_BYTES_TRANSFER}. @@ -879,6 +891,7 @@ public class StatsPullAtomService extends SystemService { registerMobileBytesTransferBackground(); registerBytesTransferByTagAndMetered(); registerDataUsageBytesTransfer(); + registerOemManagedBytesTransfer(); } /** @@ -1057,7 +1070,7 @@ public class StatsPullAtomService extends SystemService { new int[] {TRANSPORT_WIFI, TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false, /*slicedByTag=*/true, /*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN, - /*subInfo=*/null)); + /*subInfo=*/null, OEM_MANAGED_ALL)); } break; } @@ -1067,6 +1080,10 @@ public class StatsPullAtomService extends SystemService { } break; } + case FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER: { + ret.addAll(getDataUsageBytesTransferSnapshotForOemManaged()); + break; + } default: throw new IllegalArgumentException("Unknown atomTag " + atomTag); } @@ -1094,7 +1111,7 @@ public class StatsPullAtomService extends SystemService { final NetworkStatsExt diff = new NetworkStatsExt( item.stats.subtract(baseline.stats).removeEmptyEntries(), item.transports, item.slicedByFgbg, item.slicedByTag, item.slicedByMetered, item.ratType, - item.subInfo); + item.subInfo, item.oemManaged); // If no diff, skip. if (diff.stats.size() == 0) continue; @@ -1106,6 +1123,9 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER: addDataUsageBytesTransferAtoms(diff, pulledData); break; + case FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER: + addOemDataUsageBytesTransferAtoms(diff, pulledData); + break; default: addNetworkStats(atomTag, pulledData, diff); } @@ -1177,6 +1197,49 @@ public class StatsPullAtomService extends SystemService { } } + private void addOemDataUsageBytesTransferAtoms(@NonNull NetworkStatsExt statsExt, + @NonNull List<StatsEvent> pulledData) { + final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling + final int oemManaged = statsExt.oemManaged; + for (final int transport : statsExt.transports) { + for (int i = 0; i < statsExt.stats.size(); i++) { + statsExt.stats.getValues(i, entry); + pulledData.add(FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER, entry.uid, (entry.set > 0), + oemManaged, transport, entry.rxBytes, entry.rxPackets, entry.txBytes, + entry.txPackets)); + } + } + } + + @NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() { + final int[] transports = new int[] {MATCH_ETHERNET, MATCH_MOBILE_WILDCARD, + MATCH_WIFI_WILDCARD}; + final int[] oemManagedTypes = new int[] {OEM_PAID | OEM_PRIVATE, OEM_PAID, OEM_PRIVATE}; + + final List<NetworkStatsExt> ret = new ArrayList<>(); + + for (final int transport : transports) { + for (final int oemManaged : oemManagedTypes) { + /* A null subscriberId will set wildcard=true, since we aren't trying to select a + specific ssid or subscriber. */ + final NetworkTemplate template = new NetworkTemplate(transport, + /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, + oemManaged); + final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, true); + if (stats != null) { + ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats), + new int[] {transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/true, + /*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN, + /*subInfo=*/null, oemManaged)); + } + } + } + + return ret; + } + /** * Create a snapshot of NetworkStats for a given transport. */ @@ -1229,7 +1292,8 @@ public class StatsPullAtomService extends SystemService { if (stats != null) { ret.add(new NetworkStatsExt(sliceNetworkStatsByFgbg(stats), new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true, - /*slicedByTag=*/false, /*slicedByMetered=*/false, ratType, subInfo)); + /*slicedByTag=*/false, /*slicedByMetered=*/false, ratType, subInfo, + OEM_MANAGED_ALL)); } } return ret; @@ -1374,6 +1438,19 @@ public class StatsPullAtomService extends SystemService { ); } + private void registerOemManagedBytesTransfer() { + int tagId = FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {5, 6, 7, 8}) + .build(); + mStatsManager.setPullAtomCallback( + tagId, + metadata, + BackgroundThread.getExecutor(), + mStatsCallbackImpl + ); + } + private void registerBluetoothBytesTransfer() { int tagId = FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER; PullAtomMetadata metadata = new PullAtomMetadata.Builder() diff --git a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java index 06c81ee5e2be..7dbba0d4337d 100644 --- a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java +++ b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsExt.java @@ -16,6 +16,8 @@ package com.android.server.stats.pull.netstats; +import static android.net.NetworkTemplate.OEM_MANAGED_ALL; + import android.annotation.NonNull; import android.annotation.Nullable; import android.net.NetworkStats; @@ -37,17 +39,18 @@ public class NetworkStatsExt { public final boolean slicedByTag; public final boolean slicedByMetered; public final int ratType; + public final int oemManaged; @Nullable public final SubInfo subInfo; public NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg) { this(stats, transports, slicedByFgbg, /*slicedByTag=*/false, /*slicedByMetered=*/false, - TelephonyManager.NETWORK_TYPE_UNKNOWN, /*subInfo=*/null); + TelephonyManager.NETWORK_TYPE_UNKNOWN, /*subInfo=*/null, OEM_MANAGED_ALL); } public NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg, boolean slicedByTag, boolean slicedByMetered, int ratType, - @Nullable SubInfo subInfo) { + @Nullable SubInfo subInfo, int oemManaged) { this.stats = stats; // Sort transports array so that we can test for equality without considering order. @@ -59,6 +62,7 @@ public class NetworkStatsExt { this.slicedByMetered = slicedByMetered; this.ratType = ratType; this.subInfo = subInfo; + this.oemManaged = oemManaged; } /** @@ -67,6 +71,7 @@ public class NetworkStatsExt { public boolean hasSameSlicing(@NonNull NetworkStatsExt other) { return Arrays.equals(transports, other.transports) && slicedByFgbg == other.slicedByFgbg && slicedByTag == other.slicedByTag && slicedByMetered == other.slicedByMetered - && ratType == other.ratType && Objects.equals(subInfo, other.subInfo); + && ratType == other.ratType && Objects.equals(subInfo, other.subInfo) + && oemManaged == other.oemManaged; } } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 302a23fb262c..c4f5575c22ab 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -36,6 +36,7 @@ import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; +import android.hardware.fingerprint.IUdfpsHbmListener; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -828,12 +829,24 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override + public void setUdfpsHbmListener(IUdfpsHbmListener listener) { + enforceStatusBarService(); + if (mBar != null) { + try { + mBar.setUdfpsHbmListener(listener); + } catch (RemoteException ex) { + } + } + } + + @Override public void startTracing() { if (mBar != null) { try { mBar.startTracing(); mTracingEnabled = true; - } catch (RemoteException ex) {} + } catch (RemoteException ex) { + } } } diff --git a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java new file mode 100644 index 000000000000..f00f856c2910 --- /dev/null +++ b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.timedetector; + +import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; + +import android.annotation.UserIdInt; +import android.app.time.Capabilities.CapabilityState; +import android.app.time.TimeCapabilities; +import android.app.time.TimeCapabilitiesAndConfig; +import android.app.time.TimeConfiguration; +import android.os.UserHandle; + +import java.util.Objects; + +/** + * Holds configuration values that affect time behaviour. + */ +public final class ConfigurationInternal { + + private final @UserIdInt int mUserId; + private final boolean mUserConfigAllowed; + private final boolean mAutoDetectionEnabled; + + private ConfigurationInternal(Builder builder) { + mUserId = builder.mUserId; + mUserConfigAllowed = builder.mUserConfigAllowed; + mAutoDetectionEnabled = builder.mAutoDetectionEnabled; + } + + /** Returns a {@link TimeCapabilitiesAndConfig} objects based on configuration values. */ + public TimeCapabilitiesAndConfig capabilitiesAndConfig() { + return new TimeCapabilitiesAndConfig(timeCapabilities(), timeConfiguration()); + } + + private TimeConfiguration timeConfiguration() { + return new TimeConfiguration.Builder() + .setAutoDetectionEnabled(mAutoDetectionEnabled) + .build(); + } + + private TimeCapabilities timeCapabilities() { + @CapabilityState int configureAutoTimeDetectionEnabledCapability = + mUserConfigAllowed + ? CAPABILITY_POSSESSED + : CAPABILITY_NOT_ALLOWED; + + @CapabilityState int suggestTimeManuallyCapability = + mUserConfigAllowed + ? CAPABILITY_POSSESSED + : CAPABILITY_NOT_ALLOWED; + + return new TimeCapabilities.Builder(UserHandle.of(mUserId)) + .setConfigureAutoTimeDetectionEnabledCapability( + configureAutoTimeDetectionEnabledCapability) + .setSuggestTimeManuallyCapability(suggestTimeManuallyCapability) + .build(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConfigurationInternal that = (ConfigurationInternal) o; + return mUserId == that.mUserId + && mUserConfigAllowed == that.mUserConfigAllowed + && mAutoDetectionEnabled == that.mAutoDetectionEnabled; + } + + @Override + public int hashCode() { + return Objects.hash(mUserId, mUserConfigAllowed, mAutoDetectionEnabled); + } + + @Override + public String toString() { + return "ConfigurationInternal{" + + "mUserId=" + mUserId + + ", mUserConfigAllowed=" + mUserConfigAllowed + + ", mAutoDetectionEnabled=" + mAutoDetectionEnabled + + '}'; + } + + static final class Builder { + private final @UserIdInt int mUserId; + private boolean mUserConfigAllowed; + private boolean mAutoDetectionEnabled; + + Builder(@UserIdInt int userId) { + mUserId = userId; + } + + Builder setUserConfigAllowed(boolean userConfigAllowed) { + mUserConfigAllowed = userConfigAllowed; + return this; + } + + Builder setAutoDetectionEnabled(boolean autoDetectionEnabled) { + mAutoDetectionEnabled = autoDetectionEnabled; + return this; + } + + ConfigurationInternal build() { + return new ConfigurationInternal(this); + } + } + +} diff --git a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java index 5cd171839996..4f5e8fa9c944 100644 --- a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java +++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java @@ -21,6 +21,7 @@ import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPH import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; @@ -28,6 +29,8 @@ import android.os.Build; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.util.Slog; @@ -71,6 +74,7 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme @NonNull private final ContentResolver mContentResolver; @NonNull private final PowerManager.WakeLock mWakeLock; @NonNull private final AlarmManager mAlarmManager; + @NonNull private final UserManager mUserManager; @NonNull private final int[] mOriginPriorities; public EnvironmentImpl(@NonNull Context context) { @@ -83,6 +87,8 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class)); + mUserManager = Objects.requireNonNull(context.getSystemService(UserManager.class)); + mSystemClockUpdateThresholdMillis = SystemProperties.getInt("ro.sys.time_detector_update_diff", SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT); @@ -115,6 +121,14 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme } @Override + public ConfigurationInternal configurationInternal(@UserIdInt int userId) { + return new ConfigurationInternal.Builder(userId) + .setUserConfigAllowed(isUserConfigAllowed(userId)) + .setAutoDetectionEnabled(isAutoTimeDetectionEnabled()) + .build(); + } + + @Override public void acquireWakeLock() { if (mWakeLock.isHeld()) { Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held"); @@ -150,6 +164,11 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme } } + private boolean isUserConfigAllowed(@UserIdInt int userId) { + UserHandle userHandle = UserHandle.of(userId); + return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle); + } + private static int[] getOriginPriorities(@NonNull Context context) { String[] originStrings = context.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index b210339adf79..eefa045abe1b 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -18,7 +18,10 @@ package com.android.server.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.time.ExternalTimeSuggestion; +import android.app.time.TimeCapabilitiesAndConfig; +import android.app.time.TimeConfiguration; import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ITimeDetectorService; import android.app.timedetector.ManualTimeSuggestion; @@ -36,6 +39,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.server.FgThread; import com.android.server.SystemService; +import com.android.server.timezonedetector.CallerIdentityInjector; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -71,6 +75,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @NonNull private final Handler mHandler; @NonNull private final Context mContext; @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; + @NonNull private final CallerIdentityInjector mCallerIdentityInjector; private static TimeDetectorService create(@NonNull Context context) { TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context); @@ -97,9 +102,42 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @VisibleForTesting public TimeDetectorService(@NonNull Context context, @NonNull Handler handler, @NonNull TimeDetectorStrategy timeDetectorStrategy) { + this(context, handler, timeDetectorStrategy, CallerIdentityInjector.REAL); + } + + @VisibleForTesting + public TimeDetectorService(@NonNull Context context, @NonNull Handler handler, + @NonNull TimeDetectorStrategy timeDetectorStrategy, + @NonNull CallerIdentityInjector callerIdentityInjector) { mContext = Objects.requireNonNull(context); mHandler = Objects.requireNonNull(handler); mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy); + mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector); + } + + @Override + public TimeCapabilitiesAndConfig getCapabilitiesAndConfig() { + int userId = mCallerIdentityInjector.getCallingUserId(); + return getTimeCapabilitiesAndConfig(userId); + } + + private TimeCapabilitiesAndConfig getTimeCapabilitiesAndConfig(@UserIdInt int userId) { + enforceManageTimeDetectorPermission(); + + final long token = mCallerIdentityInjector.clearCallingIdentity(); + try { + ConfigurationInternal configurationInternal = + mTimeDetectorStrategy.getConfigurationInternal(userId); + return configurationInternal.capabilitiesAndConfig(); + } finally { + mCallerIdentityInjector.restoreCallingIdentity(token); + } + } + + @Override + public boolean updateConfiguration(TimeConfiguration timeConfiguration) { + // TODO(b/172891783) Add actual logic + return false; } @Override @@ -193,4 +231,11 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { android.Manifest.permission.SET_TIME, "suggest time from external source"); } + + private void enforceManageTimeDetectorPermission() { + mContext.enforceCallingPermission( + android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION, + "manage time and time zone detection"); + } + } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index 792f372b0c49..cde66becdee2 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -18,6 +18,7 @@ package com.android.server.timedetector; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.time.ExternalTimeSuggestion; import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; @@ -88,6 +89,9 @@ public interface TimeDetectorStrategy extends Dumpable { /** Processes the suggested time from external sources. */ void suggestExternalTime(@NonNull ExternalTimeSuggestion timeSuggestion); + /** Returns the configuration that controls time detector behaviour for specified user. */ + ConfigurationInternal getConfigurationInternal(@UserIdInt int userId); + /** * Handles the auto-time configuration changing For example, when the auto-time setting is * toggled on or off. diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index 16e8632c6e40..289d8d6e648e 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -22,6 +22,7 @@ import static java.util.stream.Collectors.joining; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.AlarmManager; import android.app.time.ExternalTimeSuggestion; import android.app.timedetector.GnssTimeSuggestion; @@ -155,6 +156,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { */ @Origin int[] autoOriginPriorities(); + /** + * Returns {@link ConfigurationInternal} for specified user. + */ + @NonNull + ConfigurationInternal configurationInternal(@UserIdInt int userId); + /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */ void acquireWakeLock(); @@ -267,6 +274,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @Override + @NonNull + public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) { + return mEnvironment.configurationInternal(userId); + } + + @Override public synchronized void handleAutoTimeConfigChanged() { boolean enabled = mEnvironment.isAutoTimeDetectionEnabled(); // When automatic time detection is enabled we update the system clock instantly if we can. diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java index ee78a4e4d1cf..b4aa20130791 100644 --- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java +++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java @@ -16,10 +16,10 @@ package com.android.server.timezonedetector; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; +import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; import android.annotation.NonNull; import android.annotation.UserIdInt; diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index 5d34dd7daffb..c34a7d37ba24 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -15,7 +15,7 @@ */ package com.android.server.timezonedetector; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d361a8c03fbd..b12ce67832ef 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2247,6 +2247,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Go ahead and cancel the request. ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Clearing startingData for token=%s", this); mStartingData = null; + // Clean surface up since we don't want the window to be added back, so we don't + // need to keep the surface to remove it. + mStartingSurface = null; } return; } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 59d40cf36e9d..18e55527f1e1 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1429,6 +1429,7 @@ class ActivityStarter { + "; allowBackgroundActivityStart: " + allowBackgroundActivityStart + "; intent: " + intent + "; callerApp: " + callerApp + + "; inVisibleTask: " + (callerApp != null && callerApp.hasActivityInVisibleTask()) + "]"); // log aborted activity start to TRON if (mService.isActivityStartsLoggingEnabled()) { diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 5b685b4a0499..99289e0139b0 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, + mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent, remoteAnimationAdapter, mHandler); } } diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java index ab1ed67d4cf9..71a10df34d30 100644 --- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java +++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java @@ -70,9 +70,10 @@ class BackgroundLaunchProcessController { } boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName, - boolean appSwitchAllowed, boolean isCheckingForFgsStart, boolean hasVisibleActivities, - boolean hasBackgroundActivityStartPrivileges, long lastStopAppSwitchesTime, - long lastActivityLaunchTime, long lastActivityFinishTime) { + boolean appSwitchAllowed, boolean isCheckingForFgsStart, + boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges, + long lastStopAppSwitchesTime, long lastActivityLaunchTime, + long lastActivityFinishTime) { // If app switching is not allowed, we ignore all the start activity grace period // exception so apps cannot start itself in onPause() after pressing home button. if (appSwitchAllowed) { @@ -110,7 +111,7 @@ class BackgroundLaunchProcessController { return true; } // Allow if the caller has an activity in any foreground task. - if (appSwitchAllowed && hasVisibleActivities) { + if (appSwitchAllowed && hasActivityInVisibleTask) { if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "[Process(" + pid + ")] Activity start allowed: process has activity in foreground task"); diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 986f76831bfc..465042dfb9b1 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -492,8 +492,11 @@ class InsetsPolicy { mAnimatingShown = show; final InsetsState state = getInsetsForWindow(mFocusedWin); + + // We are about to playing the default animation. Passing a null frame indicates + // the controlled types should be animated regardless of the frame. mAnimationControl = new InsetsAnimationControlImpl(controls, - state.getDisplayFrame(), state, mListener, typesReady, this, + null /* frame */, state, mListener, typesReady, this, mListener.getDurationMs(), InsetsController.SYSTEM_BARS_INTERPOLATOR, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, null /* translator */); SurfaceAnimationThread.getHandler().post( diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 45c4233b40aa..38ad4f08b35d 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -43,6 +43,7 @@ import static com.android.server.wm.WindowManagerService.H.LAYOUT_AND_ASSIGN_WIN import android.annotation.NonNull; import android.annotation.Nullable; +import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; @@ -102,7 +103,8 @@ class InsetsSourceProvider { mSource = source; mDisplayContent = displayContent; mStateController = stateController; - mFakeControl = new InsetsSourceControl(source.getType(), null /* leash */, new Point()); + mFakeControl = new InsetsSourceControl( + source.getType(), null /* leash */, new Point(), Insets.NONE); switch (source.getType()) { case ITYPE_STATUS_BAR: @@ -246,8 +248,10 @@ class InsetsSourceProvider { setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy()); updateSourceFrame(); if (mControl != null) { + boolean changed = false; final Point position = getWindowFrameSurfacePosition(); if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) { + changed = true; if (!mWin.getWindowFrames().didFrameSizeChange()) { updateLeashPosition(-1 /* frameNumber */); } else if (mWin.mInRelayout) { @@ -255,6 +259,14 @@ class InsetsSourceProvider { } else { mWin.mPendingPositionChanged = this; } + } + final Insets insetsHint = mSource.calculateInsets( + mWin.getBounds(), true /* ignoreVisibility */); + if (!insetsHint.equals(mControl.getInsetsHint())) { + changed = true; + mControl.setInsetsHint(insetsHint); + } + if (changed) { mStateController.notifyControlChanged(mControlTarget); } } @@ -343,7 +355,8 @@ class InsetsSourceProvider { final SurfaceControl leash = mAdapter.mCapturedLeash; mControlTarget = target; updateVisibility(); - mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition); + mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition, + mSource.calculateInsets(mWin.getBounds(), true /* ignoreVisibility */)); ProtoLog.d(WM_DEBUG_IME, "InsetsSource Control %s for target %s", mControl, mControlTarget); } @@ -418,7 +431,7 @@ class InsetsSourceProvider { // to the client in case that the client applies its transaction sooner than ours // that we could unexpectedly overwrite the surface state. return new InsetsSourceControl(mControl.getType(), null /* leash */, - mControl.getSurfacePosition()); + mControl.getSurfacePosition(), mControl.getInsetsHint()); } return mControl; } diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java index 5d6d51377c3f..4ab5cd6a8d23 100644 --- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java +++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java @@ -16,6 +16,12 @@ 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; @@ -26,6 +32,7 @@ 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; @@ -35,9 +42,11 @@ import java.util.ArrayList; class NonAppWindowAnimationAdapter implements AnimationAdapter { - private final WindowState mWindow; + private final WindowContainer mWindowContainer; private RemoteAnimationTarget mTarget; private SurfaceControl mCapturedLeash; + private SurfaceAnimator.OnAnimationFinishedCallback mCapturedLeashFinishCallback; + private @SurfaceAnimator.AnimationType int mLastAnimationType; private long mDurationHint; private long mStatusBarTransitionDelay; @@ -47,21 +56,46 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { return false; } - NonAppWindowAnimationAdapter(WindowState w, - long durationHint, long statusBarTransitionDelay) { - mWindow = w; + NonAppWindowAnimationAdapter(WindowContainer w, long durationHint, + long statusBarTransitionDelay) { + mWindowContainer = w; mDurationHint = durationHint; mStatusBarTransitionDelay = statusBarTransitionDelay; } + static RemoteAnimationTarget[] startNonAppWindowAnimations(WindowManagerService service, + DisplayContent displayContent, @WindowManager.TransitionOldType int transit, + long durationHint, long statusBarTransitionDelay, + ArrayList<NonAppWindowAnimationAdapter> adaptersOut) { + 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, adaptersOut); + } 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, + adaptersOut); + } + } + 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 */ - public static RemoteAnimationTarget[] startNonAppWindowAnimationsForKeyguardExit( - WindowManagerService service, long durationHint, long statusBarTransitionDelay) { - final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); + private static void startNonAppWindowAnimationsForKeyguardExit(WindowManagerService service, + long durationHint, long statusBarTransitionDelay, + ArrayList<RemoteAnimationTarget> targets, + ArrayList<NonAppWindowAnimationAdapter> adaptersOut) { final WindowManagerPolicy policy = service.mPolicy; service.mRoot.forAllWindows(nonAppWindow -> { @@ -69,12 +103,30 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { && nonAppWindow.wouldBeVisibleIfPolicyIgnored() && !nonAppWindow.isVisible()) { final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter( nonAppWindow, durationHint, statusBarTransitionDelay); + adaptersOut.add(nonAppAdapter); nonAppWindow.startAnimation(nonAppWindow.getPendingTransaction(), nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION); targets.add(nonAppAdapter.createRemoteAnimationTarget()); } }, true /* traverseTopToBottom */); - return targets.toArray(new RemoteAnimationTarget[targets.size()]); + } + + /** + * 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, + ArrayList<NonAppWindowAnimationAdapter> adaptersOut) { + final WindowState navWindow = displayContent.getDisplayPolicy().getNavigationBar(); + final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter( + navWindow.mToken, durationHint, statusBarTransitionDelay); + adaptersOut.add(nonAppAdapter); + navWindow.mToken.startAnimation(navWindow.mToken.getPendingTransaction(), + nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION); + targets.add(nonAppAdapter.createRemoteAnimationTarget()); } /** @@ -82,16 +134,39 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { */ RemoteAnimationTarget createRemoteAnimationTarget() { mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false, - new Rect(), null, mWindow.getPrefixOrderIndex(), mWindow.getLastSurfacePosition(), - mWindow.getBounds(), null, mWindow.getWindowConfiguration(), true, null, null, - null); + new Rect(), null, mWindowContainer.getPrefixOrderIndex(), + mWindowContainer.getLastSurfacePosition(), mWindowContainer.getBounds(), null, + mWindowContainer.getWindowConfiguration(), true, null, null, null, + mWindowContainer.getWindowType()); return mTarget; } @Override public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) { + ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation"); mCapturedLeash = animationLeash; + mCapturedLeashFinishCallback = finishCallback; + mLastAnimationType = type; + } + + /** + * @return the callback to call to clean up when the animation has finished. + */ + SurfaceAnimator.OnAnimationFinishedCallback getLeashFinishedCallback() { + return mCapturedLeashFinishCallback; + } + + /** + * @return the type of animation. + */ + @SurfaceAnimator.AnimationType + int getLastAnimationType() { + return mLastAnimationType; + } + + WindowContainer getWindowContainer() { + return mWindowContainer; } @Override @@ -120,8 +195,8 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter { @Override public void dump(PrintWriter pw, String prefix) { pw.print(prefix); - pw.print("token="); - pw.println(mWindow.mToken); + pw.print("windowContainer="); + pw.println(mWindowContainer); 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 6fc585e473a9..f851e3559def 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -16,9 +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 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; @@ -41,6 +38,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.WindowManager; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLogImpl; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.FastPrintWriter; @@ -60,10 +58,13 @@ 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 = new ArrayList<>(); + @VisibleForTesting + final ArrayList<NonAppWindowAnimationAdapter> mPendingNonAppAnimations = new ArrayList<>(); private final Rect mTmpRect = new Rect(); private final Handler mHandler; private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable"); @@ -72,9 +73,10 @@ class RemoteAnimationController implements DeathRecipient { private boolean mCanceled; private boolean mLinkedToDeathOfRunner; - RemoteAnimationController(WindowManagerService service, + RemoteAnimationController(WindowManagerService service, DisplayContent displayContent, RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) { mService = service; + mDisplayContent = displayContent; mRemoteAnimationAdapter = remoteAnimationAdapter; mHandler = handler; } @@ -224,12 +226,12 @@ class RemoteAnimationController implements DeathRecipient { private RemoteAnimationTarget[] createNonAppWindowAnimations( @WindowManager.TransitionOldType int transit) { ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createNonAppWindowAnimations()"); - return (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY - || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) - ? NonAppWindowAnimationAdapter.startNonAppWindowAnimationsForKeyguardExit(mService, - mRemoteAnimationAdapter.getDuration(), - mRemoteAnimationAdapter.getStatusBarTransitionDelay()) - : new RemoteAnimationTarget[0]; + return NonAppWindowAnimationAdapter.startNonAppWindowAnimations(mService, + mDisplayContent, + transit, + mRemoteAnimationAdapter.getDuration(), + mRemoteAnimationAdapter.getStatusBarTransitionDelay(), + mPendingNonAppAnimations); } private void onAnimationFinished() { @@ -267,6 +269,15 @@ class RemoteAnimationController implements DeathRecipient { mPendingWallpaperAnimations.remove(i); ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\twallpaper=%s", adapter.getToken()); } + + for (int i = mPendingNonAppAnimations.size() - 1; i >= 0; i--) { + final NonAppWindowAnimationAdapter adapter = mPendingNonAppAnimations.get(i); + adapter.getLeashFinishedCallback().onAnimationFinished( + adapter.getLastAnimationType(), adapter); + mPendingNonAppAnimations.remove(i); + ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tnonApp=%s", + adapter.getWindowContainer()); + } } catch (Exception e) { Slog.e(TAG, "Failed to finish remote animation", e); throw e; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 2a768e7c99b6..422d4e79e259 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -273,7 +273,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Whether tasks have moved and we need to rank the tasks before next OOM scoring private boolean mTaskLayersChanged = true; private int mTmpTaskLayerRank; - private final LockedScheduler mRankTaskLayersScheduler; + private final RankTaskLayersRunnable mRankTaskLayersRunnable = new RankTaskLayersRunnable(); private boolean mTmpBoolean; private RemoteException mTmpRemoteException; @@ -451,12 +451,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mTaskSupervisor = mService.mTaskSupervisor; mTaskSupervisor.mRootWindowContainer = this; mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl("Display-off"); - mRankTaskLayersScheduler = new LockedScheduler(mService) { - @Override - public void execute() { - rankTaskLayersIfNeeded(); - } - }; } boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) { @@ -2660,16 +2654,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } void invalidateTaskLayers() { - mTaskLayersChanged = true; - mRankTaskLayersScheduler.scheduleIfNeeded(); + if (!mTaskLayersChanged) { + mTaskLayersChanged = true; + mService.mH.post(mRankTaskLayersRunnable); + } } /** Generate oom-score-adjustment rank for all tasks in the system based on z-order. */ - void rankTaskLayersIfNeeded() { - if (!mTaskLayersChanged) { - return; + void rankTaskLayers() { + if (mTaskLayersChanged) { + mTaskLayersChanged = false; + mService.mH.removeCallbacks(mRankTaskLayersRunnable); } - mTaskLayersChanged = false; mTmpTaskLayerRank = 0; // Only rank for leaf tasks because the score of activity is based on immediate parent. forAllLeafTasks(task -> { @@ -3669,32 +3665,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - /** - * Helper class to schedule the runnable if it hasn't scheduled on display thread inside window - * manager lock. - */ - abstract static class LockedScheduler implements Runnable { - private final ActivityTaskManagerService mService; - private boolean mScheduled; - - LockedScheduler(ActivityTaskManagerService service) { - mService = service; - } - + private class RankTaskLayersRunnable implements Runnable { @Override public void run() { synchronized (mService.mGlobalLock) { - mScheduled = false; - execute(); - } - } - - abstract void execute(); - - void scheduleIfNeeded() { - if (!mScheduled) { - mService.mH.post(this); - mScheduled = true; + if (mTaskLayersChanged) { + mTaskLayersChanged = false; + rankTaskLayers(); + } } } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index e3679c0d8096..0bb9854d8020 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -29,6 +29,7 @@ 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; @@ -3308,4 +3309,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mListeners.remove(listener); unregisterConfigurationChangeListener(listener); } + + /** + * Returns the {@link WindowManager.LayoutParams.WindowType}. + */ + @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 dce79a093a6f..c5e000000eee 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1600,11 +1600,17 @@ public class WindowManagerService extends IWindowManager.Stub ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token " + ".%s Aborting.", token); return WindowManagerGlobal.ADD_APP_EXITING; - } else if (type == TYPE_APPLICATION_STARTING && activity.mStartingWindow != null) { - ProtoLog.w(WM_ERROR, - "Attempted to add starting window to token with already existing" - + " starting window"); - return WindowManagerGlobal.ADD_DUPLICATE_ADD; + } else if (type == TYPE_APPLICATION_STARTING) { + if (activity.mStartingWindow != null) { + ProtoLog.w(WM_ERROR, "Attempted to add starting window to " + + "token with already existing starting window"); + return WindowManagerGlobal.ADD_DUPLICATE_ADD; + } + if (activity.mStartingData == null) { + ProtoLog.w(WM_ERROR, "Attempted to add starting window to " + + "token but already cleaned"); + return WindowManagerGlobal.ADD_DUPLICATE_ADD; + } } } else if (rootType == TYPE_INPUT_METHOD) { if (token.windowType != TYPE_INPUT_METHOD) { diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index c5e24a9df3a7..bac1ab1264ab 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -225,6 +225,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private static final int ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING = 1 << 19; private static final int ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE = 1 << 20; private static final int ACTIVITY_STATE_FLAG_HAS_RESUMED = 1 << 21; + private static final int ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK = 1 << 22; private static final int ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER = 0x0000ffff; /** @@ -479,7 +480,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } void setLastActivityFinishTimeIfNeeded(long finishTime) { - if (finishTime <= mLastActivityFinishTime || !hasVisibleActivities()) { + if (finishTime <= mLastActivityFinishTime || !hasActivityInVisibleTask()) { return; } mLastActivityFinishTime = finishTime; @@ -516,7 +517,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed, boolean isCheckingForFgsStart) { return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid, mInfo.packageName, - appSwitchAllowed, isCheckingForFgsStart, hasVisibleActivities(), + appSwitchAllowed, isCheckingForFgsStart, hasActivityInVisibleTask(), mInstrumentingWithBackgroundActivityStartPrivileges, mAtm.getLastStopAppSwitchesTime(), mLastActivityLaunchTime, mLastActivityFinishTime); @@ -653,6 +654,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return (mActivityStateFlags & ACTIVITY_STATE_FLAG_IS_VISIBLE) != 0; } + boolean hasActivityInVisibleTask() { + return (mActivityStateFlags & ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK) != 0; + } + @HotPath(caller = HotPath.LRU_UPDATE) public boolean hasActivitiesOrRecentTasks() { return mHasActivities || mHasRecentTasks; @@ -996,11 +1001,14 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio if (r.isVisible()) { stateFlags |= ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE; } + final Task task = r.getTask(); + if (task != null && task.mLayerRank != Task.LAYER_RANK_INVISIBLE) { + stateFlags |= ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK; + } if (r.mVisibleRequested) { if (r.isState(RESUMED)) { stateFlags |= ACTIVITY_STATE_FLAG_HAS_RESUMED; } - final Task task = r.getTask(); if (task != null && minTaskLayer > 0) { final int layer = task.mLayerRank; if (layer >= 0 && minTaskLayer > layer) { @@ -1048,7 +1056,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** Called when the process has some oom related changes and it is going to update oom-adj. */ private void prepareOomAdjustment() { - mAtm.mRootWindowContainer.rankTaskLayersIfNeeded(); + mAtm.mRootWindowContainer.rankTaskLayers(); mAtm.mTaskSupervisor.computeProcessActivityStateBatch(); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 2eadcd581417..6d88387fe25c 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5892,4 +5892,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP void setSurfaceTranslationY(int translationY) { mSurfaceTranslationY = translationY; } + + @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 8867aa747379..5276d9c8a5f1 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -764,4 +764,9 @@ class WindowToken extends WindowContainer<WindowState> { forAllWindows(WindowState::clearFrozenInsetsState, true /* traverseTopToBottom */); } } + + @Override + @WindowManager.LayoutParams.WindowType int getWindowType() { + return windowType; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java index 84e6da0d9851..2825eea975fd 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java @@ -48,6 +48,11 @@ public class DevicePolicyConstants { private static final String BATTERY_THRESHOLD_CHARGING_KEY = "battery_threshold_charging"; + // TODO(b/182994391): Replace with more generic solution to override the supervision + // component. + private static final String USE_TEST_ADMIN_AS_SUPERVISION_COMPONENT_KEY = + "use_test_admin_as_supervision_component"; + /** * The back-off before re-connecting, when a service binding died, due to the owner * crashing repeatedly. @@ -79,6 +84,12 @@ public class DevicePolicyConstants { */ public final int BATTERY_THRESHOLD_CHARGING; + /** + * Whether to default to considering the current DO/PO as the supervision component + * if they are a testOnly admin. + */ + public final boolean USE_TEST_ADMIN_AS_SUPERVISION_COMPONENT; + private DevicePolicyConstants(String settings) { @@ -110,6 +121,9 @@ public class DevicePolicyConstants { int batteryThresholdCharging = parser.getInt( BATTERY_THRESHOLD_CHARGING_KEY, 20); + boolean useTestAdminAsSupervisionComponent = parser.getBoolean( + USE_TEST_ADMIN_AS_SUPERVISION_COMPONENT_KEY, false); + // Set minimum: 5 seconds. dasDiedServiceReconnectBackoffSec = Math.max(5, dasDiedServiceReconnectBackoffSec); @@ -128,6 +142,7 @@ public class DevicePolicyConstants { dasDiedServiceStableConnectionThresholdSec; BATTERY_THRESHOLD_NOT_CHARGING = batteryThresholdNotCharging; BATTERY_THRESHOLD_CHARGING = batteryThresholdCharging; + USE_TEST_ADMIN_AS_SUPERVISION_COMPONENT = useTestAdminAsSupervisionComponent; } public static DevicePolicyConstants loadFromString(String settings) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 70756787c561..f2b452948384 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5482,7 +5482,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean hasKeyPair(String callerPackage, String alias) { final CallerIdentity caller = getCallerIdentity(callerPackage); - Preconditions.checkCallAuthorization(canManageCertificates(caller)); + Preconditions.checkCallAuthorization(canManageCertificates(caller) + || isCredentialManagementApp(caller, alias)); return mInjector.binderWithCleanCallingIdentity(() -> { try (KeyChainConnection keyChainConnection = @@ -8799,11 +8800,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final ComponentName doComponent = mOwners.getDeviceOwnerComponent(); final ComponentName poComponent = mOwners.getProfileOwnerComponent(userHandle.getIdentifier()); - // Return test only admin by default. - if (isAdminTestOnlyLocked(doComponent, userHandle.getIdentifier())) { - return doComponent; - } else if (isAdminTestOnlyLocked(poComponent, userHandle.getIdentifier())) { - return poComponent; + // Return test only admin if configured to do so. + // TODO(b/182994391): Replace with more generic solution to override the supervision + // component. + if (mConstants.USE_TEST_ADMIN_AS_SUPERVISION_COMPONENT) { + if (isAdminTestOnlyLocked(doComponent, userHandle.getIdentifier())) { + return doComponent; + } else if (isAdminTestOnlyLocked(poComponent, userHandle.getIdentifier())) { + return poComponent; + } } final String supervisor = mContext.getResources().getString( com.android.internal.R.string.config_defaultSupervisionProfileOwnerComponent); @@ -12123,6 +12128,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void onChange(boolean selfChange, Uri uri, int userId) { mConstants = loadConstants(); + + mInjector.binderWithCleanCallingIdentity(() -> { + final Intent intent = new Intent( + DevicePolicyManager.ACTION_DEVICE_POLICY_CONSTANTS_CHANGED); + intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + final List<UserInfo> users = mUserManager.getAliveUsers(); + for (int i = 0; i < users.size(); i++) { + mContext.sendBroadcastAsUser(intent, UserHandle.of(users.get(i).id)); + } + }); } } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 7e4bc1e371b2..281c1aafe049 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -75,6 +75,7 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; +import android.platform.test.annotations.LargeTest; import android.provider.DeviceConfig; import android.util.SparseBooleanArray; @@ -286,14 +287,20 @@ public class QuotaControllerTest { doReturn(procState).when(mActivityMangerInternal).getUidProcessState(uid); SparseBooleanArray foregroundUids = mQuotaController.getForegroundUids(); spyOn(foregroundUids); + final boolean contained = foregroundUids.get(uid); mUidObserver.onUidStateChanged(uid, procState, 0, ActivityManager.PROCESS_CAPABILITY_NONE); if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { - verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)) - .put(eq(uid), eq(true)); + if (!contained) { + verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)) + .put(eq(uid), eq(true)); + } assertTrue(foregroundUids.get(uid)); } else { - verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)).delete(eq(uid)); + if (contained) { + verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)) + .delete(eq(uid)); + } assertFalse(foregroundUids.get(uid)); } waitForNonDelayedMessagesProcessed(); @@ -1993,7 +2000,7 @@ public class QuotaControllerTest { setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); final long gracePeriodMs = 15 * SECOND_IN_MILLIS; - setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); Handler handler = mQuotaController.getHandler(); spyOn(handler); @@ -2017,6 +2024,52 @@ public class QuotaControllerTest { } } + /** + * Tests that Timers properly track sessions when an app becomes top and is closed. + */ + @Test + public void testIsWithinEJQuotaLocked_TopApp() { + setDischarging(); + JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_TopApp", 1); + setStandbyBucket(FREQUENT_INDEX, js); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 10 * MINUTE_IN_MILLIS); + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), true); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), true); + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); + } + + final long gracePeriodMs = 15 * SECOND_IN_MILLIS; + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, gracePeriodMs); + Handler handler = mQuotaController.getHandler(); + spyOn(handler); + + // Apps on top should be able to schedule & start EJs, even if they're out + // of quota (as long as they are in the top grace period). + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); + } + advanceElapsedClock(10 * SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + // Still in grace period + synchronized (mQuotaController.mLock) { + assertTrue(mQuotaController.isWithinEJQuotaLocked(js)); + } + advanceElapsedClock(6 * SECOND_IN_MILLIS); + // Out of grace period. + synchronized (mQuotaController.mLock) { + assertFalse(mQuotaController.isWithinEJQuotaLocked(js)); + } + } + @Test public void testMaybeScheduleCleanupAlarmLocked() { // No sessions saved yet. @@ -2657,8 +2710,9 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 87 * SECOND_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, 86 * SECOND_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 85 * SECOND_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 84 * SECOND_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 83 * SECOND_IN_MILLIS); assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); @@ -2697,7 +2751,8 @@ public class QuotaControllerTest { assertEquals(87 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); assertEquals(86 * SECOND_IN_MILLIS, mQuotaController.getEJRewardInteractionMs()); assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); - assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJTempAllowlistGracePeriodMs()); + assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs()); + assertEquals(83 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs()); } @Test @@ -2737,7 +2792,8 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, -1); - setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, -1); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, -1); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, -1); assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(0, mQuotaController.getInQuotaBufferMs()); @@ -2773,7 +2829,8 @@ public class QuotaControllerTest { assertEquals(10 * SECOND_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); assertEquals(5 * SECOND_IN_MILLIS, mQuotaController.getEJRewardInteractionMs()); assertEquals(0, mQuotaController.getEJRewardNotificationSeenMs()); - assertEquals(0, mQuotaController.getEJTempAllowlistGracePeriodMs()); + assertEquals(0, mQuotaController.getEJGracePeriodTempAllowlistMs()); + assertEquals(0, mQuotaController.getEJGracePeriodTopAppMs()); // Invalid configurations. // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD @@ -2807,7 +2864,8 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 25 * HOUR_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); @@ -2833,7 +2891,8 @@ public class QuotaControllerTest { assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardTopAppMs()); assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardInteractionMs()); assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); - assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJTempAllowlistGracePeriodMs()); + assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs()); + assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs()); } /** Tests that TimingSessions aren't saved when the device is charging. */ @@ -3433,7 +3492,7 @@ public class QuotaControllerTest { setDischarging(); setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); final long gracePeriodMs = 15 * SECOND_IN_MILLIS; - setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); Handler handler = mQuotaController.getHandler(); spyOn(handler); @@ -4910,6 +4969,7 @@ public class QuotaControllerTest { @Test public void testEJTimerTracking_TopAndNonTop() { setDischarging(); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 0); JobStatus jobBg1 = createExpeditedJobStatus("testEJTimerTracking_TopAndNonTop", 1); JobStatus jobBg2 = createExpeditedJobStatus("testEJTimerTracking_TopAndNonTop", 2); @@ -5032,7 +5092,7 @@ public class QuotaControllerTest { setDischarging(); setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); final long gracePeriodMs = 15 * SECOND_IN_MILLIS; - setDeviceConfigLong(QcConstants.KEY_EJ_TEMP_ALLOWLIST_GRACE_PERIOD_MS, gracePeriodMs); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); Handler handler = mQuotaController.getHandler(); spyOn(handler); @@ -5134,11 +5194,158 @@ public class QuotaControllerTest { } /** + * Tests that Timers properly track sessions when TOP state and temp allowlisting overlaps. + */ + @Test + @LargeTest + public void testEJTimerTracking_TopAndTempAllowlisting() throws Exception { + setDischarging(); + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + final long gracePeriodMs = 5 * SECOND_IN_MILLIS; + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, gracePeriodMs); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, gracePeriodMs); + Handler handler = mQuotaController.getHandler(); + spyOn(handler); + + JobStatus job1 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 1); + JobStatus job2 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 2); + JobStatus job3 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 3); + JobStatus job4 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 4); + JobStatus job5 = createExpeditedJobStatus("testEJTimerTracking_TopAndTempAllowlisting", 5); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job1, null); + } + assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + List<TimingSession> expected = new ArrayList<>(); + + // Case 1: job starts in TA grace period then app becomes TOP + long start = JobSchedulerService.sElapsedRealtimeClock.millis(); + mTempAllowlistListener.onAppAdded(mSourceUid); + mTempAllowlistListener.onAppRemoved(mSourceUid); + advanceElapsedClock(gracePeriodMs / 2); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(job1); + } + setProcessState(ActivityManager.PROCESS_STATE_TOP); + advanceElapsedClock(gracePeriodMs); + // Wait for the grace period to expire so the handler can process the message. + Thread.sleep(gracePeriodMs); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job1, job1, true); + } + assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(gracePeriodMs); + + // Case 2: job starts in TOP grace period then is TAed + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + advanceElapsedClock(gracePeriodMs / 2); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job2, null); + mQuotaController.prepareForExecutionLocked(job2); + } + mTempAllowlistListener.onAppAdded(mSourceUid); + advanceElapsedClock(gracePeriodMs); + // Wait for the grace period to expire so the handler can process the message. + Thread.sleep(gracePeriodMs); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job2, null, false); + } + assertNull(mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(gracePeriodMs); + + // Case 3: job starts in TA grace period then app becomes TOP; job ends after TOP grace + mTempAllowlistListener.onAppAdded(mSourceUid); + mTempAllowlistListener.onAppRemoved(mSourceUid); + advanceElapsedClock(gracePeriodMs / 2); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job3, null); + mQuotaController.prepareForExecutionLocked(job3); + } + advanceElapsedClock(SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_TOP); + advanceElapsedClock(gracePeriodMs); + // Wait for the grace period to expire so the handler can process the message. + Thread.sleep(gracePeriodMs); + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + advanceElapsedClock(gracePeriodMs); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + // Wait for the grace period to expire so the handler can process the message. + Thread.sleep(2 * gracePeriodMs); + advanceElapsedClock(gracePeriodMs); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job3, job3, true); + } + expected.add(createTimingSession(start, gracePeriodMs, 1)); + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(gracePeriodMs); + + // Case 4: job starts in TOP grace period then app becomes TAed; job ends after TA grace + setProcessState(ActivityManager.PROCESS_STATE_TOP); + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + advanceElapsedClock(gracePeriodMs / 2); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job4, null); + mQuotaController.prepareForExecutionLocked(job4); + } + advanceElapsedClock(SECOND_IN_MILLIS); + mTempAllowlistListener.onAppAdded(mSourceUid); + advanceElapsedClock(gracePeriodMs); + // Wait for the grace period to expire so the handler can process the message. + Thread.sleep(gracePeriodMs); + mTempAllowlistListener.onAppRemoved(mSourceUid); + advanceElapsedClock(gracePeriodMs); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + // Wait for the grace period to expire so the handler can process the message. + Thread.sleep(2 * gracePeriodMs); + advanceElapsedClock(gracePeriodMs); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job4, job4, true); + } + expected.add(createTimingSession(start, gracePeriodMs, 1)); + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(gracePeriodMs); + + // Case 5: job starts during overlapping grace period + setProcessState(ActivityManager.PROCESS_STATE_TOP); + advanceElapsedClock(SECOND_IN_MILLIS); + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + advanceElapsedClock(SECOND_IN_MILLIS); + mTempAllowlistListener.onAppAdded(mSourceUid); + advanceElapsedClock(SECOND_IN_MILLIS); + mTempAllowlistListener.onAppRemoved(mSourceUid); + advanceElapsedClock(gracePeriodMs - SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job5, null); + mQuotaController.prepareForExecutionLocked(job5); + } + advanceElapsedClock(SECOND_IN_MILLIS); + start = JobSchedulerService.sElapsedRealtimeClock.millis(); + // Wait for the grace period to expire so the handler can process the message. + Thread.sleep(2 * gracePeriodMs); + advanceElapsedClock(10 * SECOND_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(job5, job5, true); + } + expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1)); + assertEquals(expected, + mQuotaController.getEJTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** * Tests that expedited jobs aren't stopped when an app runs out of quota. */ @Test public void testEJTracking_OutOfQuota_ForegroundAndBackground() { setDischarging(); + setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 0); JobStatus jobBg = createExpeditedJobStatus("testEJTracking_OutOfQuota_ForegroundAndBackground", 1); diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java index 6814c050a845..2eb9e34b3fd0 100644 --- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java @@ -524,7 +524,7 @@ public class GestureLauncherServiceTest { assertTrue(outLaunched.value); verify(mUiEventLogger, times(1)) - .log(GestureLauncherService.GestureLauncherEvent.GESTURE_PANIC_TAP_POWER); + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER); verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected(); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); @@ -578,7 +578,7 @@ public class GestureLauncherServiceTest { assertTrue(intercepted); verify(mUiEventLogger, times(1)) - .log(GestureLauncherService.GestureLauncherEvent.GESTURE_PANIC_TAP_POWER); + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER); verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected(); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java index c7e7c7861370..45f43e8b672f 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java @@ -16,6 +16,8 @@ package com.android.server.accessibility; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static junit.framework.Assert.assertFalse; import static junit.framework.TestCase.assertNull; import static junit.framework.TestCase.assertTrue; @@ -30,6 +32,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -47,10 +50,13 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.testing.DexmakerShareClassLoaderRule; +import android.testing.TestableContext; import android.util.ArraySet; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityWindowInfo; +import com.android.internal.R; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -58,7 +64,9 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; /** @@ -72,9 +80,9 @@ public class AccessibilitySecurityPolicyTest { private static final int APP_UID = 10400; private static final int APP_PID = 2000; private static final int SYSTEM_PID = 558; - - private static final String PERMISSION = "test-permission"; - private static final String FUNCTION = "test-function-name"; + private static final int TEST_USER_ID = UserHandle.USER_SYSTEM; + private static final ComponentName TEST_COMPONENT_NAME = new ComponentName( + "com.android.server.accessibility", "AccessibilitySecurityPolicyTest"); private static final int[] ALWAYS_DISPATCH_EVENTS = { AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, @@ -108,29 +116,51 @@ public class AccessibilitySecurityPolicyTest { private AccessibilitySecurityPolicy mA11ySecurityPolicy; + @Rule + public final TestableContext mContext = new TestableContext( + getInstrumentation().getTargetContext(), null); + // To mock package-private class - @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = + @Rule + public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); - @Mock private Context mMockContext; - @Mock private PackageManager mMockPackageManager; - @Mock private UserManager mMockUserManager; - @Mock private AppOpsManager mMockAppOpsManager; - @Mock private AccessibilityServiceConnection mMockA11yServiceConnection; - @Mock private AccessibilityWindowManager mMockA11yWindowManager; - @Mock private AppWidgetManagerInternal mMockAppWidgetManager; - @Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager; + @Mock + private PackageManager mMockPackageManager; + @Mock + private UserManager mMockUserManager; + @Mock + private AppOpsManager mMockAppOpsManager; + @Mock + private AccessibilityServiceConnection mMockA11yServiceConnection; + @Mock + private AccessibilityWindowManager mMockA11yWindowManager; + @Mock + private AppWidgetManagerInternal mMockAppWidgetManager; + @Mock + private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager; + @Mock + private AccessibilityServiceInfo mMockA11yServiceInfo; + @Mock + private PolicyWarningUIController mPolicyWarningUIController; @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); - when(mMockContext.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager); - when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOpsManager); + mContext.setMockPackageManager(mMockPackageManager); + mContext.addMockSystemService(Context.USER_SERVICE, mMockUserManager); + mContext.addMockSystemService(Context.APP_OPS_SERVICE, mMockAppOpsManager); + mContext.getOrCreateTestableResources().addOverride( + R.dimen.accessibility_focus_highlight_stroke_width, 1); - mA11ySecurityPolicy = new AccessibilitySecurityPolicy(mMockContext, mMockA11yUserManager); + when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME); + when(mMockA11yServiceConnection.getServiceInfo()).thenReturn(mMockA11yServiceInfo); + + mA11ySecurityPolicy = new AccessibilitySecurityPolicy( + mPolicyWarningUIController, mContext, mMockA11yUserManager); mA11ySecurityPolicy.setAccessibilityWindowManager(mMockA11yWindowManager); mA11ySecurityPolicy.setAppWidgetManager(mMockAppWidgetManager); + mA11ySecurityPolicy.onSwitchUserLocked(TEST_USER_ID, new HashSet<>()); when(mMockA11yWindowManager.resolveParentWindowIdLocked(anyInt())).then(returnsFirstArg()); } @@ -141,7 +171,7 @@ public class AccessibilitySecurityPolicyTest { final AccessibilityEvent event = AccessibilityEvent.obtain(ALWAYS_DISPATCH_EVENTS[i]); assertTrue("Should dispatch [" + event + "]", mA11ySecurityPolicy.canDispatchAccessibilityEventLocked( - UserHandle.USER_SYSTEM, + TEST_USER_ID, event)); } } @@ -154,28 +184,28 @@ public class AccessibilitySecurityPolicyTest { event.setWindowId(invalidWindowId); assertFalse("Shouldn't dispatch [" + event + "]", mA11ySecurityPolicy.canDispatchAccessibilityEventLocked( - UserHandle.USER_SYSTEM, + TEST_USER_ID, event)); } } @Test public void canDispatchAccessibilityEvent_otherEvents_windowIdIsActive_returnTrue() { - when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM)) + when(mMockA11yWindowManager.getActiveWindowId(TEST_USER_ID)) .thenReturn(WINDOWID); for (int i = 0; i < OTHER_EVENTS.length; i++) { final AccessibilityEvent event = AccessibilityEvent.obtain(OTHER_EVENTS[i]); event.setWindowId(WINDOWID); assertTrue("Should dispatch [" + event + "]", mA11ySecurityPolicy.canDispatchAccessibilityEventLocked( - UserHandle.USER_SYSTEM, + TEST_USER_ID, event)); } } @Test public void canDispatchAccessibilityEvent_otherEvents_windowIdExist_returnTrue() { - when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM)) + when(mMockA11yWindowManager.getActiveWindowId(TEST_USER_ID)) .thenReturn(WINDOWID2); when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID)) .thenReturn(AccessibilityWindowInfo.obtain()); @@ -184,7 +214,7 @@ public class AccessibilitySecurityPolicyTest { event.setWindowId(WINDOWID); assertTrue("Should dispatch [" + event + "]", mA11ySecurityPolicy.canDispatchAccessibilityEventLocked( - UserHandle.USER_SYSTEM, + TEST_USER_ID, event)); } } @@ -192,24 +222,24 @@ public class AccessibilitySecurityPolicyTest { @Test public void resolveValidReportedPackage_nullPkgName_returnNull() { assertNull(mA11ySecurityPolicy.resolveValidReportedPackageLocked( - null, Process.SYSTEM_UID, UserHandle.USER_SYSTEM, SYSTEM_PID)); + null, Process.SYSTEM_UID, TEST_USER_ID, SYSTEM_PID)); } @Test public void resolveValidReportedPackage_uidIsSystem_returnPkgName() { assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked( - PACKAGE_NAME, Process.SYSTEM_UID, UserHandle.USER_SYSTEM, SYSTEM_PID), + PACKAGE_NAME, Process.SYSTEM_UID, TEST_USER_ID, SYSTEM_PID), PACKAGE_NAME); } @Test public void resolveValidReportedPackage_uidAndPkgNameMatched_returnPkgName() throws PackageManager.NameNotFoundException { - when(mMockPackageManager.getPackageUidAsUser(PACKAGE_NAME, UserHandle.USER_SYSTEM)) + when(mMockPackageManager.getPackageUidAsUser(PACKAGE_NAME, TEST_USER_ID)) .thenReturn(APP_UID); assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked( - PACKAGE_NAME, APP_UID, UserHandle.USER_SYSTEM, APP_PID), + PACKAGE_NAME, APP_UID, TEST_USER_ID, APP_PID), PACKAGE_NAME); } @@ -225,11 +255,11 @@ public class AccessibilitySecurityPolicyTest { when(mMockAppWidgetManager.getHostedWidgetPackages(widgetHostUid)) .thenReturn(widgetPackages); - when(mMockPackageManager.getPackageUidAsUser(hostPackageName, UserHandle.USER_SYSTEM)) + when(mMockPackageManager.getPackageUidAsUser(hostPackageName, TEST_USER_ID)) .thenReturn(widgetHostUid); assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked( - widgetPackageName, widgetHostUid, UserHandle.USER_SYSTEM, widgetHostPid), + widgetPackageName, widgetHostUid, TEST_USER_ID, widgetHostPid), widgetPackageName); } @@ -240,16 +270,16 @@ public class AccessibilitySecurityPolicyTest { final String[] uidPackages = {PACKAGE_NAME, PACKAGE_NAME2}; when(mMockPackageManager.getPackagesForUid(APP_UID)) .thenReturn(uidPackages); - when(mMockPackageManager.getPackageUidAsUser(invalidPackageName, UserHandle.USER_SYSTEM)) + when(mMockPackageManager.getPackageUidAsUser(invalidPackageName, TEST_USER_ID)) .thenThrow(PackageManager.NameNotFoundException.class); when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID)) .thenReturn(new ArraySet<>()); - when(mMockContext.checkPermission( - eq(Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY), anyInt(), eq(APP_UID))) - .thenReturn(PackageManager.PERMISSION_DENIED); + mContext.getTestablePermissions().setPermission( + Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY, + PackageManager.PERMISSION_DENIED); assertEquals(PACKAGE_NAME, mA11ySecurityPolicy.resolveValidReportedPackageLocked( - invalidPackageName, APP_UID, UserHandle.USER_SYSTEM, APP_PID)); + invalidPackageName, APP_UID, TEST_USER_ID, APP_PID)); } @Test @@ -260,16 +290,16 @@ public class AccessibilitySecurityPolicyTest { final String[] uidPackages = {PACKAGE_NAME}; when(mMockPackageManager.getPackagesForUid(APP_UID)) .thenReturn(uidPackages); - when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, UserHandle.USER_SYSTEM)) + when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, TEST_USER_ID)) .thenReturn(wantedUid); when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID)) .thenReturn(new ArraySet<>()); - when(mMockContext.checkPermission( - eq(Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY), anyInt(), eq(APP_UID))) - .thenReturn(PackageManager.PERMISSION_GRANTED); + mContext.getTestablePermissions().setPermission( + Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY, + PackageManager.PERMISSION_GRANTED); assertEquals(wantedPackageName, mA11ySecurityPolicy.resolveValidReportedPackageLocked( - wantedPackageName, APP_UID, UserHandle.USER_SYSTEM, APP_PID)); + wantedPackageName, APP_UID, TEST_USER_ID, APP_PID)); } @Test @@ -280,16 +310,16 @@ public class AccessibilitySecurityPolicyTest { final String[] uidPackages = {PACKAGE_NAME}; when(mMockPackageManager.getPackagesForUid(APP_UID)) .thenReturn(uidPackages); - when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, UserHandle.USER_SYSTEM)) + when(mMockPackageManager.getPackageUidAsUser(wantedPackageName, TEST_USER_ID)) .thenReturn(wantedUid); when(mMockAppWidgetManager.getHostedWidgetPackages(APP_UID)) .thenReturn(new ArraySet<>()); - when(mMockContext.checkPermission( - eq(Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY), anyInt(), eq(APP_UID))) - .thenReturn(PackageManager.PERMISSION_DENIED); + mContext.getTestablePermissions().setPermission( + Manifest.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY, + PackageManager.PERMISSION_DENIED); assertEquals(PACKAGE_NAME, mA11ySecurityPolicy.resolveValidReportedPackageLocked( - wantedPackageName, APP_UID, UserHandle.USER_SYSTEM, APP_PID)); + wantedPackageName, APP_UID, TEST_USER_ID, APP_PID)); } @Test @@ -301,7 +331,7 @@ public class AccessibilitySecurityPolicyTest { @Test public void computeValidReportedPackages_uidIsAppWidgetHost_returnTargetAndWidgetName() { final int widgetHostUid = APP_UID; - final String targetPackageName = PACKAGE_NAME; + final String targetPackageName = PACKAGE_NAME; final String widgetPackageName = PACKAGE_NAME2; final ArraySet<String> widgetPackages = new ArraySet<>(); widgetPackages.add(widgetPackageName); @@ -320,7 +350,7 @@ public class AccessibilitySecurityPolicyTest { when(mMockA11yServiceConnection.getCapabilities()) .thenReturn(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT); - assertFalse(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(UserHandle.USER_SYSTEM, + assertFalse(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(TEST_USER_ID, mMockA11yServiceConnection, invalidWindowId)); } @@ -328,10 +358,10 @@ public class AccessibilitySecurityPolicyTest { public void canGetAccessibilityNodeInfo_hasCapAndWindowIsActive_returnTrue() { when(mMockA11yServiceConnection.getCapabilities()) .thenReturn(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT); - when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM)) + when(mMockA11yWindowManager.getActiveWindowId(TEST_USER_ID)) .thenReturn(WINDOWID); - assertTrue(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(UserHandle.USER_SYSTEM, + assertTrue(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(TEST_USER_ID, mMockA11yServiceConnection, WINDOWID)); } @@ -339,12 +369,12 @@ public class AccessibilitySecurityPolicyTest { public void canGetAccessibilityNodeInfo_hasCapAndWindowExist_returnTrue() { when(mMockA11yServiceConnection.getCapabilities()) .thenReturn(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT); - when(mMockA11yWindowManager.getActiveWindowId(UserHandle.USER_SYSTEM)) + when(mMockA11yWindowManager.getActiveWindowId(TEST_USER_ID)) .thenReturn(WINDOWID2); when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID)) .thenReturn(AccessibilityWindowInfo.obtain()); - assertTrue(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(UserHandle.USER_SYSTEM, + assertTrue(mA11ySecurityPolicy.canGetAccessibilityNodeInfoLocked(TEST_USER_ID, mMockA11yServiceConnection, WINDOWID)); } @@ -464,8 +494,10 @@ public class AccessibilitySecurityPolicyTest { .thenReturn(currentUserId); doReturn(callingParentId).when(spySecurityPolicy).resolveProfileParentLocked( callingUserId); - when(mMockContext.checkCallingPermission(any())) - .thenReturn(PackageManager.PERMISSION_DENIED); + mContext.getTestablePermissions().setPermission(Manifest.permission.INTERACT_ACROSS_USERS, + PackageManager.PERMISSION_DENIED); + mContext.getTestablePermissions().setPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, PackageManager.PERMISSION_DENIED); spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( UserHandle.USER_CURRENT_OR_SELF); @@ -482,8 +514,8 @@ public class AccessibilitySecurityPolicyTest { .thenReturn(currentUserId); doReturn(callingParentId).when(spySecurityPolicy).resolveProfileParentLocked( callingUserId); - when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS)) - .thenReturn(PackageManager.PERMISSION_GRANTED); + mContext.getTestablePermissions().setPermission(Manifest.permission.INTERACT_ACROSS_USERS, + PackageManager.PERMISSION_GRANTED); assertEquals(wantedUserId, spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(wantedUserId)); @@ -500,8 +532,8 @@ public class AccessibilitySecurityPolicyTest { .thenReturn(currentUserId); doReturn(callingParentId).when(spySecurityPolicy).resolveProfileParentLocked( callingUserId); - when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)) - .thenReturn(PackageManager.PERMISSION_GRANTED); + mContext.getTestablePermissions().setPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, PackageManager.PERMISSION_GRANTED); assertEquals(wantedUserId, spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(wantedUserId)); @@ -518,10 +550,10 @@ public class AccessibilitySecurityPolicyTest { .thenReturn(currentUserId); doReturn(callingParentId).when(spySecurityPolicy).resolveProfileParentLocked( callingUserId); - when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS)) - .thenReturn(PackageManager.PERMISSION_DENIED); - when(mMockContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)) - .thenReturn(PackageManager.PERMISSION_DENIED); + mContext.getTestablePermissions().setPermission(Manifest.permission.INTERACT_ACROSS_USERS, + PackageManager.PERMISSION_DENIED); + mContext.getTestablePermissions().setPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, PackageManager.PERMISSION_DENIED); spySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(wantedUserId); } @@ -562,4 +594,57 @@ public class AccessibilitySecurityPolicyTest { APP_UID, PACKAGE_NAME); } + @Test + public void onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction() { + final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>(); + boundServices.add(mMockA11yServiceConnection); + when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true); + + mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices); + + verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any()); + } + + @Test + public void onBoundServicesChanged_unbindA11yCategoryService_noUIControllerAction() { + onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction(); + + mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>()); + + verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(), + any()); + } + + @Test + public void onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction() { + final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>(); + boundServices.add(mMockA11yServiceConnection); + when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(false); + + mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices); + + verify(mPolicyWarningUIController).onNonA11yCategoryServiceBound(eq(TEST_USER_ID), + eq(TEST_COMPONENT_NAME)); + } + + @Test + public void onBoundServicesChanged_unbindNonA11yCategoryService_activateUIControllerAction() { + onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction(); + + mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>()); + + verify(mPolicyWarningUIController).onNonA11yCategoryServiceUnbound(eq(TEST_USER_ID), + eq(TEST_COMPONENT_NAME)); + } + + @Test + public void onSwitchUser_differentUser_activateUIControllerAction() { + onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction(); + + mA11ySecurityPolicy.onSwitchUserLocked(2, new HashSet<>()); + + verify(mPolicyWarningUIController).onSwitchUserLocked(eq(2), eq(new HashSet<>())); + verify(mPolicyWarningUIController).onNonA11yCategoryServiceUnbound(eq(TEST_USER_ID), + eq(TEST_COMPONENT_NAME)); + } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java new file mode 100644 index 000000000000..01a641f141ad --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java @@ -0,0 +1,241 @@ +/* + * 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.app.AlarmManager.RTC_WAKEUP; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_VIEW_AND_CONTROL_ACCESS; + +import static com.google.common.truth.Truth.assertThat; + +import static junit.framework.Assert.assertEquals; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.StatusBarManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Bundle; +import android.os.UserHandle; +import android.provider.Settings; +import android.testing.TestableContext; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Tests for the {@link PolicyWarningUIController}. + */ +public class PolicyWarningUIControllerTest { + private static final int TEST_USER_ID = UserHandle.USER_SYSTEM; + private static final ComponentName TEST_COMPONENT_NAME = new ComponentName( + "com.android.server.accessibility", "PolicyWarningUIControllerTest"); + + private final List<AccessibilityServiceInfo> mEnabledServiceList = new ArrayList<>(); + + @Rule + public final A11yTestableContext mContext = new A11yTestableContext( + getInstrumentation().getTargetContext()); + @Mock + private AlarmManager mAlarmManager; + @Mock + private NotificationManager mNotificationManager; + @Mock + private StatusBarManager mStatusBarManager; + @Mock + private AccessibilityServiceInfo mMockA11yServiceInfo; + @Mock + private ResolveInfo mMockResolveInfo; + @Mock + private ServiceInfo mMockServiceInfo; + @Mock + private Context mSpyContext; + @Mock + private AccessibilitySecurityPolicy mAccessibilitySecurityPolicy; + + private PolicyWarningUIController mPolicyWarningUIController; + private FakeNotificationController mFakeNotificationController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext.addMockSystemService(AlarmManager.class, mAlarmManager); + mContext.addMockSystemService(NotificationManager.class, mNotificationManager); + mContext.addMockSystemService(StatusBarManager.class, mStatusBarManager); + mFakeNotificationController = new FakeNotificationController(mContext); + mPolicyWarningUIController = new PolicyWarningUIController( + getInstrumentation().getTargetContext().getMainThreadHandler(), mContext, + mFakeNotificationController); + mPolicyWarningUIController.setAccessibilityPolicyManager(mAccessibilitySecurityPolicy); + mPolicyWarningUIController.onSwitchUserLocked(TEST_USER_ID, new HashSet<>()); + mEnabledServiceList.clear(); + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES, + "", TEST_USER_ID); + } + + @Test + public void receiveActionSendNotification_isNonA11yCategoryService_sendNotification() { + mEnabledServiceList.add(mMockA11yServiceInfo); + mMockResolveInfo.serviceInfo = mMockServiceInfo; + when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo); + when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME); + when(mAccessibilitySecurityPolicy.isA11yCategoryService( + mMockA11yServiceInfo)).thenReturn(false); + + mFakeNotificationController.onReceive(mContext, createIntent(TEST_USER_ID, + PolicyWarningUIController.ACTION_SEND_NOTIFICATION, + TEST_COMPONENT_NAME.flattenToShortString())); + + verify(mNotificationManager).notify(eq(TEST_COMPONENT_NAME.flattenToShortString()), + eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any( + Notification.class)); + } + + @Test + public void receiveActionA11ySettings_launchA11ySettingsAndDismissNotification() { + mFakeNotificationController.onReceive(mContext, + createIntent(TEST_USER_ID, PolicyWarningUIController.ACTION_A11Y_SETTINGS, + TEST_COMPONENT_NAME.flattenToShortString())); + + verifyLaunchA11ySettings(); + verify(mNotificationManager).cancel(TEST_COMPONENT_NAME.flattenToShortString(), + NOTE_A11Y_VIEW_AND_CONTROL_ACCESS); + assertNotifiedSettingsEqual(TEST_USER_ID, + TEST_COMPONENT_NAME.flattenToShortString()); + } + + @Test + public void receiveActionDismissNotification_addToNotifiedSettings() { + mFakeNotificationController.onReceive(mContext, createIntent(TEST_USER_ID, + PolicyWarningUIController.ACTION_DISMISS_NOTIFICATION, + TEST_COMPONENT_NAME.flattenToShortString())); + + assertNotifiedSettingsEqual(TEST_USER_ID, + TEST_COMPONENT_NAME.flattenToShortString()); + } + + @Test + public void onEnabledServicesChangedLocked_serviceDisabled_removedFromNotifiedSettings() { + final Set<ComponentName> enabledServices = new HashSet<>(); + enabledServices.add(TEST_COMPONENT_NAME); + mPolicyWarningUIController.onEnabledServicesChangedLocked(TEST_USER_ID, enabledServices); + getInstrumentation().waitForIdleSync(); + receiveActionDismissNotification_addToNotifiedSettings(); + + mPolicyWarningUIController.onEnabledServicesChangedLocked(TEST_USER_ID, new HashSet<>()); + getInstrumentation().waitForIdleSync(); + + assertNotifiedSettingsEqual(TEST_USER_ID, ""); + } + + @Test + public void onNonA11yCategoryServiceBound_setAlarm() { + mPolicyWarningUIController.onNonA11yCategoryServiceBound(TEST_USER_ID, TEST_COMPONENT_NAME); + getInstrumentation().waitForIdleSync(); + + verify(mAlarmManager).set(eq(RTC_WAKEUP), anyLong(), + eq(PolicyWarningUIController.createPendingIntent(mContext, TEST_USER_ID, + PolicyWarningUIController.ACTION_SEND_NOTIFICATION, + TEST_COMPONENT_NAME.flattenToShortString()))); + } + + @Test + public void onNonA11yCategoryServiceUnbound_cancelAlarm() { + mPolicyWarningUIController.onNonA11yCategoryServiceUnbound(TEST_USER_ID, + TEST_COMPONENT_NAME); + getInstrumentation().waitForIdleSync(); + + verify(mAlarmManager).cancel( + eq(PolicyWarningUIController.createPendingIntent(mContext, TEST_USER_ID, + PolicyWarningUIController.ACTION_SEND_NOTIFICATION, + TEST_COMPONENT_NAME.flattenToShortString()))); + } + + private void assertNotifiedSettingsEqual(int userId, String settingString) { + final String notifiedServicesSetting = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES, + userId); + assertEquals(settingString, notifiedServicesSetting); + } + + private Intent createIntent(int userId, String action, String serviceComponentName) { + final Intent intent = new Intent(action); + intent.setPackage(mContext.getPackageName()) + .setIdentifier(serviceComponentName) + .putExtra(Intent.EXTRA_USER_ID, userId); + return intent; + } + + private void verifyLaunchA11ySettings() { + final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + final ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass( + UserHandle.class); + verify(mSpyContext).startActivityAsUser(intentCaptor.capture(), + any(), userHandleCaptor.capture()); + assertThat(intentCaptor.getValue().getAction()).isEqualTo( + Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS); + assertThat(userHandleCaptor.getValue().getIdentifier()).isEqualTo(TEST_USER_ID); + verify(mStatusBarManager).collapsePanels(); + } + + private class A11yTestableContext extends TestableContext { + A11yTestableContext(Context base) { + super(base); + } + + @Override + public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) { + mSpyContext.startActivityAsUser(intent, options, user); + } + } + + private class FakeNotificationController extends + PolicyWarningUIController.NotificationController { + FakeNotificationController(Context context) { + super(context); + } + + @Override + protected List<AccessibilityServiceInfo> getEnabledServiceInfos() { + return mEnabledServiceList; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java index b5f49124483e..ad22cbaef575 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java @@ -103,12 +103,12 @@ public class AppSearchImplPlatformTest { // Insert package1 document GenericDocument document1 = new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build(); - mAppSearchImpl.putDocument("package1", "database1", document1); + mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null); // Insert package2 document GenericDocument document2 = new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build(); - mAppSearchImpl.putDocument("package2", "database2", document2); + mAppSearchImpl.putDocument("package2", "database2", document2, /*logger=*/ null); // No query filters specified, global query can retrieve all documents. SearchSpec searchSpec = @@ -155,12 +155,12 @@ public class AppSearchImplPlatformTest { // Insert package1 document GenericDocument document1 = new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build(); - mAppSearchImpl.putDocument("package1", "database1", document1); + mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null); // Insert package2 document GenericDocument document2 = new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build(); - mAppSearchImpl.putDocument("package2", "database2", document2); + mAppSearchImpl.putDocument("package2", "database2", document2, /*logger=*/ null); // "package1" filter specified SearchSpec searchSpec = diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java index c84c1cf8c8f1..e0cdeddc200e 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java @@ -416,7 +416,7 @@ public class AppSearchImplTest { i++) { GenericDocument document = new GenericDocument.Builder<>("namespace", "uri" + i, "type").build(); - mAppSearchImpl.putDocument("package", "database", document); + mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); } // Check optimize() will release 0 docs since there is no deletion. @@ -476,7 +476,7 @@ public class AppSearchImplTest { // Insert document GenericDocument document = new GenericDocument.Builder<>("namespace", "uri", "type").build(); - mAppSearchImpl.putDocument("package", "database", document); + mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); // Rewrite SearchSpec mAppSearchImpl.rewriteSearchSpecForPrefixesLocked( @@ -516,11 +516,11 @@ public class AppSearchImplTest { // Insert documents GenericDocument document1 = new GenericDocument.Builder<>("namespace", "uri", "typeA").build(); - mAppSearchImpl.putDocument("package", "database1", document1); + mAppSearchImpl.putDocument("package", "database1", document1, /*logger=*/ null); GenericDocument document2 = new GenericDocument.Builder<>("namespace", "uri", "typeB").build(); - mAppSearchImpl.putDocument("package", "database2", document2); + mAppSearchImpl.putDocument("package", "database2", document2, /*logger=*/ null); // Rewrite SearchSpec mAppSearchImpl.rewriteSearchSpecForPrefixesLocked( @@ -560,7 +560,7 @@ public class AppSearchImplTest { // Insert document GenericDocument document = new GenericDocument.Builder<>("namespace", "uri", "type").build(); - mAppSearchImpl.putDocument("package", "database", document); + mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); // If 'allowedPrefixedSchemas' is empty, this returns false since there's nothing to // search over. Despite the searchSpecProto having schema type filters. @@ -613,7 +613,7 @@ public class AppSearchImplTest { // Insert package1 document GenericDocument document = new GenericDocument.Builder<>("namespace", "uri", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document); + mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null); // No query filters specified, package2 shouldn't be able to query for package1's documents. SearchSpec searchSpec = @@ -624,7 +624,7 @@ public class AppSearchImplTest { // Insert package2 document document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build(); - mAppSearchImpl.putDocument("package2", "database2", document); + mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null); // No query filters specified. package2 should only get its own documents back. searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec); @@ -663,7 +663,7 @@ public class AppSearchImplTest { // Insert package1 document GenericDocument document = new GenericDocument.Builder<>("namespace", "uri", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document); + mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null); // "package1" filter specified, but package2 shouldn't be able to query for package1's // documents. @@ -678,7 +678,7 @@ public class AppSearchImplTest { // Insert package2 document document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build(); - mAppSearchImpl.putDocument("package2", "database2", document); + mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null); // "package2" filter specified, package2 should only get its own documents back. searchSpec = @@ -1124,7 +1124,8 @@ public class AppSearchImplTest { appSearchImpl.putDocument( "package", "database", - new GenericDocument.Builder<>("namespace", "uri", "type").build()); + new GenericDocument.Builder<>("namespace", "uri", "type").build(), + /*logger=*/ null); }); expectThrows( diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java new file mode 100644 index 000000000000..467ede4efc1c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java @@ -0,0 +1,144 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appsearch.external.localstorage; + +import static com.google.common.truth.Truth.assertThat; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.appsearch.AppSearchResult; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.server.appsearch.external.localstorage.stats.CallStats; +import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; +import com.android.server.appsearch.proto.PutDocumentStatsProto; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.util.Collections; +import java.util.List; + +public class AppSearchLoggerTest { + @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); + private AppSearchImpl mAppSearchImpl; + private TestLogger mLogger; + + @Before + public void setUp() throws Exception { + Context context = ApplicationProvider.getApplicationContext(); + + // Give ourselves global query permissions + mAppSearchImpl = + AppSearchImpl.create( + mTemporaryFolder.newFolder(), + context, + VisibilityStore.NO_OP_USER_ID, + /*globalQuerierPackage=*/ context.getPackageName()); + mLogger = new TestLogger(); + } + + // Test only not thread safe. + public class TestLogger implements AppSearchLogger { + @Nullable PutDocumentStats mPutDocumentStats; + + @Override + public void logStats(@NonNull CallStats stats) { + throw new UnsupportedOperationException(); + } + + @Override + public void logStats(@NonNull PutDocumentStats stats) { + mPutDocumentStats = stats; + } + } + + @Test + public void testAppSearchLoggerHelper_testCopyNativeStats_putDocument() { + final int nativeLatencyMillis = 3; + final int nativeDocumentStoreLatencyMillis = 4; + final int nativeIndexLatencyMillis = 5; + final int nativeIndexMergeLatencyMillis = 6; + final int nativeDocumentSize = 7; + final int nativeNumTokensIndexed = 8; + final boolean nativeExceededMaxNumTokens = true; + PutDocumentStatsProto nativePutDocumentStats = + PutDocumentStatsProto.newBuilder() + .setLatencyMs(nativeLatencyMillis) + .setDocumentStoreLatencyMs(nativeDocumentStoreLatencyMillis) + .setIndexLatencyMs(nativeIndexLatencyMillis) + .setIndexMergeLatencyMs(nativeIndexMergeLatencyMillis) + .setDocumentSize(nativeDocumentSize) + .setTokenizationStats( + PutDocumentStatsProto.TokenizationStats.newBuilder() + .setNumTokensIndexed(nativeNumTokensIndexed) + .setExceededMaxTokenNum(nativeExceededMaxNumTokens) + .build()) + .build(); + PutDocumentStats.Builder pBuilder = new PutDocumentStats.Builder("packageName", "database"); + + AppSearchLoggerHelper.copyNativeStats(nativePutDocumentStats, pBuilder); + + PutDocumentStats pStats = pBuilder.build(); + assertThat(pStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); + assertThat(pStats.getNativeDocumentStoreLatencyMillis()) + .isEqualTo(nativeDocumentStoreLatencyMillis); + assertThat(pStats.getNativeIndexLatencyMillis()).isEqualTo(nativeIndexLatencyMillis); + assertThat(pStats.getNativeIndexMergeLatencyMillis()) + .isEqualTo(nativeIndexMergeLatencyMillis); + assertThat(pStats.getNativeDocumentSizeBytes()).isEqualTo(nativeDocumentSize); + assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed); + assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens); + } + + // + // Testing actual logging + // + @Test + public void testLoggingStats_putDocument() throws Exception { + // Insert schema + final String testPackageName = "testPackage"; + final String testDatabase = "testDatabase"; + List<AppSearchSchema> schemas = + Collections.singletonList(new AppSearchSchema.Builder("type").build()); + mAppSearchImpl.setSchema( + testPackageName, + testDatabase, + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*forceOverride=*/ false); + GenericDocument document = + new GenericDocument.Builder<>("namespace", "uri", "type").build(); + + mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger); + + PutDocumentStats pStats = mLogger.mPutDocumentStats; + assertThat(pStats).isNotNull(); + assertThat(pStats.getGeneralStats().getPackageName()).isEqualTo(testPackageName); + assertThat(pStats.getGeneralStats().getDatabase()).isEqualTo(testDatabase); + assertThat(pStats.getGeneralStats().getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); + // The rest of native stats have been tested in testCopyNativeStats + assertThat(pStats.getNativeDocumentSizeBytes()).isGreaterThan(0); + } +} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java index 4308885faaad..8dbf249c4e96 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java @@ -18,12 +18,14 @@ package com.android.server.appsearch.external.localstorage.stats; import static com.google.common.truth.Truth.assertThat; +import android.app.appsearch.AppSearchResult; + import org.junit.Test; public class AppSearchStatsTest { static final String TEST_PACKAGE_NAME = "com.google.test"; static final String TEST_DATA_BASE = "testDataBase"; - static final int TEST_STATUS_CODE = 2; + static final int TEST_STATUS_CODE = AppSearchResult.RESULT_INTERNAL_ERROR; static final int TEST_TOTAL_LATENCY_MILLIS = 20; @Test @@ -40,25 +42,38 @@ public class AppSearchStatsTest { assertThat(gStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); } + /** Make sure status code is UNKNOWN if not set in {@link GeneralStats} */ @Test - public void testAppSearchStats_CallStats() { - final int estimatedBinderLatencyMillis = 1; - final int numOperationsSucceeded = 2; - final int numOperationsFailed = 3; - + public void testAppSearchStats_GeneralStats_defaultStatsCode_Unknown() { final GeneralStats gStats = new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) - .setStatusCode(TEST_STATUS_CODE) .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) .build(); + + assertThat(gStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); + assertThat(gStats.getDatabase()).isEqualTo(TEST_DATA_BASE); + assertThat(gStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_UNKNOWN_ERROR); + assertThat(gStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); + } + + @Test + public void testAppSearchStats_CallStats() { + final int estimatedBinderLatencyMillis = 1; + final int numOperationsSucceeded = 2; + final int numOperationsFailed = 3; final @CallStats.CallType int callType = CallStats.CALL_TYPE_PUT_DOCUMENTS; - final CallStats cStats = - new CallStats.Builder(gStats) + + final CallStats.Builder cStatsBuilder = + new CallStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) .setCallType(callType) .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) .setNumOperationsSucceeded(numOperationsSucceeded) - .setNumOperationsFailed(numOperationsFailed) - .build(); + .setNumOperationsFailed(numOperationsFailed); + cStatsBuilder + .getGeneralStatsBuilder() + .setStatusCode(TEST_STATUS_CODE) + .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS); + final CallStats cStats = cStatsBuilder.build(); assertThat(cStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME); assertThat(cStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE); @@ -82,15 +97,9 @@ public class AppSearchStatsTest { final int nativeIndexMergeLatencyMillis = 6; final int nativeDocumentSize = 7; final int nativeNumTokensIndexed = 8; - final int nativeNumTokensClipped = 9; - - final GeneralStats gStats = - new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) - .setStatusCode(TEST_STATUS_CODE) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) - .build(); - final PutDocumentStats pStats = - new PutDocumentStats.Builder(gStats) + final boolean nativeExceededMaxNumTokens = true; + final PutDocumentStats.Builder pStatsBuilder = + new PutDocumentStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) .setGenerateDocumentProtoLatencyMillis(generateDocumentProtoLatencyMillis) .setRewriteDocumentTypesLatencyMillis(rewriteDocumentTypesLatencyMillis) .setNativeLatencyMillis(nativeLatencyMillis) @@ -99,8 +108,12 @@ public class AppSearchStatsTest { .setNativeIndexMergeLatencyMillis(nativeIndexMergeLatencyMillis) .setNativeDocumentSizeBytes(nativeDocumentSize) .setNativeNumTokensIndexed(nativeNumTokensIndexed) - .setNativeNumTokensClipped(nativeNumTokensClipped) - .build(); + .setNativeExceededMaxNumTokens(nativeExceededMaxNumTokens); + pStatsBuilder + .getGeneralStatsBuilder() + .setStatusCode(TEST_STATUS_CODE) + .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS); + final PutDocumentStats pStats = pStatsBuilder.build(); assertThat(pStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME); assertThat(pStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE); @@ -119,6 +132,6 @@ public class AppSearchStatsTest { .isEqualTo(nativeIndexMergeLatencyMillis); assertThat(pStats.getNativeDocumentSizeBytes()).isEqualTo(nativeDocumentSize); assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed); - assertThat(pStats.getNativeNumTokensClipped()).isEqualTo(nativeNumTokensClipped); + assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens); } } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java new file mode 100644 index 000000000000..d4222e6e0b63 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java @@ -0,0 +1,56 @@ +/* + * 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.timedetector; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.time.Capabilities; +import android.app.time.TimeCapabilities; +import android.app.time.TimeCapabilitiesAndConfig; +import android.app.time.TimeConfiguration; +import android.os.UserHandle; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class ConfigurationInternalTest { + + @Test + public void capabilitiesAndConfig() { + int userId = 112233; + ConfigurationInternal configurationInternal = new ConfigurationInternal.Builder(userId) + .setAutoDetectionEnabled(true) + .setUserConfigAllowed(true) + .build(); + + TimeCapabilities timeCapabilities = new TimeCapabilities.Builder(UserHandle.of(userId)) + .setConfigureAutoTimeDetectionEnabledCapability(Capabilities.CAPABILITY_POSSESSED) + .setSuggestTimeManuallyCapability(Capabilities.CAPABILITY_POSSESSED) + .build(); + TimeConfiguration timeConfiguration = new TimeConfiguration.Builder() + .setAutoDetectionEnabled(true) + .build(); + TimeCapabilitiesAndConfig expected = + new TimeCapabilitiesAndConfig(timeCapabilities, timeConfiguration); + + assertThat(configurationInternal.capabilitiesAndConfig()).isEqualTo(expected); + } + +} diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index bbf11fd557a3..106827078290 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -328,6 +328,11 @@ public class TimeDetectorServiceTest { } @Override + public ConfigurationInternal getConfigurationInternal(int userId) { + throw new UnsupportedOperationException(); + } + + @Override public void handleAutoTimeConfigChanged() { mHandleAutoTimeDetectionChangedCalled = true; } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index f7a498bc9d73..095703e6b939 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -1158,6 +1158,11 @@ public class TimeDetectorStrategyImplTest { } @Override + public ConfigurationInternal configurationInternal(int userId) { + throw new UnsupportedOperationException(); + } + + @Override public void acquireWakeLock() { if (mWakeLockAcquired) { fail("Wake lock already acquired"); diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java index 00369829db56..aa46e7ef658f 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java @@ -16,10 +16,10 @@ package com.android.server.timezonedetector; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; +import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; 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 c98e013479f4..956c277e18c1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -18,11 +18,13 @@ 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; @@ -34,8 +36,11 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; +import static junit.framework.Assert.fail; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -96,7 +101,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, mAdapter, mHandler); + mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter, mHandler); } private WindowState createAppOverlayWindow() { @@ -525,6 +530,110 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { } } + @Test + public void testNonAppTarget_sendNavBar() throws Exception { + final int transit = TRANSIT_OLD_TASK_OPEN; + final AnimationAdapter adapter = setupForNonAppTargetNavBar(transit, true); + + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = + ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); + verify(mMockRunner).onAnimationStart(eq(transit), any(), any(), + 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); + assertEquals(1, mController.mPendingNonAppAnimations.size()); + final NonAppWindowAnimationAdapter nonAppAdapter = + mController.mPendingNonAppAnimations.get(0); + spyOn(nonAppAdapter.getLeashFinishedCallback()); + + finishedCaptor.getValue().onAnimationFinished(); + verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), + eq(adapter)); + verify(nonAppAdapter.getLeashFinishedCallback()) + .onAnimationFinished(nonAppAdapter.getLastAnimationType(), nonAppAdapter); + } + + @Test + public void testNonAppTarget_notSendNavBar_notAttachToApp() throws Exception { + final int transit = TRANSIT_OLD_TASK_OPEN; + setupForNonAppTargetNavBar(transit, false); + + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + verify(mMockRunner).onAnimationStart(eq(transit), + any(), any(), nonAppsCaptor.capture(), any()); + for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { + if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { + fail("Non-app animation target must not contain navbar"); + } + } + } + + @Test + public void testNonAppTarget_notSendNavBar_controlledByFixedRotation() throws Exception { + final FixedRotationAnimationController mockController = + mock(FixedRotationAnimationController.class); + doReturn(mockController).when(mDisplayContent).getFixedRotationAnimationController(); + final int transit = TRANSIT_OLD_TASK_OPEN; + setupForNonAppTargetNavBar(transit, true); + + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + verify(mMockRunner).onAnimationStart(eq(transit), + any(), any(), nonAppsCaptor.capture(), any()); + for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { + if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { + fail("Non-app animation target must not contain navbar"); + } + } + } + + @Test + public void testNonAppTarget_notSendNavBar_controlledByRecents() throws Exception { + final RecentsAnimationController mockController = + mock(RecentsAnimationController.class); + doReturn(mockController).when(mWm).getRecentsAnimationController(); + final int transit = TRANSIT_OLD_TASK_OPEN; + setupForNonAppTargetNavBar(transit, true); + + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + verify(mMockRunner).onAnimationStart(eq(transit), + any(), any(), nonAppsCaptor.capture(), any()); + for (int i = 0; i < nonAppsCaptor.getValue().length; i++) { + if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) { + fail("Non-app animation target must not contain navbar"); + } + } + } + + private AnimationAdapter setupForNonAppTargetNavBar(int transit, boolean shouldAttachNavBar) { + 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(shouldAttachNavBar).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); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + return adapter; + } + private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { verify(binder, atLeast(0)).asBinder(); verifyNoMoreInteractions(binder); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 171aa76032db..1b114c194bfb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -116,8 +116,7 @@ public class RootWindowContainerTests extends WindowTestsBase { final Task rootTask = new TaskBuilder(mSupervisor).build(); final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); new ActivityBuilder(mAtm).setTask(task1).build().mVisibleRequested = true; - // RootWindowContainer#invalidateTaskLayers should post to update. - waitHandlerIdle(mWm.mH); + mWm.mRoot.rankTaskLayers(); assertEquals(1, task1.mLayerRank); // Only tasks that directly contain activities have a ranking. @@ -125,7 +124,7 @@ public class RootWindowContainerTests extends WindowTestsBase { final Task task2 = new TaskBuilder(mSupervisor).build(); new ActivityBuilder(mAtm).setTask(task2).build().mVisibleRequested = true; - waitHandlerIdle(mWm.mH); + mWm.mRoot.rankTaskLayers(); // Note that ensureActivitiesVisible is disabled in SystemServicesTestRule, so both the // activities have the visible rank. @@ -134,6 +133,7 @@ public class RootWindowContainerTests extends WindowTestsBase { assertEquals(1, task2.mLayerRank); task2.moveToBack("test", null /* task */); + // RootWindowContainer#invalidateTaskLayers should post to update. waitHandlerIdle(mWm.mH); assertEquals(1, task1.mLayerRank); diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java index c53c95cc982e..d6c046921c57 100644 --- a/services/usb/java/com/android/server/usb/UsbPortManager.java +++ b/services/usb/java/com/android/server/usb/UsbPortManager.java @@ -60,9 +60,9 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; +import android.service.ServiceProtoEnums; import android.service.usb.UsbPortInfoProto; import android.service.usb.UsbPortManagerProto; -import android.service.usb.UsbServiceProto; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; @@ -1061,15 +1061,15 @@ public class UsbPortManager { private static int convertContaminantDetectionStatusToProto(int contaminantDetectionStatus) { switch (contaminantDetectionStatus) { case UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED: - return UsbServiceProto.CONTAMINANT_STATUS_NOT_SUPPORTED; + return ServiceProtoEnums.CONTAMINANT_STATUS_NOT_SUPPORTED; case UsbPortStatus.CONTAMINANT_DETECTION_DISABLED: - return UsbServiceProto.CONTAMINANT_STATUS_DISABLED; + return ServiceProtoEnums.CONTAMINANT_STATUS_DISABLED; case UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED: - return UsbServiceProto.CONTAMINANT_STATUS_NOT_DETECTED; + return ServiceProtoEnums.CONTAMINANT_STATUS_NOT_DETECTED; case UsbPortStatus.CONTAMINANT_DETECTION_DETECTED: - return UsbServiceProto.CONTAMINANT_STATUS_DETECTED; + return ServiceProtoEnums.CONTAMINANT_STATUS_DETECTED; default: - return UsbServiceProto.CONTAMINANT_STATUS_UNKNOWN; + return ServiceProtoEnums.CONTAMINANT_STATUS_UNKNOWN; } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 2626bfdc3d19..80d4f8fd0c2f 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -985,6 +985,7 @@ public class VoiceInteractionManagerService extends SystemService { @Override public void setHotwordDetectionServiceConfig(@Nullable Bundle options, @Nullable SharedMemory sharedMemory) { + enforceCallingPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION); synchronized (this) { enforceIsCurrentVoiceInteractionService(); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 47fbe1350a79..74421a08b22d 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4077,6 +4077,22 @@ public class CarrierConfigManager { "is_opportunistic_subscription_bool"; /** + * The flatten string {@link android.content.ComponentName componentName} of carrier + * provisioning app receiver. + * + * <p> + * The RadioInfo activity(*#*#INFO#*#*) will broadcast an intent to this receiver when the + * "Carrier Provisioning Info" or "Trigger Carrier Provisioning" button clicked. + * + * <p> + * e.g, com.google.android.carrierPackageName/.CarrierReceiverName + * + * @hide + */ + public static final String KEY_CARRIER_PROVISIONING_APP_STRING = + "carrier_provisioning_app_string"; + + /** * Configs used by the IMS stack. */ public static final class Ims { @@ -5374,6 +5390,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY, new String[]{"ia", "default", "ims", "mms", "dun", "emergency"}); sDefaults.putBoolean(KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL, false); + sDefaults.putString(KEY_CARRIER_PROVISIONING_APP_STRING, ""); } /** diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index ffe5399e406b..ef02589abaf8 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -139,7 +139,7 @@ public final class DataCallResponse implements Parcelable { private final int mPduSessionId; private final Qos mDefaultQos; private final List<QosBearerSession> mQosBearerSessions; - private final SliceInfo mSliceInfo; + private final NetworkSliceInfo mSliceInfo; private final List<TrafficDescriptor> mTrafficDescriptors; /** @@ -202,7 +202,8 @@ public final class DataCallResponse implements Parcelable { @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6, @HandoverFailureMode int handoverFailureMode, int pduSessionId, @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions, - @Nullable SliceInfo sliceInfo, @Nullable List<TrafficDescriptor> trafficDescriptors) { + @Nullable NetworkSliceInfo sliceInfo, + @Nullable List<TrafficDescriptor> trafficDescriptors) { mCause = cause; mSuggestedRetryTime = suggestedRetryTime; mId = id; @@ -255,7 +256,7 @@ public final class DataCallResponse implements Parcelable { mDefaultQos = source.readParcelable(Qos.class.getClassLoader()); mQosBearerSessions = new ArrayList<>(); source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader()); - mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader()); + mSliceInfo = source.readParcelable(NetworkSliceInfo.class.getClassLoader()); mTrafficDescriptors = new ArrayList<>(); source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader()); } @@ -410,7 +411,7 @@ public final class DataCallResponse implements Parcelable { * @return The slice info related to this data connection. */ @Nullable - public SliceInfo getSliceInfo() { + public NetworkSliceInfo getSliceInfo() { return mSliceInfo; } @@ -626,7 +627,7 @@ public final class DataCallResponse implements Parcelable { private List<QosBearerSession> mQosBearerSessions = new ArrayList<>(); - private SliceInfo mSliceInfo; + private NetworkSliceInfo mSliceInfo; private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>(); @@ -865,13 +866,13 @@ public final class DataCallResponse implements Parcelable { * The Slice used for this data connection. * <p/> * If a handover occurs from EPDG to 5G, - * this is the {@link SliceInfo} used in {@link DataService#setupDataCall}. + * this is the {@link NetworkSliceInfo} used in {@link DataService#setupDataCall}. * * @param sliceInfo the slice info for the data call * * @return The same instance of the builder. */ - public @NonNull Builder setSliceInfo(@Nullable SliceInfo sliceInfo) { + public @NonNull Builder setSliceInfo(@Nullable NetworkSliceInfo sliceInfo) { mSliceInfo = sliceInfo; return this; } diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index 048b3297a1b4..2f034752ae5f 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -218,7 +218,7 @@ public abstract class DataService extends Service { boolean isRoaming, boolean allowRoaming, @SetupDataReason int reason, @Nullable LinkProperties linkProperties, - @IntRange(from = 0, to = 15) int pduSessionId, @Nullable SliceInfo sliceInfo, + @IntRange(from = 0, to = 15) int pduSessionId, @Nullable NetworkSliceInfo sliceInfo, @Nullable TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, @NonNull DataServiceCallback callback) { /* Call the old version since the new version isn't supported */ @@ -418,13 +418,13 @@ public abstract class DataService extends Service { public final int reason; public final LinkProperties linkProperties; public final int pduSessionId; - public final SliceInfo sliceInfo; + public final NetworkSliceInfo sliceInfo; public final TrafficDescriptor trafficDescriptor; public final boolean matchAllRuleAllowed; public final IDataServiceCallback callback; SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId, - SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, + NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, IDataServiceCallback callback) { this.accessNetworkType = accessNetworkType; this.dataProfile = dataProfile; @@ -711,7 +711,7 @@ public abstract class DataService extends Service { @Override public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, - LinkProperties linkProperties, int pduSessionId, SliceInfo sliceInfo, + LinkProperties linkProperties, int pduSessionId, NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, IDataServiceCallback callback) { mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0, diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl index 81f5fd3b69a9..134694694a0e 100644 --- a/telephony/java/android/telephony/data/IDataService.aidl +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -19,7 +19,7 @@ package android.telephony.data; import android.net.LinkProperties; import android.telephony.data.DataProfile; import android.telephony.data.IDataServiceCallback; -import android.telephony.data.SliceInfo; +import android.telephony.data.NetworkSliceInfo; import android.telephony.data.TrafficDescriptor; /** @@ -31,7 +31,7 @@ oneway interface IDataService void removeDataServiceProvider(int slotId); void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, in LinkProperties linkProperties, - int pduSessionId, in SliceInfo sliceInfo, + int pduSessionId, in NetworkSliceInfo sliceInfo, in TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, IDataServiceCallback callback); void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback); diff --git a/telephony/java/android/telephony/data/SliceInfo.aidl b/telephony/java/android/telephony/data/NetworkSliceInfo.aidl index 286ea5e4f8c7..e1a11f2b87b8 100644 --- a/telephony/java/android/telephony/data/SliceInfo.aidl +++ b/telephony/java/android/telephony/data/NetworkSliceInfo.aidl @@ -17,4 +17,4 @@ /** @hide */ package android.telephony.data; -parcelable SliceInfo; +parcelable NetworkSliceInfo; diff --git a/telephony/java/android/telephony/data/SliceInfo.java b/telephony/java/android/telephony/data/NetworkSliceInfo.java index 51857a7b4908..3383696c56ac 100644 --- a/telephony/java/android/telephony/data/SliceInfo.java +++ b/telephony/java/android/telephony/data/NetworkSliceInfo.java @@ -34,7 +34,7 @@ import java.util.Objects; * @hide */ @SystemApi -public final class SliceInfo implements Parcelable { +public final class NetworkSliceInfo implements Parcelable { /** * When set on a Slice Differentiator, this value indicates that there is no corresponding * Slice. @@ -93,7 +93,7 @@ public final class SliceInfo implements Parcelable { @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) private final int mMappedHplmnSliceDifferentiator; - private SliceInfo(@SliceServiceType int sliceServiceType, + private NetworkSliceInfo(@SliceServiceType int sliceServiceType, int sliceDifferentiator, int mappedHplmnSliceServiceType, int mappedHplmnSliceDifferentiator) { mSliceServiceType = sliceServiceType; @@ -136,7 +136,7 @@ public final class SliceInfo implements Parcelable { } /** - * This Slice Differentiator corresponds to a {@link SliceInfo} (S-NSSAI) of the HPLMN; + * This Slice Differentiator corresponds to a {@link NetworkSliceInfo} (S-NSSAI) of the HPLMN; * {@link #getSliceDifferentiator()} is mapped to this value. * <p/> * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if either of the following are true: @@ -152,7 +152,7 @@ public final class SliceInfo implements Parcelable { return mMappedHplmnSliceDifferentiator; } - private SliceInfo(@NonNull Parcel in) { + private NetworkSliceInfo(@NonNull Parcel in) { mSliceServiceType = in.readInt(); mSliceDifferentiator = in.readInt(); mMappedHplmnSliceServiceType = in.readInt(); @@ -172,18 +172,18 @@ public final class SliceInfo implements Parcelable { dest.writeInt(mMappedHplmnSliceDifferentiator); } - public static final @android.annotation.NonNull Parcelable.Creator<SliceInfo> CREATOR = - new Parcelable.Creator<SliceInfo>() { + public static final @android.annotation.NonNull Parcelable.Creator<NetworkSliceInfo> CREATOR = + new Parcelable.Creator<NetworkSliceInfo>() { @Override @NonNull - public SliceInfo createFromParcel(@NonNull Parcel source) { - return new SliceInfo(source); + public NetworkSliceInfo createFromParcel(@NonNull Parcel source) { + return new NetworkSliceInfo(source); } @Override @NonNull - public SliceInfo[] newArray(int size) { - return new SliceInfo[size]; + public NetworkSliceInfo[] newArray(int size) { + return new NetworkSliceInfo[size]; } }; @@ -217,7 +217,7 @@ public final class SliceInfo implements Parcelable { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - SliceInfo sliceInfo = (SliceInfo) o; + NetworkSliceInfo sliceInfo = (NetworkSliceInfo) o; return mSliceServiceType == sliceInfo.mSliceServiceType && mSliceDifferentiator == sliceInfo.mSliceDifferentiator && mMappedHplmnSliceServiceType == sliceInfo.mMappedHplmnSliceServiceType @@ -231,7 +231,7 @@ public final class SliceInfo implements Parcelable { } /** - * Provides a convenient way to set the fields of a {@link SliceInfo} when creating a + * Provides a convenient way to set the fields of a {@link NetworkSliceInfo} when creating a * new instance. * * <p>The example below shows how you might create a new {@code SliceInfo}: @@ -329,13 +329,13 @@ public final class SliceInfo implements Parcelable { } /** - * Build the {@link SliceInfo}. + * Build the {@link NetworkSliceInfo}. * - * @return the {@link SliceInfo} object. + * @return the {@link NetworkSliceInfo} object. */ @NonNull - public SliceInfo build() { - return new SliceInfo(this.mSliceServiceType, this.mSliceDifferentiator, + public NetworkSliceInfo build() { + return new NetworkSliceInfo(this.mSliceServiceType, this.mSliceDifferentiator, this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator); } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index d669a01b42f3..e118363de58d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -19,7 +19,6 @@ package com.android.server.wm.flicker.close import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface -import androidx.test.filters.FlakyTest import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerTestParameter @@ -38,7 +37,6 @@ import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer import com.android.server.wm.flicker.wallpaperWindowBecomesVisible -import org.junit.Assume import org.junit.Test abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) { @@ -92,32 +90,16 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) @Presubmit @Test open fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest - @Test - open fun navBarLayerRotatesAndScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) } @Presubmit @Test open fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest - @Test - open fun statusBarLayerRotatesScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) } - @FlakyTest(bugId = 173689015) + @Presubmit @Test open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { testSpec.assertWm { @@ -125,7 +107,7 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) } } - @FlakyTest(bugId = 173689015) + @Presubmit @Test open fun visibleLayersShownMoreThanOneConsecutiveEntry() { testSpec.assertLayers { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt index aad06ee94914..90c23385d16b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt @@ -17,10 +17,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit -import android.view.Surface -import android.view.WindowManagerPolicyConstants import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -30,19 +27,15 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper -import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -64,23 +57,15 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } setup { - test { - device.wakeUpAndGoToHomeScreen() - } eachRun { testApp.launchViaIntent(wmHelper) testApp.openIME(device, wmHelper) - this.setRotation(testSpec.config.startRotation) } } teardown { test { - testApp.exit() - wmHelper.waitForAppTransitionIdle() - this.setRotation(Surface.ROTATION_0) + testApp.exit(wmHelper) } } transitions { @@ -89,15 +74,15 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter } } - @Postsubmit + @Presubmit @Test fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun visibleWindowsShownMoreThanOneConsecutiveEntry() { testSpec.assertWm { @@ -107,59 +92,43 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter } } - @Postsubmit + @Presubmit @Test fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp) - @Postsubmit + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - @Postsubmit + @Presubmit @Test fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) - @Postsubmit + @Presubmit @Test fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() - @Postsubmit + @Presubmit @Test fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp) - @Presubmit + @FlakyTest @Test fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(testSpec.isRotated) testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) } @FlakyTest @Test - fun navBarLayerRotatesAndScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) - } - - @Presubmit - @Test fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) - } - - @FlakyTest - @Test - fun statusBarLayerRotatesScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) } - @FlakyTest + @Presubmit @Test fun visibleLayersShownMoreThanOneConsecutiveEntry() { testSpec.assertLayers { @@ -172,12 +141,7 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests( - repetitions = 5, - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) - ) + .getConfigNonRotationTests(repetitions = 5) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt index d08e7e2e5191..b25bc997e9ab 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt @@ -19,7 +19,6 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface -import android.view.WindowManagerPolicyConstants import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -29,19 +28,15 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper -import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -63,22 +58,15 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } setup { - test { - device.wakeUpAndGoToHomeScreen() - } eachRun { testApp.launchViaIntent(wmHelper) testApp.openIME(device, wmHelper) - this.setRotation(testSpec.config.startRotation) } } teardown { test { - testApp.exit() - this.setRotation(Surface.ROTATION_0) + testApp.exit(wmHelper) } } transitions { @@ -128,31 +116,15 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete @Test fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp) - @Presubmit + @FlakyTest @Test fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(testSpec.isRotated) testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) } @FlakyTest @Test - fun navBarLayerRotatesAndScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @Presubmit - @Test fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest - @Test - fun statusBarLayerRotatesScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) } @@ -167,17 +139,6 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete @Presubmit @Test fun visibleLayersShownMoreThanOneConsecutiveEntry() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.assertLayers { - this.visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME)) - } - } - - @FlakyTest - @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() { - Assume.assumeTrue(testSpec.isRotated) testSpec.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry( listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME)) @@ -189,12 +150,7 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests( - repetitions = 5, - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) - ) + .getConfigNonRotationTests(repetitions = 5) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt index 19dec7abee54..6b8bf63aa926 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt @@ -18,8 +18,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit -import android.view.Surface -import android.view.WindowManagerPolicyConstants +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerBuilderProvider @@ -28,16 +27,14 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import org.junit.Assume import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.FixMethodOrder import org.junit.Test @@ -60,13 +57,9 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } setup { test { - device.wakeUpAndGoToHomeScreen() testApp.launchViaIntent() - this.setRotation(testSpec.config.startRotation) } eachRun { testApp.openIME(device, wmHelper) @@ -74,8 +67,7 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { } teardown { test { - testApp.exit() - this.setRotation(Surface.ROTATION_0) + testApp.exit(wmHelper) } } transitions { @@ -120,13 +112,31 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun navBarLayerRotatesAndScales() = + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + } @Presubmit @Test - fun statusBarLayerRotatesScales() = + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + } @Presubmit @Test @@ -149,12 +159,7 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests( - repetitions = 5, - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) - ) + .getConfigNonRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt index b214414ab7f1..9b37cafa74c3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt @@ -20,7 +20,6 @@ import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface import android.view.WindowManagerPolicyConstants -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerBuilderProvider @@ -29,18 +28,14 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper -import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -61,15 +56,9 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } setup { - test { - device.wakeUpAndGoToHomeScreen() - } eachRun { testApp.launchViaIntent(wmHelper) - this.setRotation(testSpec.config.startRotation) testApp.openIME(device, wmHelper) } } @@ -85,7 +74,6 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { } test { testApp.exit() - this.setRotation(Surface.ROTATION_0) } } } @@ -146,18 +134,10 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) } - @FlakyTest - @Test - fun statusBarLayerRotatesScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest + @Presubmit @Test fun visibleLayersShownMoreThanOneConsecutiveEntry() { testSpec.assertLayers { @@ -175,6 +155,7 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { repetitions = 5, supportedRotations = listOf(Surface.ROTATION_0), supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY ) ) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt index ab5e9b40463e..d39044ab285d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt @@ -20,7 +20,6 @@ import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface import android.view.WindowManagerPolicyConstants -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerBuilderProvider @@ -28,8 +27,6 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible @@ -37,12 +34,10 @@ import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.layerAlwaysVisible -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -64,13 +59,9 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } setup { test { - device.wakeUpAndGoToHomeScreen() testApp.launchViaIntent(wmHelper) - this.setRotation(testSpec.config.startRotation) } } transitions { @@ -82,7 +73,6 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { } test { testApp.exit() - this.setRotation(Surface.ROTATION_0) } } } @@ -127,32 +117,16 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) - } - - @FlakyTest - @Test - fun navBarLayerRotatesAndScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) } @Presubmit @Test fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) - } - - @FlakyTest - @Test - fun statusBarLayerRotatesScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) } - @FlakyTest + @Presubmit @Test fun visibleLayersShownMoreThanOneConsecutiveEntry() { testSpec.assertLayers { @@ -160,7 +134,7 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { } } - @FlakyTest + @Presubmit @Test fun visibleWindowsShownMoreThanOneConsecutiveEntry() { testSpec.assertWm { @@ -177,6 +151,7 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { repetitions = 5, supportedRotations = listOf(Surface.ROTATION_0), supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY ) ) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt index 52a826655993..95b1d3c28e47 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt @@ -30,7 +30,6 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible @@ -38,14 +37,12 @@ import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.testapp.ActivityOptions -import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -68,11 +65,8 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } setup { test { - device.wakeUpAndGoToHomeScreen() testApp.launchViaIntent(wmHelper) testApp.openIME(device, wmHelper) } @@ -89,7 +83,6 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { } teardown { test { - this.setRotation(Surface.ROTATION_0) testApp.exit() } } @@ -104,7 +97,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { @Test fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - @Presubmit + @FlakyTest @Test fun visibleWindowsShownMoreThanOneConsecutiveEntry() { testSpec.assertWm { @@ -148,35 +141,19 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { fun appLayerReplacesWallpaperLayer() = testSpec.appLayerReplacesWallpaperLayer(testAppComponentName.className) - @Presubmit + @FlakyTest @Test fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(testSpec.isRotated) testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) } @FlakyTest @Test - fun navBarLayerRotatesAndScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) - } - - @Presubmit - @Test fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) - } - - @FlakyTest - @Test - fun statusBarLayerRotatesScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) } - @FlakyTest + @Presubmit @Test fun visibleLayersShownMoreThanOneConsecutiveEntry() { testSpec.assertLayers { @@ -192,6 +169,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { .getConfigNonRotationTests( repetitions = 1, supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY ) ) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt index 2e59dac30f44..a19a95da021c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -16,7 +16,7 @@ package com.android.server.wm.flicker.launch -import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.Postsubmit import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -27,7 +27,6 @@ import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import com.android.server.wm.flicker.dsl.FlickerBuilder -import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -64,69 +63,21 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio } } + @Postsubmit @Test override fun appWindowReplacesLauncherAsTopWindow() = super.appWindowReplacesLauncherAsTopWindow() + @Postsubmit @Test override fun wallpaperWindowBecomesInvisible() { testSpec.wallpaperWindowBecomesInvisible() } - @Presubmit - @Test - override fun statusBarLayerIsAlwaysVisible() { - Assume.assumeTrue(testSpec.isRotated) - super.statusBarLayerIsAlwaysVisible() - } - - @Presubmit - @Test - override fun navBarLayerIsAlwaysVisible() { - Assume.assumeTrue(testSpec.isRotated) - super.navBarLayerIsAlwaysVisible() - } - - @FlakyTest - @Test - fun statusBarLayerIsAlwaysVisible_Flaky() { - Assume.assumeFalse(testSpec.isRotated) - super.statusBarLayerIsAlwaysVisible() - } - - @FlakyTest - @Test - fun navBarLayerIsAlwaysVisible_Flaky() { - Assume.assumeFalse(testSpec.isRotated) - super.navBarLayerIsAlwaysVisible() - } - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() { - Assume.assumeFalse(testSpec.isRotated) - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - } - - @FlakyTest - @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - } - - @Presubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() { - Assume.assumeFalse(testSpec.isRotated) - super.visibleLayersShownMoreThanOneConsecutiveEntry() - } - @FlakyTest @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - super.visibleLayersShownMoreThanOneConsecutiveEntry() + override fun navBarLayerRotatesAndScales() { + super.navBarLayerRotatesAndScales() } companion object { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt index 5197f0ebc720..cd5c61a5a927 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt @@ -19,7 +19,6 @@ package com.android.server.wm.flicker.launch import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface -import androidx.test.filters.FlakyTest import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerTestParameter @@ -41,7 +40,6 @@ import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible -import org.junit.Assume import org.junit.Test abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { @@ -86,14 +84,6 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { @Presubmit @Test open fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) - } - - @FlakyTest - @Test - open fun navBarLayerRotatesAndScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) } @@ -112,14 +102,6 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { @Presubmit @Test open fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) - } - - @FlakyTest - @Test - open fun statusBarLayerRotatesScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) } @@ -131,7 +113,7 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { } } - @FlakyTest + @Presubmit @Test open fun visibleLayersShownMoreThanOneConsecutiveEntry() { testSpec.assertLayers { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index dee7e5945b1c..dcc64c94e0c4 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -16,7 +16,6 @@ package com.android.server.wm.flicker.launch -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -25,7 +24,6 @@ import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.dsl.FlickerBuilder import org.junit.FixMethodOrder -import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -63,11 +61,6 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp } } - @FlakyTest - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt index 6985b360c9cc..f037f1d19583 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt @@ -17,12 +17,14 @@ package com.android.server.wm.flicker.rotation import android.platform.test.annotations.Presubmit +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper +import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -51,6 +53,26 @@ class ChangeAppRotationTest( } } + @FlakyTest(bugId = 151179149) + @Test + override fun focusDoesNotChange() { + super.focusDoesNotChange() + } + + @Presubmit + @Test + override fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + super.navBarLayerRotatesAndScales() + } + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerRotatesAndScales_flaky() { + Assume.assumeTrue(testSpec.isRotated) + super.navBarLayerRotatesAndScales() + } + @Presubmit @Test fun screenshotLayerBecomesInvisible() { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index e914f64bb6ca..6d2a9238bed5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt @@ -18,7 +18,6 @@ package com.android.server.wm.flicker.rotation import android.app.Instrumentation import android.platform.test.annotations.Presubmit -import androidx.test.filters.FlakyTest import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerTestParameter @@ -74,13 +73,13 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) testSpec.navBarWindowIsAlwaysVisible() } - @FlakyTest(bugId = 140855415) + @Presubmit @Test open fun navBarLayerIsAlwaysVisible() { testSpec.navBarLayerIsAlwaysVisible() } - @FlakyTest(bugId = 140855415) + @Presubmit @Test open fun navBarLayerRotatesAndScales() { testSpec.navBarLayerRotatesAndScales( @@ -93,20 +92,20 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) testSpec.statusBarWindowIsAlwaysVisible() } - @FlakyTest(bugId = 140855415) + @Presubmit @Test open fun statusBarLayerIsAlwaysVisible() { testSpec.statusBarLayerIsAlwaysVisible() } - @FlakyTest(bugId = 140855415) + @Presubmit @Test open fun statusBarLayerRotatesScales() { testSpec.statusBarLayerRotatesScales( testSpec.config.startRotation, testSpec.config.endRotation) } - @FlakyTest(bugId = 140855415) + @Presubmit @Test open fun visibleLayersShownMoreThanOneConsecutiveEntry() { testSpec.assertLayers { @@ -129,13 +128,13 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) testSpec.config.endRotation, allStates = false) } - @FlakyTest(bugId = 151179149) + @Presubmit @Test open fun focusDoesNotChange() { testSpec.focusDoesNotChange() } - @FlakyTest(bugId = 140855415) + @Presubmit @Test open fun appLayerRotates_StartingPos() { testSpec.assertLayersStart { @@ -143,7 +142,7 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) } } - @FlakyTest(bugId = 140855415) + @Presubmit @Test open fun appLayerRotates_EndingPos() { testSpec.assertLayersEnd { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt index 45d3006b9481..fe444bdecba7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt @@ -22,7 +22,6 @@ import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper import com.android.server.wm.flicker.layerAlwaysVisible @@ -61,26 +60,24 @@ class SeamlessAppRotationTest( @FlakyTest(bugId = 140855415) @Test - override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() + override fun navBarLayerRotatesAndScales() { + super.navBarLayerRotatesAndScales() + } @FlakyTest(bugId = 140855415) @Test - override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() - - @FlakyTest(bugId = 147659548) - @Test - override fun noUncoveredRegions() = super.noUncoveredRegions() + override fun statusBarLayerRotatesScales() { + super.statusBarLayerRotatesScales() + } @Presubmit @Test - fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`) + fun appLayerAlwaysVisible() { + testSpec.layerAlwaysVisible(testApp.`package`) + } @Presubmit @Test - fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`) - - @FlakyTest(bugId = 147659548) - @Test fun appLayerRotates() { testSpec.assertLayers { this.coversExactly(startingPos, testApp.`package`) diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java index d04c87b29c25..b7a42ec29356 100644 --- a/tests/net/java/android/net/VpnTransportInfoTest.java +++ b/tests/net/java/android/net/VpnTransportInfoTest.java @@ -42,7 +42,13 @@ public class VpnTransportInfoTest { VpnTransportInfo v1 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM); VpnTransportInfo v2 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE); VpnTransportInfo v3 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM); + VpnTransportInfo v4 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY); + VpnTransportInfo v5 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM); + assertNotEquals(v1, v2); + assertNotEquals(v3, v4); + assertNotEquals(v4, v5); + assertEquals(v1, v3); assertEquals(v1.hashCode(), v3.hashCode()); } diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt index 8ea226db938e..b62bdbcfb5eb 100644 --- a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt +++ b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt @@ -18,6 +18,7 @@ package android.net.util import android.content.Context import android.content.res.Resources +import android.net.ConnectivityResources import android.net.NetworkCapabilities import android.net.NetworkCapabilities.MAX_TRANSPORT import android.net.NetworkCapabilities.TRANSPORT_CELLULAR @@ -26,13 +27,15 @@ import android.net.NetworkCapabilities.TRANSPORT_VPN import android.net.NetworkCapabilities.TRANSPORT_WIFI import androidx.test.filters.SmallTest import com.android.internal.R +import org.junit.After import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.fail import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import org.mockito.ArgumentMatchers +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mockito.any import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock @@ -47,21 +50,33 @@ import org.mockito.Mockito.mock class KeepaliveUtilsTest { // Prepare mocked context with given resource strings. - private fun getMockedContextWithStringArrayRes(id: Int, res: Array<out String?>?): Context { + private fun getMockedContextWithStringArrayRes( + id: Int, + name: String, + res: Array<out String?>? + ): Context { val mockRes = mock(Resources::class.java) - doReturn(res).`when`(mockRes).getStringArray(ArgumentMatchers.eq(id)) + doReturn(res).`when`(mockRes).getStringArray(eq(id)) + doReturn(id).`when`(mockRes).getIdentifier(eq(name), any(), any()) return mock(Context::class.java).apply { doReturn(mockRes).`when`(this).getResources() + ConnectivityResources.setResourcesContextForTest(this) } } + @After + fun tearDown() { + ConnectivityResources.setResourcesContextForTest(null) + } + @Test fun testGetSupportedKeepalives() { fun assertRunWithException(res: Array<out String?>?) { try { val mockContext = getMockedContextWithStringArrayRes( - R.array.config_networkSupportedKeepaliveCount, res) + R.array.config_networkSupportedKeepaliveCount, + "config_networkSupportedKeepaliveCount", res) KeepaliveUtils.getSupportedKeepalives(mockContext) fail("Expected KeepaliveDeviceConfigurationException") } catch (expected: KeepaliveUtils.KeepaliveDeviceConfigurationException) { @@ -89,7 +104,8 @@ class KeepaliveUtilsTest { val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0) val mockContext = getMockedContextWithStringArrayRes( - R.array.config_networkSupportedKeepaliveCount, validRes) + R.array.config_networkSupportedKeepaliveCount, + "config_networkSupportedKeepaliveCount", validRes) val actual = KeepaliveUtils.getSupportedKeepalives(mockContext) assertArrayEquals(expectedValidRes, actual) } diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt index c1315f64c56b..25aa6266577e 100644 --- a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt +++ b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt @@ -21,18 +21,20 @@ 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.ConnectivityResources +import android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI +import android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE 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 import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.test.mock.MockContentResolver import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import com.android.internal.R +import com.android.connectivity.resources.R import com.android.internal.util.test.FakeSettingsProvider +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -41,6 +43,7 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.argThat +import org.mockito.ArgumentMatchers.eq import org.mockito.Mockito.any import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock @@ -57,6 +60,8 @@ import org.mockito.Mockito.verify @SmallTest class MultinetworkPolicyTrackerTest { private val resources = mock(Resources::class.java).also { + doReturn(R.integer.config_networkAvoidBadWifi).`when`(it).getIdentifier( + eq("config_networkAvoidBadWifi"), eq("integer"), any()) doReturn(0).`when`(it).getInteger(R.integer.config_networkAvoidBadWifi) } private val telephonyManager = mock(TelephonyManager::class.java) @@ -75,6 +80,7 @@ class MultinetworkPolicyTrackerTest { doReturn(resources).`when`(it).resources doReturn(it).`when`(it).createConfigurationContext(any()) Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "1") + ConnectivityResources.setResourcesContextForTest(it) } private val tracker = MultinetworkPolicyTracker(context, null /* handler */) @@ -85,6 +91,11 @@ class MultinetworkPolicyTrackerTest { assertEquals(preference, tracker.meteredMultipathPreference) } + @After + fun tearDown() { + ConnectivityResources.setResourcesContextForTest(null) + } + @Test fun testUpdateMeteredMultipathPreference() { assertMultipathPreference(MULTIPATH_PREFERENCE_HANDOVER) diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index a0b13c89ff49..d70572d1a7bf 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -87,10 +87,10 @@ import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; -import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED; -import static android.net.NetworkPolicyManager.RULE_NONE; -import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; -import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; +import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_DATA_SAVER; +import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.NetworkPolicyManager.BLOCKED_REASON_BATTERY_SAVER; +import static android.net.NetworkPolicyManager.BLOCKED_REASON_NONE; import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; @@ -179,6 +179,8 @@ import android.net.ConnectivityManager.NetworkCallback; import android.net.ConnectivityManager.PacketKeepalive; import android.net.ConnectivityManager.PacketKeepaliveCallback; import android.net.ConnectivityManager.TooManyRequestsException; +import android.net.ConnectivityResources; +import android.net.ConnectivitySettingsManager; import android.net.ConnectivityThread; import android.net.DataStallReportParcelable; import android.net.EthernetManager; @@ -187,7 +189,6 @@ import android.net.IDnsResolver; import android.net.INetd; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; -import android.net.INetworkPolicyListener; import android.net.IOnCompleteListener; import android.net.IQosCallback; import android.net.InetAddresses; @@ -206,6 +207,7 @@ import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkPolicyManager; +import android.net.NetworkPolicyManager.NetworkPolicyCallback; import android.net.NetworkRequest; import android.net.NetworkScore; import android.net.NetworkSpecifier; @@ -281,7 +283,6 @@ import com.android.internal.util.test.FakeSettingsProvider; import com.android.net.module.util.ArrayTrackRecord; import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo; import com.android.server.connectivity.ConnectivityConstants; -import com.android.server.connectivity.ConnectivityResources; import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.Nat464Xlat; import com.android.server.connectivity.NetworkAgentInfo; @@ -422,7 +423,7 @@ public class ConnectivityServiceTest { private TestNetworkAgentWrapper mEthernetNetworkAgent; private MockVpn mMockVpn; private Context mContext; - private INetworkPolicyListener mPolicyListener; + private NetworkPolicyCallback mPolicyCallback; private WrappedMultinetworkPolicyTracker mPolicyTracker; private HandlerThread mAlarmManagerThread; private TestNetIdManager mNetIdManager; @@ -434,8 +435,7 @@ public class ConnectivityServiceTest { private TestNetworkCallback mProfileDefaultNetworkCallback; // State variables required to emulate NetworkPolicyManagerService behaviour. - private int mUidRules = RULE_NONE; - private boolean mRestrictBackground = false; + private int mBlockedReasons = BLOCKED_REASON_NONE; @Mock DeviceIdleInternal mDeviceIdleInternal; @Mock INetworkManagementService mNetworkManagementService; @@ -1374,28 +1374,13 @@ public class ConnectivityServiceTest { } private void mockUidNetworkingBlocked() { - doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class) - .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules, - i.getArgument(1) /* metered */, mRestrictBackground) + doAnswer(i -> NetworkPolicyManager.isUidBlocked(mBlockedReasons, i.getArgument(1)) ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean()); - - doAnswer(inv -> mContext.getSystemService(NetworkPolicyManager.class) - .checkUidNetworkingBlocked(inv.getArgument(0) /* uid */, - inv.getArgument(1) /* uidRules */, - inv.getArgument(2) /* isNetworkMetered */, - inv.getArgument(3) /* isBackgroundRestricted */) - ).when(mNetworkPolicyManager).checkUidNetworkingBlocked( - anyInt(), anyInt(), anyBoolean(), anyBoolean()); - } - - private void setUidRulesChanged(int uidRules) throws RemoteException { - mUidRules = uidRules; - mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules); } - private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException { - mRestrictBackground = restrictBackground; - mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground); + private void setBlockedReasonChanged(int blockedReasons) { + mBlockedReasons = blockedReasons; + mPolicyCallback.onUidBlockedReasonChanged(Process.myUid(), blockedReasons); } private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) { @@ -1537,10 +1522,11 @@ public class ConnectivityServiceTest { mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS; verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any()); - final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor = - ArgumentCaptor.forClass(INetworkPolicyListener.class); - verify(mNetworkPolicyManager).registerListener(policyListenerCaptor.capture()); - mPolicyListener = policyListenerCaptor.getValue(); + final ArgumentCaptor<NetworkPolicyCallback> policyCallbackCaptor = + ArgumentCaptor.forClass(NetworkPolicyCallback.class); + verify(mNetworkPolicyManager).registerNetworkPolicyCallback(any(), + policyCallbackCaptor.capture()); + mPolicyCallback = policyCallbackCaptor.getValue(); // Create local CM before sending system ready so that we can answer // getSystemService() correctly. @@ -1553,7 +1539,7 @@ public class ConnectivityServiceTest { mQosCallbackTracker = mock(QosCallbackTracker.class); // Ensure that the default setting for Captive Portals is used for most tests - setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); + setCaptivePortalMode(ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_PROMPT); setAlwaysOnNetworks(false); setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); } @@ -1584,11 +1570,19 @@ public class ConnectivityServiceTest { com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl); doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray( com.android.connectivity.resources.R.array.config_wakeonlan_supported_interfaces); - final com.android.server.connectivity.ConnectivityResources connRes = mock( - ConnectivityResources.class); + doReturn(new String[] { "0,1", "1,3" }).when(mResources).getStringArray( + com.android.connectivity.resources.R.array.config_networkSupportedKeepaliveCount); + doReturn(com.android.connectivity.resources.R.array.config_networkSupportedKeepaliveCount) + .when(mResources).getIdentifier(eq("config_networkSupportedKeepaliveCount"), + eq("array"), any()); + final ConnectivityResources connRes = mock(ConnectivityResources.class); doReturn(mResources).when(connRes).get(); doReturn(connRes).when(deps).getResources(any()); + final Context mockResContext = mock(Context.class); + doReturn(mResources).when(mockResContext).getResources(); + ConnectivityResources.setResourcesContextForTest(mockResContext); + return deps; } @@ -1644,6 +1638,7 @@ public class ConnectivityServiceTest { waitForIdle(); FakeSettingsProvider.clearSettingsProvider(); + ConnectivityResources.setResourcesContextForTest(null); mCsHandlerThread.quitSafely(); mAlarmManagerThread.quitSafely(); @@ -3406,7 +3401,7 @@ public class ConnectivityServiceTest { .addCapability(NET_CAPABILITY_VALIDATED).build(); mCm.registerNetworkCallback(validatedRequest, validatedCallback); - setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_AVOID); + setCaptivePortalMode(ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_AVOID); // Bring up a network with a captive portal. // Expect it to fail to connect and not result in any callbacks. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); @@ -4056,20 +4051,21 @@ public class ConnectivityServiceTest { private void setCaptivePortalMode(int mode) { ContentResolver cr = mServiceContext.getContentResolver(); - Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode); + Settings.Global.putInt(cr, ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE, mode); } private void setAlwaysOnNetworks(boolean enable) { ContentResolver cr = mServiceContext.getContentResolver(); - Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0); + Settings.Global.putInt(cr, ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON, + enable ? 1 : 0); mService.updateAlwaysOnNetworks(); waitForIdle(); } private void setPrivateDnsSettings(String mode, String specifier) { final ContentResolver cr = mServiceContext.getContentResolver(); - Settings.Global.putString(cr, Settings.Global.PRIVATE_DNS_MODE, mode); - Settings.Global.putString(cr, Settings.Global.PRIVATE_DNS_SPECIFIER, specifier); + Settings.Global.putString(cr, ConnectivitySettingsManager.PRIVATE_DNS_MODE, mode); + Settings.Global.putString(cr, ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER, specifier); mService.updatePrivateDnsSettings(); waitForIdle(); } @@ -4307,7 +4303,7 @@ public class ConnectivityServiceTest { @Test public void testAvoidBadWifiSetting() throws Exception { final ContentResolver cr = mServiceContext.getContentResolver(); - final String settingName = Settings.Global.NETWORK_AVOID_BAD_WIFI; + final String settingName = ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI; mPolicyTracker.mConfigRestrictsAvoidBadWifi = false; String[] values = new String[] {null, "0", "1"}; @@ -4364,7 +4360,7 @@ public class ConnectivityServiceTest { TestNetworkCallback validatedWifiCallback = new TestNetworkCallback(); mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback); - Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0); + Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 0); mPolicyTracker.reevaluate(); // Bring up validated cell. @@ -4432,7 +4428,7 @@ public class ConnectivityServiceTest { validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); // Simulate the user selecting "switch" and checking the don't ask again checkbox. - Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1); + Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 1); mPolicyTracker.reevaluate(); // We now switch to cell. @@ -4445,11 +4441,11 @@ public class ConnectivityServiceTest { // Simulate the user turning the cellular fallback setting off and then on. // We switch to wifi and then to cell. - Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null); + Settings.Global.putString(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, null); mPolicyTracker.reevaluate(); defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertEquals(mCm.getActiveNetwork(), wifiNetwork); - Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1); + Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 1); mPolicyTracker.reevaluate(); defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); assertEquals(mCm.getActiveNetwork(), cellNetwork); @@ -4468,7 +4464,7 @@ public class ConnectivityServiceTest { @Test public void testMeteredMultipathPreferenceSetting() throws Exception { final ContentResolver cr = mServiceContext.getContentResolver(); - final String settingName = Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE; + final String settingName = ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE; for (int config : Arrays.asList(0, 3, 2)) { for (String setting: Arrays.asList(null, "0", "2", "1")) { @@ -7263,7 +7259,7 @@ public class ConnectivityServiceTest { assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertExtraInfoFromCmPresent(mCellNetworkAgent); - setUidRulesChanged(RULE_REJECT_ALL); + setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); @@ -7271,17 +7267,17 @@ public class ConnectivityServiceTest { assertExtraInfoFromCmBlocked(mCellNetworkAgent); // ConnectivityService should cache it not to invoke the callback again. - setUidRulesChanged(RULE_REJECT_METERED); + setBlockedReasonChanged(BLOCKED_METERED_REASON_USER_RESTRICTED); cellNetworkCallback.assertNoCallback(); - setUidRulesChanged(RULE_NONE); + setBlockedReasonChanged(BLOCKED_REASON_NONE); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertExtraInfoFromCmPresent(mCellNetworkAgent); - setUidRulesChanged(RULE_REJECT_METERED); + setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); @@ -7306,33 +7302,33 @@ public class ConnectivityServiceTest { assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); assertExtraInfoFromCmBlocked(mCellNetworkAgent); - setUidRulesChanged(RULE_ALLOW_METERED); + setBlockedReasonChanged(BLOCKED_REASON_NONE); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertExtraInfoFromCmPresent(mCellNetworkAgent); - setUidRulesChanged(RULE_NONE); + setBlockedReasonChanged(BLOCKED_REASON_NONE); cellNetworkCallback.assertNoCallback(); // Restrict background data. Networking is not blocked because the network is unmetered. - setRestrictBackgroundChanged(true); + setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); assertExtraInfoFromCmBlocked(mCellNetworkAgent); - setRestrictBackgroundChanged(true); + setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); cellNetworkCallback.assertNoCallback(); - setUidRulesChanged(RULE_ALLOW_METERED); + setBlockedReasonChanged(BLOCKED_REASON_NONE); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertExtraInfoFromCmPresent(mCellNetworkAgent); - setRestrictBackgroundChanged(false); + setBlockedReasonChanged(BLOCKED_REASON_NONE); cellNetworkCallback.assertNoCallback(); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); @@ -7349,9 +7345,9 @@ public class ConnectivityServiceTest { mockUidNetworkingBlocked(); // No Networkcallbacks invoked before any network is active. - setUidRulesChanged(RULE_REJECT_ALL); - setUidRulesChanged(RULE_NONE); - setUidRulesChanged(RULE_REJECT_METERED); + setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER); + setBlockedReasonChanged(BLOCKED_REASON_NONE); + setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); defaultCallback.assertNoCallback(); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); @@ -7376,8 +7372,8 @@ public class ConnectivityServiceTest { defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); // Verify there's no Networkcallbacks invoked after data saver on/off. - setRestrictBackgroundChanged(true); - setRestrictBackgroundChanged(false); + setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); + setBlockedReasonChanged(BLOCKED_REASON_NONE); defaultCallback.assertNoCallback(); mCellNetworkAgent.disconnect(); @@ -11400,7 +11396,6 @@ public class ConnectivityServiceTest { mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); mProfileDefaultNetworkCallback.assertNoCallback(); - waitForIdle(); inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); @@ -11419,7 +11414,6 @@ public class ConnectivityServiceTest { assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId, uidRangeFor(testHandle)); - waitForIdle(); inOrder.verify(mMockNetd).networkDestroy(workAgent.getNetwork().netId); mCellNetworkAgent.disconnect(); @@ -11427,6 +11421,8 @@ public class ConnectivityServiceTest { mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + // Waiting for the handler to be idle before checking for networkDestroy is necessary + // here because ConnectivityService calls onLost before the network is fully torn down. waitForIdle(); inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId); @@ -11456,7 +11452,6 @@ public class ConnectivityServiceTest { workAgent2.disconnect(); mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2); assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); - waitForIdle(); inOrder.verify(mMockNetd).networkDestroy(workAgent2.getNetwork().netId); assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java index b7ece8f4c4c9..692c50fbef86 100644 --- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java @@ -18,15 +18,15 @@ package com.android.server.connectivity; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER; import static android.net.NetworkCapabilities.MAX_TRANSPORT; import static android.net.NetworkCapabilities.MIN_TRANSPORT; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE; import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS; -import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE; -import static android.provider.Settings.Global.PRIVATE_DNS_MODE; -import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; import static com.android.testutils.MiscAsserts.assertContainsExactly; import static com.android.testutils.MiscAsserts.assertContainsStringsExactly; diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java index 5b17aadc50a6..8a0c923d5fb0 100644 --- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -109,16 +109,6 @@ public class VcnGatewayConnectionConfigTest { } @Test - public void testBuilderRequiresNonEmptyUnderlyingCaps() { - try { - newBuilder().addExposedCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build(); - - fail("Expected exception due to invalid required underlying capabilities"); - } catch (IllegalArgumentException e) { - } - } - - @Test public void testBuilderRequiresNonNullRetryInterval() { try { newBuilder().setRetryInterval(null); |