diff options
327 files changed, 7028 insertions, 3750 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp index 754c4e94e73a..8090bec24fdb 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -152,8 +152,8 @@ droidstubs { args: metalava_framework_docs_args, check_api: { current: { - api_file: "non-updatable-api/current.txt", - removed_api_file: "non-updatable-api/removed.txt", + api_file: "core/api/current.txt", + removed_api_file: "core/api/removed.txt", }, last_released: { api_file: ":android-non-updatable.api.public.latest", @@ -212,8 +212,8 @@ droidstubs { args: metalava_framework_docs_args + priv_apps, check_api: { current: { - api_file: "non-updatable-api/system-current.txt", - removed_api_file: "non-updatable-api/system-removed.txt", + api_file: "core/api/system-current.txt", + removed_api_file: "core/api/system-removed.txt", }, last_released: { api_file: ":android-non-updatable.api.system.latest", @@ -223,7 +223,7 @@ droidstubs { api_lint: { enabled: true, new_since: ":android-non-updatable.api.system.latest", - baseline_file: "non-updatable-api/system-lint-baseline.txt", + baseline_file: "core/api/system-lint-baseline.txt", }, }, } @@ -257,46 +257,19 @@ droidstubs { } ///////////////////////////////////////////////////////////////////// -// Following droidstubs modules are for extra APIs for modules, +// Following droidstub module for extra APIs for modules, // namely @SystemApi(client=MODULE_LIBRARIES) APIs. ///////////////////////////////////////////////////////////////////// droidstubs { - name: "module-lib-api", - defaults: ["metalava-full-api-stubs-default"], - arg_files: ["core/res/AndroidManifest.xml"], - args: metalava_framework_docs_args + module_libs, - - // Do not generate stubs as they are not needed - generate_stubs: false, - - check_api: { - current: { - api_file: "api/module-lib-current.txt", - removed_api_file: "api/module-lib-removed.txt", - }, - last_released: { - api_file: ":android.api.module-lib.latest", - removed_api_file: ":removed.api.module-lib.latest", - baseline_file: ":module-lib-api-incompatibilities-with-last-released" - }, - api_lint: { - enabled: true, - new_since: ":android.api.module-lib.latest", - baseline_file: "api/module-lib-lint-baseline.txt", - }, - }, -} - -droidstubs { name: "module-lib-api-stubs-docs-non-updatable", defaults: ["metalava-non-updatable-api-stubs-default"], arg_files: ["core/res/AndroidManifest.xml"], args: metalava_framework_docs_args + priv_apps + module_libs, check_api: { current: { - api_file: "non-updatable-api/module-lib-current.txt", - removed_api_file: "non-updatable-api/module-lib-removed.txt", + api_file: "core/api/module-lib-current.txt", + removed_api_file: "core/api/module-lib-removed.txt", }, last_released: { api_file: ":android-non-updatable.api.module-lib.latest", diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java index 5f2fabe52929..beb9ad3d27ea 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java @@ -27,8 +27,6 @@ import android.app.appsearch.AppSearchSchema.PropertyConfig; * * <p>This class is a higher level implement of {@link GenericDocument}. * - * <p>This class will eventually migrate to Jetpack, where it will become public API. - * * @hide */ @@ -99,10 +97,9 @@ public class AppSearchEmail extends GenericDocument { } /** - * Get the from address of {@link AppSearchEmail}. + * Gets the from address of {@link AppSearchEmail}. * - * @return Returns the subject of {@link AppSearchEmail} or {@code null} if it's not been set - * yet. + * @return The subject of {@link AppSearchEmail} or {@code null} if it's not been set yet. */ @Nullable public String getFrom() { @@ -110,10 +107,10 @@ public class AppSearchEmail extends GenericDocument { } /** - * Get the destination addresses of {@link AppSearchEmail}. + * Gets the destination addresses of {@link AppSearchEmail}. * - * @return Returns the destination addresses of {@link AppSearchEmail} or {@code null} if it's - * not been set yet. + * @return The destination addresses of {@link AppSearchEmail} or {@code null} if it's not + * been set yet. */ @Nullable public String[] getTo() { @@ -121,10 +118,9 @@ public class AppSearchEmail extends GenericDocument { } /** - * Get the CC list of {@link AppSearchEmail}. + * Gets the CC list of {@link AppSearchEmail}. * - * @return Returns the CC list of {@link AppSearchEmail} or {@code null} if it's not been set - * yet. + * @return The CC list of {@link AppSearchEmail} or {@code null} if it's not been set yet. */ @Nullable public String[] getCc() { @@ -132,10 +128,9 @@ public class AppSearchEmail extends GenericDocument { } /** - * Get the BCC list of {@link AppSearchEmail}. + * Gets the BCC list of {@link AppSearchEmail}. * - * @return Returns the BCC list of {@link AppSearchEmail} or {@code null} if it's not been set - * yet. + * @return The BCC list of {@link AppSearchEmail} or {@code null} if it's not been set yet. */ @Nullable public String[] getBcc() { @@ -143,10 +138,9 @@ public class AppSearchEmail extends GenericDocument { } /** - * Get the subject of {@link AppSearchEmail}. + * Gets the subject of {@link AppSearchEmail}. * - * @return Returns the value subject of {@link AppSearchEmail} or {@code null} if it's not been - * set yet. + * @return The value subject of {@link AppSearchEmail} or {@code null} if it's not been set yet. */ @Nullable public String getSubject() { @@ -154,9 +148,9 @@ public class AppSearchEmail extends GenericDocument { } /** - * Get the body of {@link AppSearchEmail}. + * Gets the body of {@link AppSearchEmail}. * - * @return Returns the body of {@link AppSearchEmail} or {@code null} if it's not been set yet. + * @return The body of {@link AppSearchEmail} or {@code null} if it's not been set yet. */ @Nullable public String getBody() { @@ -169,7 +163,8 @@ public class AppSearchEmail extends GenericDocument { public static class Builder extends GenericDocument.Builder<AppSearchEmail.Builder> { /** - * Create a new {@link AppSearchEmail.Builder} + * Creates a new {@link AppSearchEmail.Builder} + * * @param uri The Uri of the Email. */ public Builder(@NonNull String uri) { @@ -177,56 +172,56 @@ public class AppSearchEmail extends GenericDocument { } /** - * Set the from address of {@link AppSearchEmail} + * Sets the from address of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setFrom(@NonNull String from) { - setProperty(KEY_FROM, from); + setPropertyString(KEY_FROM, from); return this; } /** - * Set the destination address of {@link AppSearchEmail} + * Sets the destination address of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setTo(@NonNull String... to) { - setProperty(KEY_TO, to); + setPropertyString(KEY_TO, to); return this; } /** - * Set the CC list of {@link AppSearchEmail} + * Sets the CC list of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setCc(@NonNull String... cc) { - setProperty(KEY_CC, cc); + setPropertyString(KEY_CC, cc); return this; } /** - * Set the BCC list of {@link AppSearchEmail} + * Sets the BCC list of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setBcc(@NonNull String... bcc) { - setProperty(KEY_BCC, bcc); + setPropertyString(KEY_BCC, bcc); return this; } /** - * Set the subject of {@link AppSearchEmail} + * Sets the subject of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setSubject(@NonNull String subject) { - setProperty(KEY_SUBJECT, subject); + setPropertyString(KEY_SUBJECT, subject); return this; } /** - * Set the body of {@link AppSearchEmail} + * Sets the body of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setBody(@NonNull String body) { - setProperty(KEY_BODY, body); + setPropertyString(KEY_BODY, body); return this; } diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java index 90e4df68f734..3933726d6729 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java @@ -16,10 +16,12 @@ package android.app.appsearch; +import android.annotation.SuppressLint; import android.os.Bundle; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.appsearch.exceptions.IllegalSchemaException; import android.util.ArraySet; @@ -28,6 +30,8 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Set; /** @@ -39,13 +43,8 @@ import java.util.Set; * @hide */ public final class AppSearchSchema { - /** @hide */ - - public static final String SCHEMA_TYPE_FIELD = "schemaType"; - - /** @hide */ - - public static final String PROPERTIES_FIELD = "properties"; + private static final String SCHEMA_TYPE_FIELD = "schemaType"; + private static final String PROPERTIES_FIELD = "properties"; private final Bundle mBundle; @@ -71,10 +70,35 @@ public final class AppSearchSchema { return mBundle.toString(); } + /** Returns the name of this schema type, e.g. Email. */ + @NonNull + public String getSchemaTypeName() { + return mBundle.getString(SCHEMA_TYPE_FIELD, ""); + } + + /** + * Returns the list of {@link PropertyConfig}s that are part of this schema. + * + * <p>This method creates a new list when called. + */ + @NonNull + public List<PropertyConfig> getProperties() { + ArrayList<Bundle> propertyBundles = + mBundle.getParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD); + if (propertyBundles.isEmpty()) { + return Collections.emptyList(); + } + List<PropertyConfig> ret = new ArrayList<>(propertyBundles.size()); + for (int i = 0; i < propertyBundles.size(); i++) { + ret.add(new PropertyConfig(propertyBundles.get(i))); + } + return ret; + } + /** Builder for {@link AppSearchSchema objects}. */ public static final class Builder { private final String mTypeName; - private final ArrayList<Bundle> mProperties = new ArrayList<>(); + private final ArrayList<Bundle> mPropertyBundles = new ArrayList<>(); private final Set<String> mPropertyNames = new ArraySet<>(); private boolean mBuilt = false; @@ -85,15 +109,19 @@ public final class AppSearchSchema { } /** Adds a property to the given type. */ + // TODO(b/171360120): MissingGetterMatchingBuilder expects a method called getPropertys, but + // we provide the (correct) method getProperties. Once the bug referenced in this TODO is + // fixed, remove this SuppressLint. + @SuppressLint("MissingGetterMatchingBuilder") @NonNull public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(propertyConfig); - if (!mPropertyNames.add(propertyConfig.mName)) { - throw new IllegalSchemaException( - "Property defined more than once: " + propertyConfig.mName); + String name = propertyConfig.getName(); + if (!mPropertyNames.add(name)) { + throw new IllegalSchemaException("Property defined more than once: " + name); } - mProperties.add(propertyConfig.mBundle); + mPropertyBundles.add(propertyConfig.mBundle); return this; } @@ -107,7 +135,7 @@ public final class AppSearchSchema { Preconditions.checkState(!mBuilt, "Builder has already been used"); Bundle bundle = new Bundle(); bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mTypeName); - bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mProperties); + bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mPropertyBundles); mBuilt = true; return new AppSearchSchema(bundle); } @@ -120,29 +148,12 @@ public final class AppSearchSchema { * a property. */ public static final class PropertyConfig { - /** @hide */ - - public static final String NAME_FIELD = "name"; - - /** @hide */ - - public static final String DATA_TYPE_FIELD = "dataType"; - - /** @hide */ - - public static final String SCHEMA_TYPE_FIELD = "schemaType"; - - /** @hide */ - - public static final String CARDINALITY_FIELD = "cardinality"; - - /** @hide */ - - public static final String INDEXING_TYPE_FIELD = "indexingType"; - - /** @hide */ - - public static final String TOKENIZER_TYPE_FIELD = "tokenizerType"; + private static final String NAME_FIELD = "name"; + private static final String DATA_TYPE_FIELD = "dataType"; + private static final String SCHEMA_TYPE_FIELD = "schemaType"; + private static final String CARDINALITY_FIELD = "cardinality"; + private static final String INDEXING_TYPE_FIELD = "indexingType"; + private static final String TOKENIZER_TYPE_FIELD = "tokenizerType"; /** * Physical data-types of the contents of the property. @@ -259,11 +270,9 @@ public final class AppSearchSchema { /** Tokenization for plain text. */ public static final int TOKENIZER_TYPE_PLAIN = 1; - final String mName; final Bundle mBundle; - PropertyConfig(@NonNull String name, @NonNull Bundle bundle) { - mName = Preconditions.checkNotNull(name); + PropertyConfig(@NonNull Bundle bundle) { mBundle = Preconditions.checkNotNull(bundle); } @@ -272,6 +281,45 @@ public final class AppSearchSchema { return mBundle.toString(); } + /** Returns the name of this property. */ + @NonNull + public String getName() { + return mBundle.getString(NAME_FIELD, ""); + } + + /** Returns the type of data the property contains (e.g. string, int, bytes, etc). */ + public @DataType int getDataType() { + return mBundle.getInt(DATA_TYPE_FIELD, -1); + } + + /** + * Returns the logical schema-type of the contents of this property. + * + * <p>Only set when {@link #getDataType} is set to {@link #DATA_TYPE_DOCUMENT}. + * Otherwise, it is {@code null}. + */ + @Nullable + public String getSchemaType() { + return mBundle.getString(SCHEMA_TYPE_FIELD); + } + + /** + * Returns the cardinality of the property (whether it is optional, required or repeated). + */ + public @Cardinality int getCardinality() { + return mBundle.getInt(CARDINALITY_FIELD, -1); + } + + /** Returns how the property is indexed. */ + public @IndexingType int getIndexingType() { + return mBundle.getInt(INDEXING_TYPE_FIELD); + } + + /** Returns how this property is tokenized (split into words). */ + public @TokenizerType int getTokenizerType() { + return mBundle.getInt(TOKENIZER_TYPE_FIELD); + } + /** * Builder for {@link PropertyConfig}. * @@ -286,13 +334,11 @@ public final class AppSearchSchema { * is also required. */ public static final class Builder { - private final String mName; private final Bundle mBundle = new Bundle(); private boolean mBuilt = false; /** Creates a new {@link PropertyConfig.Builder}. */ public Builder(@NonNull String propertyName) { - mName = Preconditions.checkNotNull(propertyName); mBundle.putString(NAME_FIELD, propertyName); } @@ -386,7 +432,7 @@ public final class AppSearchSchema { throw new IllegalSchemaException("Missing field: cardinality"); } mBuilt = true; - return new PropertyConfig(mName, mBundle); + return new PropertyConfig(mBundle); } } } diff --git a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java index 9fe2c67d00f2..48d3ac09d997 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java @@ -30,6 +30,7 @@ import com.android.internal.util.Preconditions; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Set; /** @@ -65,20 +66,14 @@ public class GenericDocument { /** The default time-to-live in millisecond of a document, which is infinity. */ private static final long DEFAULT_TTL_MILLIS = 0L; - /** @hide */ - - public static final String PROPERTIES_FIELD = "properties"; - - /** @hide */ - - public static final String BYTE_ARRAY_FIELD = "byteArray"; - - static final String SCHEMA_TYPE_FIELD = "schemaType"; - static final String URI_FIELD = "uri"; - static final String SCORE_FIELD = "score"; - static final String TTL_MILLIS_FIELD = "ttlMillis"; - static final String CREATION_TIMESTAMP_MILLIS_FIELD = "creationTimestampMillis"; - static final String NAMESPACE_FIELD = "namespace"; + private static final String PROPERTIES_FIELD = "properties"; + private static final String BYTE_ARRAY_FIELD = "byteArray"; + private static final String SCHEMA_TYPE_FIELD = "schemaType"; + private static final String URI_FIELD = "uri"; + private static final String SCORE_FIELD = "score"; + private static final String TTL_MILLIS_FIELD = "ttlMillis"; + private static final String CREATION_TIMESTAMP_MILLIS_FIELD = "creationTimestampMillis"; + private static final String NAMESPACE_FIELD = "namespace"; /** * The maximum number of indexed properties a document can have. @@ -190,6 +185,12 @@ public class GenericDocument { return mBundle.getInt(SCORE_FIELD, DEFAULT_SCORE); } + /** Returns the names of all properties defined in this document. */ + @NonNull + public Set<String> getPropertyNames() { + return Collections.unmodifiableSet(mProperties.keySet()); + } + /** * Retrieves a {@link String} value by key. * @@ -437,8 +438,6 @@ public class GenericDocument { @Override public boolean equals(@Nullable Object other) { - // Check only proto's equality is sufficient here since all properties in - // mProperties are ordered by keys and stored in proto. if (this == other) { return true; } @@ -450,8 +449,8 @@ public class GenericDocument { } /** - * Deeply checks two bundle is equally or not. - * <p> Two bundle will be considered equally if they contains same content. + * Deeply checks two bundles are equally or not. + * <p> Two bundles will be considered equally if they contain same content. */ @SuppressWarnings("unchecked") private static boolean bundleEquals(Bundle one, Bundle two) { @@ -472,7 +471,7 @@ public class GenericDocument { return false; } else if (valueOne == null && (valueTwo != null || !two.containsKey(key))) { // If we call bundle.get(key) when the 'key' doesn't actually exist in the - // bundle, we'll get back a null. So make sure that both values are null and + // bundle, we'll get back a null. So make sure that both values are null and // both keys exist in the bundle. return false; } else if (valueOne instanceof boolean[]) { @@ -538,8 +537,8 @@ public class GenericDocument { /** * Calculates the hash code for a bundle. - * <p> The hash code is only effected by the content in the bundle. Bundles will get - * consistent hash code if they have same content. + * <p> The hash code is only effected by the contents in the bundle. Bundles will get + * consistent hash code if they have same contents. */ @SuppressWarnings("unchecked") private static int bundleHashCode(Bundle bundle) { @@ -648,8 +647,11 @@ public class GenericDocument { /** * The builder class for {@link GenericDocument}. * - * @param <BuilderType> Type of subclass who extend this. + * @param <BuilderType> Type of subclass who extends this. */ + // This builder is specifically designed to be extended by classes deriving from + // GenericDocument. + @SuppressLint("StaticFinalBuilder") public static class Builder<BuilderType extends Builder> { private final Bundle mProperties = new Bundle(); @@ -662,12 +664,11 @@ public class GenericDocument { * * @param uri The uri of {@link GenericDocument}. * @param schemaType The schema type of the {@link GenericDocument}. The passed-in - * {@code schemaType} must be defined using {@code AppSearchManager#setSchema} prior + * {@code schemaType} must be defined using {@link AppSearchSession#setSchema} prior * to inserting a document of this {@code schemaType} into the AppSearch index using - * {@code AppSearchManager#putDocuments}. Otherwise, the document will be - * rejected by {@code AppSearchManager#putDocuments}. + * {@link AppSearchSession#putDocuments}. Otherwise, the document will be + * rejected by {@link AppSearchSession#putDocuments}. */ - //TODO(b/157082794) Linkify AppSearchManager once that API is public. @SuppressWarnings("unchecked") public Builder(@NonNull String uri, @NonNull String schemaType) { Preconditions.checkNotNull(uri); @@ -685,9 +686,12 @@ public class GenericDocument { } /** - * Set the app-defined namespace this Document resides in. No special values are - * reserved or understood by the infrastructure. URIs are unique within a namespace. The - * number of namespaces per app should be kept small for efficiency reasons. + * Sets the app-defined namespace this Document resides in. No special values are + * reserved or understood by the infrastructure. + * + * <p>URIs are unique within a namespace. + * + * <p>The number of namespaces per app should be kept small for efficiency reasons. */ @NonNull public BuilderType setNamespace(@NonNull String namespace) { @@ -714,7 +718,7 @@ public class GenericDocument { } /** - * Set the creation timestamp in milliseconds of the {@link GenericDocument}. Should be + * Sets the creation timestamp of the {@link GenericDocument}, in milliseconds. Should be * set using a value obtained from the {@link System#currentTimeMillis()} time base. */ @NonNull @@ -726,7 +730,7 @@ public class GenericDocument { } /** - * Set the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds. + * Sets the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds. * * <p>After this many milliseconds since the {@link #setCreationTimestampMillis creation * timestamp}, the document is deleted. @@ -752,7 +756,7 @@ public class GenericDocument { * @param values The {@code String} values of the property. */ @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull String... values) { + public BuilderType setPropertyString(@NonNull String key, @NonNull String... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(key); Preconditions.checkNotNull(values); @@ -768,7 +772,7 @@ public class GenericDocument { * @param values The {@code boolean} values of the property. */ @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull boolean... values) { + public BuilderType setPropertyBoolean(@NonNull String key, @NonNull boolean... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(key); Preconditions.checkNotNull(values); @@ -784,7 +788,7 @@ public class GenericDocument { * @param values The {@code long} values of the property. */ @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull long... values) { + public BuilderType setPropertyLong(@NonNull String key, @NonNull long... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(key); Preconditions.checkNotNull(values); @@ -800,7 +804,7 @@ public class GenericDocument { * @param values The {@code double} values of the property. */ @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull double... values) { + public BuilderType setPropertyDouble(@NonNull String key, @NonNull double... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(key); Preconditions.checkNotNull(values); @@ -815,7 +819,7 @@ public class GenericDocument { * @param values The {@code byte[]} of the property. */ @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull byte[]... values) { + public BuilderType setPropertyBytes(@NonNull String key, @NonNull byte[]... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(key); Preconditions.checkNotNull(values); @@ -831,7 +835,8 @@ public class GenericDocument { * @param values The {@link GenericDocument} values of the property. */ @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull GenericDocument... values) { + public BuilderType setPropertyDocument( + @NonNull String key, @NonNull GenericDocument... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(key); Preconditions.checkNotNull(values); diff --git a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java index 3c0e746f92ab..e1e0eda7558c 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java @@ -17,12 +17,12 @@ package android.app.appsearch; import android.annotation.NonNull; - import android.util.ArraySet; import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Set; /** @@ -40,18 +40,16 @@ public final class GetByUriRequest { mUris = uris; } - /** @hide */ - + /** Returns the namespace to get documents from. */ @NonNull public String getNamespace() { return mNamespace; } - /** @hide */ - + /** Returns the URIs to get from the namespace. */ @NonNull public Set<String> getUris() { - return mUris; + return Collections.unmodifiableSet(mUris); } /** Builder for {@link GetByUriRequest} objects. */ @@ -75,14 +73,14 @@ public final class GetByUriRequest { /** Adds one or more URIs to the request. */ @NonNull - public Builder addUris(@NonNull String... uris) { + public Builder addUri(@NonNull String... uris) { Preconditions.checkNotNull(uris); - return addUris(Arrays.asList(uris)); + return addUri(Arrays.asList(uris)); } /** Adds one or more URIs to the request. */ @NonNull - public Builder addUris(@NonNull Collection<String> uris) { + public Builder addUri(@NonNull Collection<String> uris) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(uris); mUris.addAll(uris); diff --git a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java index 7e97542c6f02..1f90bc184f6f 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java @@ -16,13 +16,16 @@ package android.app.appsearch; -import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.NonNull; +import android.app.appsearch.exceptions.AppSearchException; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; /** @@ -38,11 +41,10 @@ public final class PutDocumentsRequest { mDocuments = documents; } - /** @hide */ - + /** Returns the documents that are part of this request. */ @NonNull public List<GenericDocument> getDocuments() { - return mDocuments; + return Collections.unmodifiableList(mDocuments); } /** Builder for {@link PutDocumentsRequest} objects. */ @@ -51,6 +53,7 @@ public final class PutDocumentsRequest { private boolean mBuilt = false; /** Adds one or more documents to the request. */ + @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments() @NonNull public Builder addGenericDocument(@NonNull GenericDocument... documents) { Preconditions.checkNotNull(documents); @@ -58,6 +61,7 @@ public final class PutDocumentsRequest { } /** Adds one or more documents to the request. */ + @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments() @NonNull public Builder addGenericDocument(@NonNull Collection<GenericDocument> documents) { Preconditions.checkState(!mBuilt, "Builder has already been used"); diff --git a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java index a047041a3082..486857fba1de 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java @@ -17,12 +17,12 @@ package android.app.appsearch; import android.annotation.NonNull; - import android.util.ArraySet; import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Set; /** @@ -40,18 +40,16 @@ public final class RemoveByUriRequest { mUris = uris; } - /** @hide */ - + /** Returns the namespace to remove documents from. */ @NonNull public String getNamespace() { return mNamespace; } - /** @hide */ - + /** Returns the URIs to remove from the namespace. */ @NonNull public Set<String> getUris() { - return mUris; + return Collections.unmodifiableSet(mUris); } /** Builder for {@link RemoveByUriRequest} objects. */ @@ -75,14 +73,14 @@ public final class RemoveByUriRequest { /** Adds one or more URIs to the request. */ @NonNull - public Builder addUris(@NonNull String... uris) { + public Builder addUri(@NonNull String... uris) { Preconditions.checkNotNull(uris); - return addUris(Arrays.asList(uris)); + return addUri(Arrays.asList(uris)); } /** Adds one or more URIs to the request. */ @NonNull - public Builder addUris(@NonNull Collection<String> uris) { + public Builder addUri(@NonNull Collection<String> uris) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkNotNull(uris); mUris.addAll(uris); diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java index 758280bbc322..99cb2f16ca4d 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java @@ -49,19 +49,22 @@ public final class SearchResult { @NonNull private final Bundle mDocumentBundle; + /** Cache of the inflated document. Comes from inflating mDocumentBundle at first use. */ @Nullable private GenericDocument mDocument; - @Nullable - private final List<Bundle> mMatchBundles; - /** - * Contains a list of Snippets that matched the request. Only populated when requested in - * both {@link SearchSpec.Builder#setSnippetCount(int)} - * and {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}. + * Contains a list of MatchInfo bundles that matched the request. + * + * Only populated when requested in both {@link SearchSpec.Builder#setSnippetCount} and + * {@link SearchSpec.Builder#setSnippetCountPerProperty}. * * @see #getMatches() */ + @NonNull + private final List<Bundle> mMatchBundles; + + /** Cache of the inflated matches. Comes from inflating mMatchBundles at first use. */ @Nullable private List<MatchInfo> mMatches; @@ -70,7 +73,7 @@ public final class SearchResult { public SearchResult(@NonNull Bundle bundle) { mBundle = Preconditions.checkNotNull(bundle); mDocumentBundle = Preconditions.checkNotNull(bundle.getBundle(DOCUMENT_FIELD)); - mMatchBundles = bundle.getParcelableArrayList(MATCHES_FIELD); + mMatchBundles = Preconditions.checkNotNull(bundle.getParcelableArrayList(MATCHES_FIELD)); } /** @hide */ @@ -93,19 +96,16 @@ public final class SearchResult { } /** - * Contains a list of Snippets that matched the request. Only populated when requested in - * both {@link SearchSpec.Builder#setSnippetCount(int)} - * and {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}. + * Contains a list of Snippets that matched the request. * - * @return List of matches based on {@link SearchSpec}, if snippeting is disabled and this - * method is called it will return {@code null}. Users can also restrict snippet population - * using {@link SearchSpec.Builder#setSnippetCount} and - * {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}, for all results after that - * value this method will return {@code null}. + * @return List of matches based on {@link SearchSpec}. If snippeting is disabled using + * {@link SearchSpec.Builder#setSnippetCount} or + * {@link SearchSpec.Builder#setSnippetCountPerProperty}, for all results after that + * value, this method returns an empty list. */ - @Nullable + @NonNull public List<MatchInfo> getMatches() { - if (mMatchBundles != null && mMatches == null) { + if (mMatches == null) { mMatches = new ArrayList<>(mMatchBundles.size()); for (int i = 0; i < mMatchBundles.size(); i++) { MatchInfo matchInfo = new MatchInfo(getDocument(), mMatchBundles.get(i)); @@ -119,8 +119,8 @@ public final class SearchResult { * Snippet: It refers to a substring of text from the content of document that is returned as a * part of search result. * This class represents a match objects for any Snippets that might be present in - * {@link SearchResults} from query. Using this class user can get the full text, exact matches - * and Snippets of document content for a given match. + * {@link SearchResults} from query. Using this class + * user can get the full text, exact matches and Snippets of document content for a given match. * * <p>Class Example 1: * A document contains following text in property subject: diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java index c8719059fa8c..15acf103f2e6 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java @@ -16,16 +16,23 @@ package android.app.appsearch; +import android.annotation.SuppressLint; import android.os.Bundle; import android.annotation.IntDef; import android.annotation.NonNull; +import android.app.appsearch.exceptions.AppSearchException; import android.app.appsearch.exceptions.IllegalSearchSpecException; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; /** * This class represents the specification logic for AppSearch. It can be used to set the type of @@ -34,69 +41,26 @@ import java.lang.annotation.RetentionPolicy; */ // TODO(sidchhabra) : AddResultSpec fields for Snippets etc. public final class SearchSpec { - /** @hide */ - - public static final String TERM_MATCH_TYPE_FIELD = "termMatchType"; - - /** @hide */ - - public static final String SCHEMA_TYPES_FIELD = "schemaType"; - - /** @hide */ - - public static final String NAMESPACE_FIELD = "namespace"; - - /** @hide */ - - public static final String NUM_PER_PAGE_FIELD = "numPerPage"; - - /** @hide */ - - public static final String RANKING_STRATEGY_FIELD = "rankingStrategy"; - - /** @hide */ - - public static final String ORDER_FIELD = "order"; - - /** @hide */ - - public static final String SNIPPET_COUNT_FIELD = "snippetCount"; - - /** @hide */ - - public static final String SNIPPET_COUNT_PER_PROPERTY_FIELD = "snippetCountPerProperty"; - - /** @hide */ - - public static final String MAX_SNIPPET_FIELD = "maxSnippet"; + static final String TERM_MATCH_TYPE_FIELD = "termMatchType"; + static final String SCHEMA_TYPE_FIELD = "schemaType"; + static final String NAMESPACE_FIELD = "namespace"; + static final String NUM_PER_PAGE_FIELD = "numPerPage"; + static final String RANKING_STRATEGY_FIELD = "rankingStrategy"; + static final String ORDER_FIELD = "order"; + static final String SNIPPET_COUNT_FIELD = "snippetCount"; + static final String SNIPPET_COUNT_PER_PROPERTY_FIELD = "snippetCountPerProperty"; + static final String MAX_SNIPPET_FIELD = "maxSnippet"; /** @hide */ public static final int DEFAULT_NUM_PER_PAGE = 10; + // TODO(b/170371356): In framework, we may want these limits might be flag controlled. private static final int MAX_NUM_PER_PAGE = 10_000; private static final int MAX_SNIPPET_COUNT = 10_000; private static final int MAX_SNIPPET_PER_PROPERTY_COUNT = 10_000; private static final int MAX_SNIPPET_SIZE_LIMIT = 10_000; - private final Bundle mBundle; - - /** @hide */ - - public SearchSpec(@NonNull Bundle bundle) { - Preconditions.checkNotNull(bundle); - mBundle = bundle; - } - - /** - * Returns the {@link Bundle} populated by this builder. - * @hide - */ - @NonNull - public Bundle getBundle() { - return mBundle; - } - /** * Term Match Type for the query. * @hide @@ -108,7 +72,7 @@ public final class SearchSpec { TERM_MATCH_PREFIX }) @Retention(RetentionPolicy.SOURCE) - public @interface TermMatchCode {} + public @interface TermMatch {} /** * Query terms will only match exact tokens in the index. @@ -126,14 +90,14 @@ public final class SearchSpec { * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in - // {@link ScoringSpecProto.RankingStrategy.Code } + // {@link ScoringSpecProto.RankingStrategy.Code} @IntDef(value = { RANKING_STRATEGY_NONE, RANKING_STRATEGY_DOCUMENT_SCORE, RANKING_STRATEGY_CREATION_TIMESTAMP }) @Retention(RetentionPolicy.SOURCE) - public @interface RankingStrategyCode {} + public @interface RankingStrategy {} /** No Ranking, results are returned in arbitrary order.*/ public static final int RANKING_STRATEGY_NONE = 0; @@ -147,23 +111,109 @@ public final class SearchSpec { * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in - // {@link ScoringSpecProto.Order.Code } + // {@link ScoringSpecProto.Order.Code} @IntDef(value = { ORDER_DESCENDING, ORDER_ASCENDING }) @Retention(RetentionPolicy.SOURCE) - public @interface OrderCode {} + public @interface Order {} /** Search results will be returned in a descending order. */ public static final int ORDER_DESCENDING = 0; /** Search results will be returned in an ascending order. */ public static final int ORDER_ASCENDING = 1; + private final Bundle mBundle; + + /** @hide */ + + public SearchSpec(@NonNull Bundle bundle) { + Preconditions.checkNotNull(bundle); + mBundle = bundle; + } + + /** + * Returns the {@link Bundle} populated by this builder. + * @hide + */ + + @NonNull + public Bundle getBundle() { + return mBundle; + } + + /** Returns how the query terms should match terms in the index. */ + public @TermMatch int getTermMatch() { + return mBundle.getInt(TERM_MATCH_TYPE_FIELD, -1); + } + + /** + * Returns the list of schema types to search for. + * + * <p>If empty, the query will search over all schema types. + */ + @NonNull + public List<String> getSchemas() { + List<String> schemas = mBundle.getStringArrayList(SCHEMA_TYPE_FIELD); + if (schemas == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(schemas); + } + + /** + * Returns the list of namespaces to search for. + * + * <p>If empty, the query will search over all namespaces. + */ + @NonNull + public List<String> getNamespaces() { + List<String> namespaces = mBundle.getStringArrayList(NAMESPACE_FIELD); + if (namespaces == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(namespaces); + } + + /** Returns the number of results per page in the returned object. */ + public int getNumPerPage() { + return mBundle.getInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE); + } + + /** Returns the ranking strategy. */ + public @RankingStrategy int getRankingStrategy() { + return mBundle.getInt(RANKING_STRATEGY_FIELD); + } + + /** Returns the order of returned search results (descending or ascending). */ + public @Order int getOrder() { + return mBundle.getInt(ORDER_FIELD); + } + + /** Returns how many documents to generate snippets for. */ + public int getSnippetCount() { + return mBundle.getInt(SNIPPET_COUNT_FIELD); + } + + /** + * Returns how many matches for each property of a matching document to generate snippets for. + */ + public int getSnippetCountPerProperty() { + return mBundle.getInt(SNIPPET_COUNT_PER_PROPERTY_FIELD); + } + + /** Returns the maximum size of a snippet in characters. */ + public int getMaxSnippetSize() { + return mBundle.getInt(MAX_SNIPPET_FIELD); + } + /** Builder for {@link SearchSpec objects}. */ public static final class Builder { private final Bundle mBundle; + private final ArrayList<String> mSchemaTypes = new ArrayList<>(); + private final ArrayList<String> mNamespaces = new ArrayList<>(); private boolean mBuilt = false; /** Creates a new {@link SearchSpec.Builder}. */ @@ -176,7 +226,7 @@ public final class SearchSpec { * Indicates how the query terms should match {@code TermMatchCode} in the index. */ @NonNull - public Builder setTermMatch(@TermMatchCode int termMatchTypeCode) { + public Builder setTermMatch(@TermMatch int termMatchTypeCode) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange(termMatchTypeCode, TERM_MATCH_EXACT_ONLY, TERM_MATCH_PREFIX, "Term match type"); @@ -187,13 +237,27 @@ public final class SearchSpec { /** * Adds a Schema type filter to {@link SearchSpec} Entry. Only search for documents that * have the specified schema types. + * * <p>If unset, the query will search over all schema types. */ @NonNull - public Builder setSchemaTypes(@NonNull String... schemaTypes) { + public Builder addSchema(@NonNull String... schemaTypes) { Preconditions.checkNotNull(schemaTypes); Preconditions.checkState(!mBuilt, "Builder has already been used"); - mBundle.putStringArray(SCHEMA_TYPES_FIELD, schemaTypes); + return addSchema(Arrays.asList(schemaTypes)); + } + + /** + * Adds a Schema type filter to {@link SearchSpec} Entry. Only search for documents that + * have the specified schema types. + * + * <p>If unset, the query will search over all schema types. + */ + @NonNull + public Builder addSchema(@NonNull Collection<String> schemaTypes) { + Preconditions.checkNotNull(schemaTypes); + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mSchemaTypes.addAll(schemaTypes); return this; } @@ -203,10 +267,22 @@ public final class SearchSpec { * <p>If unset, the query will search over all namespaces. */ @NonNull - public Builder setNamespaces(@NonNull String... namespaces) { + public Builder addNamespace(@NonNull String... namespaces) { + Preconditions.checkNotNull(namespaces); + Preconditions.checkState(!mBuilt, "Builder has already been used"); + return addNamespace(Arrays.asList(namespaces)); + } + + /** + * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that + * have the specified namespaces. + * <p>If unset, the query will search over all namespaces. + */ + @NonNull + public Builder addNamespace(@NonNull Collection<String> namespaces) { Preconditions.checkNotNull(namespaces); Preconditions.checkState(!mBuilt, "Builder has already been used"); - mBundle.putStringArray(NAMESPACE_FIELD, namespaces); + mNamespaces.addAll(namespaces); return this; } @@ -224,7 +300,7 @@ public final class SearchSpec { /** Sets ranking strategy for AppSearch results.*/ @NonNull - public Builder setRankingStrategy(@RankingStrategyCode int rankingStrategy) { + public Builder setRankingStrategy(@RankingStrategy int rankingStrategy) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange(rankingStrategy, RANKING_STRATEGY_NONE, RANKING_STRATEGY_CREATION_TIMESTAMP, "Result ranking strategy"); @@ -238,7 +314,7 @@ public final class SearchSpec { * <p>This order field will be ignored if RankingStrategy = {@code RANKING_STRATEGY_NONE}. */ @NonNull - public Builder setOrder(@OrderCode int order) { + public Builder setOrder(@Order int order) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange(order, ORDER_DESCENDING, ORDER_ASCENDING, "Result ranking order"); @@ -264,11 +340,12 @@ public final class SearchSpec { } /** - * Only the first {@code matchesCountPerProperty} matches for a every property of - * {@link GenericDocument} will contain snippet information. + * Sets {@code snippetCountPerProperty}. Only the first {@code snippetCountPerProperty} + * snippets for a every property of {@link GenericDocument} will contain snippet + * information. * - * <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches} will return - * {@code null} for that result. + * <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches} + * will return {@code null} for that result. * * <p>The value should be set in range[0, 10k]. */ @@ -286,10 +363,13 @@ public final class SearchSpec { * {@code maxSnippetSize/2} bytes before the middle of the matching token and end at * {@code maxSnippetSize/2} bytes after the middle of the matching token. It respects * token boundaries, therefore the returned window may be smaller than requested. + * * <p> Setting {@code maxSnippetSize} to 0 will disable windowing and an empty string will * be returned. If matches enabled is also set to false, then snippeting is disabled. + * * <p>Ex. {@code maxSnippetSize} = 16. "foo bar baz bat rat" with a query of "baz" will * return a window of "bar baz bat" which is only 11 bytes long. + * * <p>The value should be in range[0, 10k]. */ @NonNull @@ -312,6 +392,8 @@ public final class SearchSpec { if (!mBundle.containsKey(TERM_MATCH_TYPE_FIELD)) { throw new IllegalSearchSpecException("Missing termMatchType field."); } + mBundle.putStringArrayList(NAMESPACE_FIELD, mNamespaces); + mBundle.putStringArrayList(SCHEMA_TYPE_FIELD, mSchemaTypes); mBuilt = true; return new SearchSpec(mBundle); } diff --git a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java index b2e9d469764d..f2c81564908c 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java @@ -16,13 +16,17 @@ package android.app.appsearch; +import android.annotation.SuppressLint; + import android.annotation.NonNull; +import android.app.appsearch.exceptions.AppSearchException; import android.util.ArraySet; - import com.android.internal.util.Preconditions; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Set; /** @@ -40,15 +44,13 @@ public final class SetSchemaRequest { mForceOverride = forceOverride; } - /** @hide */ - + /** Returns the schemas that are part of this request. */ @NonNull public Set<AppSearchSchema> getSchemas() { return mSchemas; } - /** @hide */ - + /** Returns whether this request will force the schema to be overridden. */ public boolean isForceOverride() { return mForceOverride; } diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java index d490469be3d6..15d0992cd081 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java +++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java @@ -54,6 +54,7 @@ public class AppSearchException extends Exception { mResultCode = resultCode; } + /** Returns the result code this exception was constructed with. */ public @AppSearchResult.ResultCode int getResultCode() { return mResultCode; } 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 7cd6ee24cb20..f2830e5b8e6d 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -33,11 +33,11 @@ import android.os.UserHandle; import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import com.android.server.SystemService; -import com.android.server.appsearch.external.localbackend.AppSearchImpl; -import com.android.server.appsearch.external.localbackend.converter.GenericDocumentToProtoConverter; -import com.android.server.appsearch.external.localbackend.converter.SchemaToProtoConverter; -import com.android.server.appsearch.external.localbackend.converter.SearchResultToProtoConverter; -import com.android.server.appsearch.external.localbackend.converter.SearchSpecToProtoConverter; +import com.android.server.appsearch.external.localstorage.AppSearchImpl; +import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; +import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter; +import com.android.server.appsearch.external.localstorage.converter.SearchResultToProtoConverter; +import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter; import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.SchemaProto; @@ -46,6 +46,7 @@ import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchSpecProto; import java.io.IOException; +import java.util.ArrayList; import java.util.List; /** @@ -193,7 +194,12 @@ public class AppSearchManagerService extends SystemService { SearchSpecToProtoConverter.toResultSpecProto(searchSpec), SearchSpecToProtoConverter.toScoringSpecProto(searchSpec)); List<SearchResult> searchResultList = - SearchResultToProtoConverter.convert(searchResultProto); + new ArrayList<>(searchResultProto.getResultsCount()); + for (int i = 0; i < searchResultProto.getResultsCount(); i++) { + SearchResult result = SearchResultToProtoConverter.convertSearchResult( + searchResultProto.getResults(i)); + searchResultList.add(result); + } SearchResults searchResults = new SearchResults(searchResultList, searchResultProto.getNextPageToken()); callback.complete(AppSearchResult.newSuccessfulResult(searchResults)); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java index 60f7005a7c0a..2871eb622f11 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java @@ -24,7 +24,7 @@ import android.os.Environment; import android.os.storage.StorageManager; import android.util.SparseArray; -import com.android.server.appsearch.external.localbackend.AppSearchImpl; +import com.android.server.appsearch.external.localstorage.AppSearchImpl; import java.io.File; diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java index 642378d992ac..b1a79f84714d 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.appsearch.external.localbackend; +package com.android.server.appsearch.external.localstorage; import android.util.Log; diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java index fdeb90dc9b0e..60684f09a202 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.android.server.appsearch.external.localbackend.converter; - -import android.os.Bundle; +package com.android.server.appsearch.external.localstorage.converter; import android.annotation.NonNull; @@ -43,7 +41,6 @@ public final class GenericDocumentToProtoConverter { @SuppressWarnings("unchecked") public static DocumentProto convert(@NonNull GenericDocument document) { Preconditions.checkNotNull(document); - Bundle properties = document.getBundle().getBundle(GenericDocument.PROPERTIES_FIELD); DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder(); mProtoBuilder.setUri(document.getUri()) .setSchema(document.getSchemaType()) @@ -51,42 +48,45 @@ public final class GenericDocumentToProtoConverter { .setScore(document.getScore()) .setTtlMs(document.getTtlMillis()) .setCreationTimestampMs(document.getCreationTimestampMillis()); - ArrayList<String> keys = new ArrayList<>(properties.keySet()); + ArrayList<String> keys = new ArrayList<>(document.getPropertyNames()); Collections.sort(keys); for (int i = 0; i < keys.size(); i++) { String name = keys.get(i); - Object values = properties.get(name); PropertyProto.Builder propertyProto = PropertyProto.newBuilder().setName(name); - if (values instanceof boolean[]) { - for (boolean value : (boolean[]) values) { - propertyProto.addBooleanValues(value); + String[] stringValues = document.getPropertyStringArray(name); + long[] longValues = document.getPropertyLongArray(name); + double[] doubleValues = document.getPropertyDoubleArray(name); + boolean[] booleanValues = document.getPropertyBooleanArray(name); + byte[][] bytesValues = document.getPropertyBytesArray(name); + GenericDocument[] documentValues = document.getPropertyDocumentArray(name); + if (stringValues != null) { + for (int j = 0; j < stringValues.length; j++) { + propertyProto.addStringValues(stringValues[j]); } - } else if (values instanceof long[]) { - for (long value : (long[]) values) { - propertyProto.addInt64Values(value); + } else if (longValues != null) { + for (int j = 0; j < longValues.length; j++) { + propertyProto.addInt64Values(longValues[j]); } - } else if (values instanceof double[]) { - for (double value : (double[]) values) { - propertyProto.addDoubleValues(value); + } else if (doubleValues != null) { + for (int j = 0; j < doubleValues.length; j++) { + propertyProto.addDoubleValues(doubleValues[j]); } - } else if (values instanceof String[]) { - for (String value : (String[]) values) { - propertyProto.addStringValues(value); + } else if (booleanValues != null) { + for (int j = 0; j < booleanValues.length; j++) { + propertyProto.addBooleanValues(booleanValues[j]); } - } else if (values instanceof ArrayList) { - for (Bundle bundle : (ArrayList<Bundle>) values) { - byte[] value = bundle.getByteArray(GenericDocument.BYTE_ARRAY_FIELD); - propertyProto.addBytesValues(ByteString.copyFrom(value)); + } else if (bytesValues != null) { + for (int j = 0; j < bytesValues.length; j++) { + propertyProto.addBytesValues(ByteString.copyFrom(bytesValues[j])); } - } else if (values instanceof Bundle[]) { - for (Bundle bundle : (Bundle[]) values) { - GenericDocument value = new GenericDocument(bundle); - propertyProto.addDocumentValues(convert(value)); + } else if (documentValues != null) { + for (int j = 0; j < documentValues.length; j++) { + DocumentProto proto = convert(documentValues[j]); + propertyProto.addDocumentValues(proto); } } else { throw new IllegalStateException( - "Property \"" + name + "\" has unsupported value type \"" - + values.getClass().getSimpleName() + "\""); + "Property \"" + name + "\" has unsupported value type"); } mProtoBuilder.addProperties(propertyProto); } @@ -107,42 +107,42 @@ public final class GenericDocumentToProtoConverter { for (int i = 0; i < proto.getPropertiesCount(); i++) { PropertyProto property = proto.getProperties(i); String name = property.getName(); - if (property.getBooleanValuesCount() > 0) { - boolean[] values = new boolean[property.getBooleanValuesCount()]; + if (property.getStringValuesCount() > 0) { + String[] values = new String[property.getStringValuesCount()]; for (int j = 0; j < values.length; j++) { - values[j] = property.getBooleanValues(j); + values[j] = property.getStringValues(j); } - documentBuilder.setProperty(name, values); + documentBuilder.setPropertyString(name, values); } else if (property.getInt64ValuesCount() > 0) { long[] values = new long[property.getInt64ValuesCount()]; for (int j = 0; j < values.length; j++) { values[j] = property.getInt64Values(j); } - documentBuilder.setProperty(name, values); + documentBuilder.setPropertyLong(name, values); } else if (property.getDoubleValuesCount() > 0) { double[] values = new double[property.getDoubleValuesCount()]; for (int j = 0; j < values.length; j++) { values[j] = property.getDoubleValues(j); } - documentBuilder.setProperty(name, values); - } else if (property.getStringValuesCount() > 0) { - String[] values = new String[property.getStringValuesCount()]; + documentBuilder.setPropertyDouble(name, values); + } else if (property.getBooleanValuesCount() > 0) { + boolean[] values = new boolean[property.getBooleanValuesCount()]; for (int j = 0; j < values.length; j++) { - values[j] = property.getStringValues(j); + values[j] = property.getBooleanValues(j); } - documentBuilder.setProperty(name, values); + documentBuilder.setPropertyBoolean(name, values); } else if (property.getBytesValuesCount() > 0) { byte[][] values = new byte[property.getBytesValuesCount()][]; for (int j = 0; j < values.length; j++) { values[j] = property.getBytesValues(j).toByteArray(); } - documentBuilder.setProperty(name, values); + documentBuilder.setPropertyBytes(name, values); } else if (property.getDocumentValuesCount() > 0) { GenericDocument[] values = new GenericDocument[property.getDocumentValuesCount()]; for (int j = 0; j < values.length; j++) { values[j] = convert(property.getDocumentValues(j)); } - documentBuilder.setProperty(name, values); + documentBuilder.setPropertyDocument(name, values); } else { throw new IllegalStateException("Unknown type of value: " + name); } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SchemaToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java index ca0d2ee970cb..403711f29544 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SchemaToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.android.server.appsearch.external.localbackend.converter; - -import android.os.Bundle; +package com.android.server.appsearch.external.localstorage.converter; import android.annotation.NonNull; @@ -28,7 +26,7 @@ import com.google.android.icing.proto.PropertyConfigProto; import com.google.android.icing.proto.SchemaTypeConfigProto; import com.google.android.icing.proto.TermMatchType; -import java.util.ArrayList; +import java.util.List; /** * Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}. @@ -45,31 +43,26 @@ public final class SchemaToProtoConverter { @NonNull public static SchemaTypeConfigProto convert(@NonNull AppSearchSchema schema) { Preconditions.checkNotNull(schema); - Bundle bundle = schema.getBundle(); SchemaTypeConfigProto.Builder protoBuilder = - SchemaTypeConfigProto.newBuilder() - .setSchemaType(bundle.getString(AppSearchSchema.SCHEMA_TYPE_FIELD, "")); - ArrayList<Bundle> properties = - bundle.getParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD); - if (properties != null) { - for (int i = 0; i < properties.size(); i++) { - PropertyConfigProto propertyProto = convertProperty(properties.get(i)); - protoBuilder.addProperties(propertyProto); - } + SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaTypeName()); + List<AppSearchSchema.PropertyConfig> properties = schema.getProperties(); + for (int i = 0; i < properties.size(); i++) { + PropertyConfigProto propertyProto = convertProperty(properties.get(i)); + protoBuilder.addProperties(propertyProto); } return protoBuilder.build(); } @NonNull - private static PropertyConfigProto convertProperty(@NonNull Bundle bundle) { - Preconditions.checkNotNull(bundle); + private static PropertyConfigProto convertProperty( + @NonNull AppSearchSchema.PropertyConfig property) { + Preconditions.checkNotNull(property); PropertyConfigProto.Builder propertyConfigProto = PropertyConfigProto.newBuilder() - .setPropertyName(bundle.getString(AppSearchSchema.PropertyConfig.NAME_FIELD, "")); + .setPropertyName(property.getName()); IndexingConfig.Builder indexingConfig = IndexingConfig.newBuilder(); // Set dataType - @AppSearchSchema.PropertyConfig.DataType int dataType = - bundle.getInt(AppSearchSchema.PropertyConfig.DATA_TYPE_FIELD); + @AppSearchSchema.PropertyConfig.DataType int dataType = property.getDataType(); PropertyConfigProto.DataType.Code dataTypeProto = PropertyConfigProto.DataType.Code.forNumber(dataType); if (dataTypeProto == null) { @@ -78,12 +71,13 @@ public final class SchemaToProtoConverter { propertyConfigProto.setDataType(dataTypeProto); // Set schemaType - propertyConfigProto.setSchemaType( - bundle.getString(AppSearchSchema.PropertyConfig.SCHEMA_TYPE_FIELD, "")); + String schemaType = property.getSchemaType(); + if (schemaType != null) { + propertyConfigProto.setSchemaType(schemaType); + } // Set cardinality - @AppSearchSchema.PropertyConfig.Cardinality int cardinality = - bundle.getInt(AppSearchSchema.PropertyConfig.CARDINALITY_FIELD); + @AppSearchSchema.PropertyConfig.Cardinality int cardinality = property.getCardinality(); PropertyConfigProto.Cardinality.Code cardinalityProto = PropertyConfigProto.Cardinality.Code.forNumber(cardinality); if (cardinalityProto == null) { @@ -92,8 +86,7 @@ public final class SchemaToProtoConverter { propertyConfigProto.setCardinality(cardinalityProto); // Set indexingType - @AppSearchSchema.PropertyConfig.IndexingType int indexingType = - bundle.getInt(AppSearchSchema.PropertyConfig.INDEXING_TYPE_FIELD); + @AppSearchSchema.PropertyConfig.IndexingType int indexingType = property.getIndexingType(); TermMatchType.Code termMatchTypeProto; switch (indexingType) { case AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE: @@ -112,7 +105,7 @@ public final class SchemaToProtoConverter { // Set tokenizerType @AppSearchSchema.PropertyConfig.TokenizerType int tokenizerType = - bundle.getInt(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_FIELD); + property.getTokenizerType(); IndexingConfig.TokenizerType.Code tokenizerTypeProto = IndexingConfig.TokenizerType.Code.forNumber(tokenizerType); if (tokenizerTypeProto == null) { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java index 524c80dd0609..9f7c6968e993 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchResultToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.appsearch.external.localbackend.converter; +package com.android.server.appsearch.external.localstorage.converter; import android.os.Bundle; @@ -22,43 +22,32 @@ import android.annotation.NonNull; import android.app.appsearch.GenericDocument; import android.app.appsearch.SearchResult; -import android.app.appsearch.SearchResults; import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SnippetMatchProto; import com.google.android.icing.proto.SnippetProto; import java.util.ArrayList; -import java.util.List; /** - * Translates a {@link SearchResultProto} into {@link SearchResults}. + * Translates a {@link SearchResultProto} into {@link SearchResult}s. + * * @hide */ public class SearchResultToProtoConverter { private SearchResultToProtoConverter() {} - /** Translates a {@link SearchResultProto} into a list of {@link SearchResult}. */ - @NonNull - public static List<SearchResult> convert(@NonNull SearchResultProto searchResultProto) { - List<SearchResult> results = new ArrayList<>(searchResultProto.getResultsCount()); - for (int i = 0; i < searchResultProto.getResultsCount(); i++) { - results.add(convertSearchResult(searchResultProto.getResults(i))); - } - return results; - } - /** Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. */ @NonNull - static SearchResult convertSearchResult(@NonNull SearchResultProto.ResultProto proto) { + public static SearchResult convertSearchResult( + @NonNull SearchResultProto.ResultProtoOrBuilder proto) { Bundle bundle = new Bundle(); GenericDocument document = GenericDocumentToProtoConverter.convert(proto.getDocument()); bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle()); - ArrayList<Bundle> matchList = null; + ArrayList<Bundle> matchList = new ArrayList<>(); if (proto.hasSnippet()) { - matchList = new ArrayList<>(); for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) { SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i); for (int j = 0; j < entry.getSnippetMatchesCount(); j++) { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchSpecToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java index a5d913a20590..14822dcdc793 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchSpecToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.android.server.appsearch.external.localbackend.converter; - -import android.os.Bundle; +package com.android.server.appsearch.external.localstorage.converter; import android.annotation.NonNull; @@ -28,8 +26,6 @@ import com.google.android.icing.proto.ScoringSpecProto; import com.google.android.icing.proto.SearchSpecProto; import com.google.android.icing.proto.TermMatchType; -import java.util.Arrays; - /** * Translates a {@link SearchSpec} into icing search protos. * @hide @@ -42,25 +38,17 @@ public final class SearchSpecToProtoConverter { @NonNull public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) { Preconditions.checkNotNull(spec); - Bundle bundle = spec.getBundle(); - SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder(); + SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder() + .addAllSchemaTypeFilters(spec.getSchemas()) + .addAllNamespaceFilters(spec.getNamespaces()); - @SearchSpec.TermMatchCode int termMatchCode = - bundle.getInt(SearchSpec.TERM_MATCH_TYPE_FIELD); + @SearchSpec.TermMatch int termMatchCode = spec.getTermMatch(); TermMatchType.Code termMatchCodeProto = TermMatchType.Code.forNumber(termMatchCode); if (termMatchCodeProto == null || termMatchCodeProto.equals(TermMatchType.Code.UNKNOWN)) { throw new IllegalArgumentException("Invalid term match type: " + termMatchCode); } protoBuilder.setTermMatchType(termMatchCodeProto); - String[] schemaTypes = bundle.getStringArray(SearchSpec.SCHEMA_TYPES_FIELD); - if (schemaTypes != null) { - protoBuilder.addAllSchemaTypeFilters(Arrays.asList(schemaTypes)); - } - String[] namespaces = bundle.getStringArray(SearchSpec.NAMESPACE_FIELD); - if (namespaces != null) { - protoBuilder.addAllNamespaceFilters(Arrays.asList(namespaces)); - } return protoBuilder.build(); } @@ -68,27 +56,23 @@ public final class SearchSpecToProtoConverter { @NonNull public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) { Preconditions.checkNotNull(spec); - Bundle bundle = spec.getBundle(); return ResultSpecProto.newBuilder() - .setNumPerPage(bundle.getInt( - SearchSpec.NUM_PER_PAGE_FIELD, SearchSpec.DEFAULT_NUM_PER_PAGE)) - .setSnippetSpec(ResultSpecProto.SnippetSpecProto.newBuilder() - .setNumToSnippet(bundle.getInt(SearchSpec.SNIPPET_COUNT_FIELD)) - .setNumMatchesPerProperty( - bundle.getInt(SearchSpec.SNIPPET_COUNT_PER_PROPERTY_FIELD)) - .setMaxWindowBytes(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD))) + .setNumPerPage(spec.getNumPerPage()) + .setSnippetSpec( + ResultSpecProto.SnippetSpecProto.newBuilder() + .setNumToSnippet(spec.getSnippetCount()) + .setNumMatchesPerProperty(spec.getSnippetCountPerProperty()) + .setMaxWindowBytes(spec.getMaxSnippetSize())) .build(); - } /** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */ @NonNull public static ScoringSpecProto toScoringSpecProto(@NonNull SearchSpec spec) { Preconditions.checkNotNull(spec); - Bundle bundle = spec.getBundle(); ScoringSpecProto.Builder protoBuilder = ScoringSpecProto.newBuilder(); - @SearchSpec.OrderCode int orderCode = bundle.getInt(SearchSpec.ORDER_FIELD); + @SearchSpec.Order int orderCode = spec.getOrder(); ScoringSpecProto.Order.Code orderCodeProto = ScoringSpecProto.Order.Code.forNumber(orderCode); if (orderCodeProto == null) { @@ -96,8 +80,7 @@ public final class SearchSpecToProtoConverter { } protoBuilder.setOrderBy(orderCodeProto); - @SearchSpec.RankingStrategyCode int rankingStrategyCode = - bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD); + @SearchSpec.RankingStrategy int rankingStrategyCode = spec.getRankingStrategy(); ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto = ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategyCode); if (rankingStrategyCodeProto == null) { diff --git a/apex/extservices/Android.bp b/apex/extservices/Android.bp deleted file mode 100644 index 0c6c4c23dce1..000000000000 --- a/apex/extservices/Android.bp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -apex { - name: "com.android.extservices", - defaults: ["com.android.extservices-defaults"], - manifest: "apex_manifest.json", -} - -apex_defaults { - name: "com.android.extservices-defaults", - updatable: true, - min_sdk_version: "current", - key: "com.android.extservices.key", - certificate: ":com.android.extservices.certificate", - apps: ["ExtServices"], -} - -apex_key { - name: "com.android.extservices.key", - public_key: "com.android.extservices.avbpubkey", - private_key: "com.android.extservices.pem", -} - -android_app_certificate { - name: "com.android.extservices.certificate", - certificate: "com.android.extservices", -} diff --git a/apex/extservices/apex_manifest.json b/apex/extservices/apex_manifest.json deleted file mode 100644 index b4acf1283d3e..000000000000 --- a/apex/extservices/apex_manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.extservices", - "version": 300000000 -} diff --git a/apex/extservices/com.android.extservices.avbpubkey b/apex/extservices/com.android.extservices.avbpubkey Binary files differdeleted file mode 100644 index f37d3e4a14d4..000000000000 --- a/apex/extservices/com.android.extservices.avbpubkey +++ /dev/null diff --git a/apex/extservices/com.android.extservices.pem b/apex/extservices/com.android.extservices.pem deleted file mode 100644 index 7bfbd34ff9b9..000000000000 --- a/apex/extservices/com.android.extservices.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEAuYshVDiRkt3tmBhqcWkKOm5GcviKpLbHSPpYQDHGDwS0dqqL -SqAd1/BgT/bVVtUkAciFApPnXn96WhNYCypptyC5FHCxM21uBCGmow+3WermD++w -5dQk4QP2ONPIpG+KzOWBl9SiBud4SpOHDyr0JycBsrXS89Tln9kAsTDuDEFfXL/J -8cX/S3IUwhPV0pAlgUIHdDp0DGFjZaJlEZBZ+HmImriC/AUNUMVb5lfbczXOEZPF -0A9+JzYschfXUxn8nu1N7RN5GDbq+chszx1FMVhuFUheukkd4dLNSDl0O0RlUnD+ -C/xz1ilDzEVZhnMtMnxS9oJ8bA/HUVMfsFnaQbgGmQ0CcxFxnfbYyGXGG1H+b8vA -MTVQi5rZXG2p+VgHIAKVrYmpETVnRPgoMqp18KuGtp5SDngi13G3YEzS7iFbqfYh -6iW2G974nD/Dq0cSire8Oljd9PEaMCMZiP5PTFJp0G/mtw7ROoyZqsSM6rX3XVTo -Y5dBmBMctSJ8rgDMi0ZNvRH+rq/E5+RT6yMAJ7DDbOJzBnQ3IIoGn8NzUT3P1FCB -HYEp1U2N7QNirIQMAuVz3IlHae9N1kl3eGAO6f2CjV7vZmFpDeWw+KSYs71mRkOb -WBgl6D9FFq4u1azrU3AwV0dj3x1eU6yVnKUy1J7ppF/mcR+VzH7ThzTdV7cCAwEA -AQKCAgEApWFU2Mv/PYhg0bPZlLLKsiA+3RWaBo0AfpTd+oIjBpnr/OWweFjVoPcZ -8cyShe4/RPOlUxHgJcO8m/MoA/PO/LLHJWf5GlzMthQEgs1sYVJVtBiydXitUn+E -hUyIR8FAV7et1lZqAXtqJhbvSF7B9u/2vIMCv+GgtuTmkAmL9RKD3Jj6eG1CS84o -oICrkx52v4rKOBgt/icEQMAKFCi1eRti3n3eCqK6JqdzbZIcAcoQnmw34mccy/im -jx+fBuxf1oywa8NyqVmyAehazBVL6lrm7ENwY9zuLK4H2fuUFYu2QFCEsMxZt6da -TgX2cTfSLnDQRfcyzeMWhu9vjHHabjpLNjiCKhIhGyO0rO1rtea8ajZHgM/2sxXq -6gLynW0dlatlxmjANlN9WQPGNdzvcIFJ0TLnI4mlJnWpqCsN9iW1d4ey13WiZUVR -DgtnR60zao+LRCCM4D3cuVLq0DjL2BlHGXnOPK/LpQG1LbI1TroZpgSEHSZlQRzT -ql9txgNqTHxijXuPL2VhhwhW7cqDoO8sLwV3BqDMIH56U0cbUBiSA/G9fKeI/DEG -i7LcrMgrBk+xnuAWoFHuzfBMAdD9i3kYyk+41tOmcza2TNJgxadVYp5woHFvYvS/ -GKaNiRz0XmcijO5Ir0yxgCq21BdkWzo5zVrTFABiKeR7YXiee8kCggEBAOeULWgR -spolJJrACWJspRvKb9FGnbGiYOnCGJoAc751kuXmNxoyWnEwgcjrSEoayNPUfOtz -IgA+twqjgl0Zec2XFPfUcgWUBrrvvUEV4NIH5ibaR7ezHGeovCWs9XoDyzHHvhDr -c6T5kXFZ60rS5h6LGUnE1hkHFJoHuTIBbn9j7eIbri8S71i7HWQ04s4KuQ+Bwbxm -UnkEhbc+zMWHXfXy7rx4/eEZcZwtEybIORcHXYNPGeqMfOlcEMHpKEOi+NvDA6cp -vTaTSwJ6ZBgYh7Tw3bNgRxSknaIhcGwMD0ojStjC5xzXT1Zr2Z3GXwYvOGcq3MeZ -z+V2cx5xuwyp7R0CggEBAM0cKKNZEZwi/1zBPUDMFB4iJoX12BxQX6e5wdlHGXgF -XeZwCnaIxOxMDxH79M5Svmpdu/jkUijI/pRvcE1iohFyIBvTUSDmlAoy4keXqMEQ -M2hA+TwVA3JLmMcV8HKy/MFlwwKJB1JDcoxGjnXsM5UjVTD2jilO7vlJZs3+0ws0 -R7qzRT3ED25QTpZyDYcKE2otc5bzIZG3yAaJtWd3NugWsKpxDgr2RFUGJiHBq72n -48FkSjfgaDTn83zYcPvS0Uykb2ho8G/N+EurstL41n3nQo0I7FLbyptOopDDwsSp -Ndejn08NVAQ+xFAafOyqHkA3Ytpl0QCZDpMBuLdvw+MCggEAOVMt1kgjPRMat4/4 -ArxANtvqyBRB7vnyIYthiaW5ARmbrntJgpuaVdCbIABWGbn9oqpD7gjHDuZ3axPE -roUi6KiQkTSusQDOlbHI2Haw+2znJRD9ldSpoGNdh7oD3htYTk9Sll+ideEthrCq -lRAV1NO8A83M7c8Z43Mr/dvq3XAAL+uIN7DpPL687NRGnJh87QDC039ExR5Ad3b9 -O5xhvwNO46rTtcgVnoJt7ji8IR46oMmQ8cWrGh0nLMkppWyPS98/ZT7ozryxYcCo -TGquFTVWvBOGJO8G8l5ytNxbYI/R9Exy52nJAuyZpvu3BBHmVWt/0Y0asIOcxZmD -owPhZQKCAQAfWAFBzReq05JQe1s/7q/YVwGqEQKgeQvVFsbvzDSxKajK0S5YJNhq -/8iByA4GBZEBsidKhqGjh+uXhVwVB1Ca9+S+O9G3BGV1FYeMxzlLn40rjlpH+zIW -okTLj6e5724+o61kUspioNn9Y77beGf9j3OyUsswttZAFB54tktL+AZKGqEnKjHt -eqo3xWAZ1clXvXBfjfIAUaRok1y8XfRvDSCcO0CZHj8c+x6SpAT5q5FbeVb6KPnj -s9p6ppzFbtb7Llm0C+1KOKCL98YRBWPJw7Bg2w86LkpM53xiQPgfk3gd5uwuaWwA -ZhMb5qBWjjynNY+OrmZ8/+bBQk8XASZfAoIBAFkHOnZOD1JJQ0QvaJ9tuCgHi216 -I8QPMMTdm3ZEDHSYMNwl7ayeseBcmB2zaqBKYz75qcU0SK4lnZkR2wIpbsHZNSVM -J0WpN6r9G4JdnVi11J04RsfSMjCUr/PTVMmPvw8xPHrCxkJmB+d56olSE80I1Jrx -djCv1LtSsT10W7FIcY82/cOi4xxGLOA70lDCf+szofQgVP8WvuOA1YaFw98ca8zc -A401CyNexk24/c3d6C19YW/MppdE0uGMxL/oHsPgwkZAf6LmvF/UF71PsBUEniLc -YFaJl3wn1cPfBBo9L4sZzyP2qokL8YHdg+wW7b4IOsYwbeqceBvqPtcUUPs= ------END RSA PRIVATE KEY----- diff --git a/apex/extservices/com.android.extservices.pk8 b/apex/extservices/com.android.extservices.pk8 Binary files differdeleted file mode 100644 index 59585a212592..000000000000 --- a/apex/extservices/com.android.extservices.pk8 +++ /dev/null diff --git a/apex/extservices/com.android.extservices.x509.pem b/apex/extservices/com.android.extservices.x509.pem deleted file mode 100644 index e0343b81d279..000000000000 --- a/apex/extservices/com.android.extservices.x509.pem +++ /dev/null @@ -1,36 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGLTCCBBWgAwIBAgIUdqdMmx/5OsCP3Ew3/hcr7+1ACHEwDQYJKoZIhvcNAQEL -BQAwgaQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH -DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy -b2lkMSAwHgYDVQQDDBdjb20uYW5kcm9pZC5leHRzZXJ2aWNlczEiMCAGCSqGSIb3 -DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAgFw0yMDAxMTcxMDIxMzZaGA80NzU3 -MTIxMzEwMjEzNlowgaQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh -MRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYD -VQQLDAdBbmRyb2lkMSAwHgYDVQQDDBdjb20uYW5kcm9pZC5leHRzZXJ2aWNlczEi -MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCAiIwDQYJKoZIhvcN -AQEBBQADggIPADCCAgoCggIBANKaSeLGaFRRt779vAtTfG3t2aQZrWOByUYc7yUN -RdmJqWxU47OL5urYmanWPbz2f972Q9oi8x+8y4ny9SEY3wg0pUbzvKNTXpkxWyG1 -HE2C2zTfzuDDLpDIf2usWynt1wLVhpYC3k+7Yv2vOIK5dKkezh6PfdKmsbDae5DE -d22tTSYZ5KwNpIWrgQle26cRG5sqhAFdkpgGMF00Huz06cjUoTjs2sNSlXTRBOTP -CCy8UoRjBivQZkwHbddfsn+Z22ARPG8JDg/n4mEi8C0T6bJeQeirSPkBCkD6Djgq -7RddJ2eLYZII8l8r6A6x+6cnTkXHaV5g3LUwPvi8XEn9IUuT9WJNRje/vfYLycTQ -kP415CZMxDvsi1Ul4YsbL3enE89ryGMTpVZPogch/36DG5Sye28yISItNUy3urJa -OXbg7mh+MwPd4bQaW4CJk+AUweKaF4aV0SZFT+nCewL4xLdGdy889KazlW98NqtK -hOSxIg1jHkZq48ajuq2A+ns1yDKt1l0f9IYCz3mz/IXInokbkjPvHahJTJ+OMHXO -THD8e5gBzcK841jJk+H3EsIYOHsp66uy2IgEHN+9pAS6vI0xfrXOYuKzuSL3oxcV -FlVTimt4xokMMerdcW4KD+MC5NFEip4DUS4JKCyG0wRI3ffEs9Zcpxi3QSibrjLW -rz+hAgMBAAGjUzBRMB0GA1UdDgQWBBTP2AhZzEUUgtAFlkaMaq+RvY06fDAfBgNV -HSMEGDAWgBTP2AhZzEUUgtAFlkaMaq+RvY06fDAPBgNVHRMBAf8EBTADAQH/MA0G -CSqGSIb3DQEBCwUAA4ICAQCbwtfo37j62Sudmt32PCfRN/r5ZNDNNA2JhR8uDUmX -xXfF5YfDvSKsNLiQKcDagu6a+0C+QnzXHXCBlXZFrTJ8NAVMlmqdHGwoFoYMfJZH -R1lCTidyFMoMLJ8GRGPJjzDkKnOeAqKMCtKvXoH2r12+JB2/ov4ooLREu/wPkEXT -OymkyWNP5XLQTKWqfEQyXXFpuwZ+m35Wkr0Fm92mZeJpVeIZPK7M7aK3zyoj7XJP -YLMsR/AQs8OULdpfNMddAuN3ndlYu03LZlsF6LG5bduaDDcESJ5hdJrgBa/NBKRU -IbS+q/6WAjYKMNRT/fPGew4wUzlWKi1Ihdk79oaqKKijE1b2JSJD1/SEYiBf+JPE -bXobUrMbBwFpdhT+YLMF9FsuPQKsUIONaWiO4QcQoY/rQwGxPP6fV8ZbBrUWJewj -MpSdU9foZNa/TmOAgfS/JxH+nXnG4+H1m8mdNBsxvsYmF2ZuGb/jdEeA2cuHIJDZ -FJeWwCFxzlCGZJaUsxsnZByADBuufUVaO/9gGs0YQC/JP1i9hK4DyZdKwZpXdLi2 -Nw27Qma4WEIZnMb6Rgk1nTV+7ALcOSIhGgFOOeDTuCGfnEcz2coai5fbD/K6Q7Xu -IRNyxHQjheZPdei2x912Ex/KqKGfaFaZJxrvCSKdhzxcTFIsO4JuZs+SDpRTKcI7 -Cw== ------END CERTIFICATE----- diff --git a/apex/extservices/testing/Android.bp b/apex/extservices/testing/Android.bp deleted file mode 100644 index 88a47246c824..000000000000 --- a/apex/extservices/testing/Android.bp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -apex_test { - name: "test_com.android.extservices", - visibility: [ - "//system/apex/tests", - ], - defaults: ["com.android.extservices-defaults"], - manifest: "test_manifest.json", - file_contexts: ":com.android.extservices-file_contexts", - // Test APEX, should never be installed - installable: false, -} diff --git a/apex/extservices/testing/test_manifest.json b/apex/extservices/testing/test_manifest.json deleted file mode 100644 index 23a50e37bdd3..000000000000 --- a/apex/extservices/testing/test_manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.extservices", - "version": 2147483647 -} diff --git a/api/current.txt b/api/current.txt index 11d445fb6bab..9ff7cc255bf4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -57228,8 +57228,8 @@ package android.view.inputmethod { method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); @@ -57455,8 +57455,8 @@ package android.view.inputmethod { method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); @@ -57490,8 +57490,8 @@ package android.view.inputmethod { method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); diff --git a/api/system-current.txt b/api/system-current.txt index 4f74874a7838..5ef042f062b1 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -682,7 +682,7 @@ package android.app { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners(); method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); - method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean); field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; @@ -7494,6 +7494,7 @@ package android.net.wifi { field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR; field public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1L; // 0x1L field public static final long SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 2L; // 0x2L + field public static final long SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION = 8L; // 0x8L field public static final long SOFTAP_FEATURE_WPA3_SAE = 4L; // 0x4L } @@ -7584,8 +7585,11 @@ package android.net.wifi { field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0 field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1 field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11 + field @Deprecated public static final int RECENT_FAILURE_DISCONNECTION_AP_BUSY = 1004; // 0x3ec field @Deprecated public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001; // 0x3e9 field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0 + field @Deprecated public static final int RECENT_FAILURE_POOR_CHANNEL_CONDITIONS = 1003; // 0x3eb + field @Deprecated public static final int RECENT_FAILURE_REFUSED_TEMPORARILY = 1002; // 0x3ea field @Deprecated public boolean allowAutojoin; field @Deprecated public int carrierId; field @Deprecated public String creatorName; @@ -7672,8 +7676,11 @@ package android.net.wifi { method public double getSuccessfulRxPacketsPerSecond(); method public double getSuccessfulTxPacketsPerSecond(); method public boolean isEphemeral(); + method public boolean isOemPaid(); + method public boolean isOemPrivate(); method public boolean isOsuAp(); method public boolean isPasspointAp(); + method public boolean isTrusted(); method @Nullable public static String sanitizeSsid(@Nullable String); field public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00"; field public static final int INVALID_RSSI = -127; // 0xffffff81 @@ -7899,11 +7906,13 @@ package android.net.wifi { public final class WifiNetworkSuggestion implements android.os.Parcelable { method @NonNull public android.net.wifi.WifiConfiguration getWifiConfiguration(); method public boolean isOemPaid(); + method public boolean isOemPrivate(); } public static final class WifiNetworkSuggestion.Builder { method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int); method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setOemPaid(boolean); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setOemPrivate(boolean); } public class WifiScanner { @@ -11769,7 +11778,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorIconIndex(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorDisplayNumber(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(); diff --git a/api/test-current.txt b/api/test-current.txt index 0a8f7a59d3b1..edc422dbf24d 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -270,6 +270,7 @@ package android.app { method public void disallowAssistantAdjustment(String); method public android.content.ComponentName getEffectsSuppressor(); method public boolean matchesCallFilter(android.os.Bundle); + method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean); method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel); } @@ -289,6 +290,7 @@ package android.app { method @NonNull public android.content.res.Configuration getConfiguration(); method @Nullable public android.app.PictureInPictureParams getPictureInPictureParams(); method @NonNull public android.window.WindowContainerToken getToken(); + method public boolean hasParentTask(); } public class TimePickerDialog extends android.app.AlertDialog implements android.content.DialogInterface.OnClickListener android.widget.TimePicker.OnTimeChangedListener { @@ -969,6 +971,14 @@ package android.media { field public static final String SAMPLE_RATE = "android.media.audiotrack.sampleRate"; } + public abstract class Image implements java.lang.AutoCloseable { + ctor protected Image(); + } + + public abstract static class Image.Plane { + ctor protected Image.Plane(); + } + public final class MediaCas implements java.lang.AutoCloseable { method public void forceResourceLost(); } diff --git a/non-updatable-api/Android.bp b/core/api/Android.bp index 00b901992b90..00b901992b90 100644 --- a/non-updatable-api/Android.bp +++ b/core/api/Android.bp diff --git a/non-updatable-api/current.txt b/core/api/current.txt index ff2e1d490c2e..16a5f5e82f21 100644 --- a/non-updatable-api/current.txt +++ b/core/api/current.txt @@ -55346,8 +55346,8 @@ package android.view.inputmethod { method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); @@ -55573,8 +55573,8 @@ package android.view.inputmethod { method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); @@ -55608,8 +55608,8 @@ package android.view.inputmethod { method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); - method public CharSequence getTextAfterCursor(int, int); - method public CharSequence getTextBeforeCursor(int, int); + method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); + method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); diff --git a/non-updatable-api/module-lib-current.txt b/core/api/module-lib-current.txt index 06e6d9ccbbf0..06e6d9ccbbf0 100644 --- a/non-updatable-api/module-lib-current.txt +++ b/core/api/module-lib-current.txt diff --git a/non-updatable-api/module-lib-removed.txt b/core/api/module-lib-removed.txt index d802177e249b..d802177e249b 100644 --- a/non-updatable-api/module-lib-removed.txt +++ b/core/api/module-lib-removed.txt diff --git a/non-updatable-api/removed.txt b/core/api/removed.txt index f2dfb84eb8fe..f2dfb84eb8fe 100644 --- a/non-updatable-api/removed.txt +++ b/core/api/removed.txt diff --git a/non-updatable-api/system-current.txt b/core/api/system-current.txt index 217c4b0674f5..eea50be6da1d 100644 --- a/non-updatable-api/system-current.txt +++ b/core/api/system-current.txt @@ -682,7 +682,7 @@ package android.app { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners(); method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); - method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean); field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; @@ -10620,7 +10620,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorIconIndex(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorDisplayNumber(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(); diff --git a/non-updatable-api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index 4db55e7e07eb..4db55e7e07eb 100644 --- a/non-updatable-api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt diff --git a/non-updatable-api/system-removed.txt b/core/api/system-removed.txt index 0c02c43b1084..0c02c43b1084 100644 --- a/non-updatable-api/system-removed.txt +++ b/core/api/system-removed.txt diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index c052186bb974..d798f22a6af9 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -177,9 +177,9 @@ interface INotificationManager boolean isNotificationListenerAccessGranted(in ComponentName listener); boolean isNotificationListenerAccessGrantedForUser(in ComponentName listener, int userId); boolean isNotificationAssistantAccessGranted(in ComponentName assistant); - void setNotificationListenerAccessGranted(in ComponentName listener, boolean enabled); + void setNotificationListenerAccessGranted(in ComponentName listener, boolean enabled, boolean userSet); void setNotificationAssistantAccessGranted(in ComponentName assistant, boolean enabled); - void setNotificationListenerAccessGrantedForUser(in ComponentName listener, int userId, boolean enabled); + void setNotificationListenerAccessGrantedForUser(in ComponentName listener, int userId, boolean enabled, boolean userSet); void setNotificationAssistantAccessGrantedForUser(in ComponentName assistant, int userId, boolean enabled); List<String> getEnabledNotificationListenerPackages(); List<ComponentName> getEnabledNotificationListeners(int userId); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 27cd78acb35f..12460ba2bd4b 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1565,6 +1565,12 @@ public class NotificationManager { } } + /** @hide */ + public void setNotificationListenerAccessGranted( + @NonNull ComponentName listener, boolean granted) { + setNotificationListenerAccessGranted(listener, granted, true); + } + /** * Grants/revokes Notification Listener access to the given component for current user. * To grant access for a particular user, obtain this service by using the {@link Context} @@ -1572,15 +1578,17 @@ public class NotificationManager { * * @param listener Name of component to grant/revoke access * @param granted Grant/revoke access + * @param userSet Whether the action was triggered explicitly by user * @hide */ @SystemApi + @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted( - @NonNull ComponentName listener, boolean granted) { + @NonNull ComponentName listener, boolean granted, boolean userSet) { INotificationManager service = getService(); try { - service.setNotificationListenerAccessGranted(listener, granted); + service.setNotificationListenerAccessGranted(listener, granted, userSet); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1591,7 +1599,7 @@ public class NotificationManager { boolean granted) { INotificationManager service = getService(); try { - service.setNotificationListenerAccessGrantedForUser(listener, userId, granted); + service.setNotificationListenerAccessGrantedForUser(listener, userId, granted, true); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 4b3bebe36c29..73777909d417 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -136,6 +136,8 @@ import android.net.lowpan.ILowpanManager; import android.net.lowpan.LowpanManager; import android.net.nsd.INsdManager; import android.net.nsd.NsdManager; +import android.net.vcn.IVcnManagementService; +import android.net.vcn.VcnManager; import android.net.wifi.WifiFrameworkInitializer; import android.net.wifi.nl80211.WifiNl80211Manager; import android.nfc.NfcManager; @@ -385,6 +387,14 @@ public final class SystemServiceRegistry { ctx, () -> ServiceManager.getService(Context.TETHERING_SERVICE)); }}); + registerService(Context.VCN_MANAGEMENT_SERVICE, VcnManager.class, + new CachedServiceFetcher<VcnManager>() { + @Override + public VcnManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.VCN_MANAGEMENT_SERVICE); + IVcnManagementService service = IVcnManagementService.Stub.asInterface(b); + return new VcnManager(ctx, service); + }}); registerService(Context.IPSEC_SERVICE, IpSecManager.class, new CachedServiceFetcher<IpSecManager>() { diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 45e9c49c5322..43b7722c0864 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -16,6 +16,8 @@ package android.app; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -204,6 +206,13 @@ public class TaskInfo { */ public ArrayList<IBinder> launchCookies = new ArrayList<>(); + /** + * The identifier of the parent task that is created by organizer, otherwise + * {@link ActivityTaskManager#INVALID_TASK_ID}. + * @hide + */ + public int parentTaskId; + TaskInfo() { // Do nothing } @@ -253,6 +262,12 @@ public class TaskInfo { launchCookies.add(cookie); } + /** @hide */ + @TestApi + public boolean hasParentTask() { + return parentTaskId != INVALID_TASK_ID; + } + /** * Reads the TaskInfo from a parcel. */ @@ -283,6 +298,7 @@ public class TaskInfo { source.readBinderList(launchCookies); letterboxActivityBounds = source.readTypedObject(Rect.CREATOR); positionInParent = source.readTypedObject(Point.CREATOR); + parentTaskId = source.readInt(); } /** @@ -316,6 +332,7 @@ public class TaskInfo { dest.writeBinderList(launchCookies); dest.writeTypedObject(letterboxActivityBounds, flags); dest.writeTypedObject(positionInParent, flags); + dest.writeInt(parentTaskId); } @Override @@ -338,6 +355,7 @@ public class TaskInfo { + " launchCookies" + launchCookies + " letterboxActivityBounds=" + letterboxActivityBounds + " positionInParent=" + positionInParent + + " parentTaskId: " + parentTaskId + "}"; } } diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index c4af4edb467b..4ae1670e9041 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -796,9 +796,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu /** * Returns {@code true} if the windowingMode represents a window in multi-window mode. * I.e. sharing the screen with another activity. - * - * TODO(b/171672645): This API could be misleading - in 'undefined' mode it's determined by the - * parent's mode * @hide */ public static boolean inMultiWindowMode(int windowingMode) { diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index c854abac291e..587e883edaf2 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -20,7 +20,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.XmlResourceParser; -import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.storage.StorageManager; @@ -92,7 +91,7 @@ public class FullBackup { /** * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage static public native int backupToTar(String packageName, String domain, String linkdomain, String rootpath, String path, FullBackupDataOutput output); diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS index 9c21e8fe5e45..7e7710b4d550 100644 --- a/core/java/android/app/backup/OWNERS +++ b/core/java/android/app/backup/OWNERS @@ -1,9 +1,4 @@ -alsutton@google.com -anniemeng@google.com -brufino@google.com -bryanmawhinney@google.com -ctate@google.com -jorlow@google.com -nathch@google.com -rthakohov@google.com +# Bug component: 656484 + +include platform/frameworks/base/services/backup:/OWNERS diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java index 4796d8db3e96..262d244c5b1c 100644 --- a/core/java/android/app/time/TimeManager.java +++ b/core/java/android/app/time/TimeManager.java @@ -110,7 +110,7 @@ public final class TimeManager { @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public boolean updateTimeZoneConfiguration(@NonNull TimeZoneConfiguration configuration) { if (DEBUG) { - Log.d(TAG, "updateConfiguration called: " + configuration); + Log.d(TAG, "updateTimeZoneConfiguration called: " + configuration); } try { return mITimeZoneDetectorService.updateConfiguration(configuration); diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java index 7cc67b310f31..a27be96e6e69 100644 --- a/core/java/android/app/time/TimeZoneCapabilities.java +++ b/core/java/android/app/time/TimeZoneCapabilities.java @@ -180,8 +180,7 @@ public final class TimeZoneCapabilities implements Parcelable { public TimeZoneConfiguration tryApplyConfigChanges( @NonNull TimeZoneConfiguration config, @NonNull TimeZoneConfiguration requestedChanges) { - TimeZoneConfiguration.Builder newConfigBuilder = - new TimeZoneConfiguration.Builder(config); + TimeZoneConfiguration.Builder newConfigBuilder = new TimeZoneConfiguration.Builder(config); if (requestedChanges.hasIsAutoDetectionEnabled()) { if (this.getConfigureAutoDetectionEnabledCapability() < CAPABILITY_NOT_APPLICABLE) { return null; diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 475be121b735..c07cd52c581e 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -687,6 +688,8 @@ public final class BluetoothAdapter { private final Map<LeScanCallback, ScanCallback> mLeScanClients; private static final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>> sMetadataListeners = new HashMap<>(); + private final Map<BluetoothConnectionCallback, Executor> + mBluetoothConnectionCallbackExecutorMap = new HashMap<>(); /** * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener @@ -3573,6 +3576,133 @@ public final class BluetoothAdapter { @Nullable byte[] value); } + private final IBluetoothConnectionCallback mConnectionCallback = + new IBluetoothConnectionCallback.Stub() { + @Override + public void onDeviceConnected(BluetoothDevice device) { + for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry: + mBluetoothConnectionCallbackExecutorMap.entrySet()) { + BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); + Executor executor = callbackExecutorEntry.getValue(); + executor.execute(() -> callback.onDeviceConnected(device)); + } + } + + @Override + public void onDeviceDisconnected(BluetoothDevice device) { + for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry: + mBluetoothConnectionCallbackExecutorMap.entrySet()) { + BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); + Executor executor = callbackExecutorEntry.getValue(); + executor.execute(() -> callback.onDeviceDisconnected(device)); + } + } + }; + + /** + * Registers the BluetoothConnectionCallback to receive callback events when a bluetooth device + * (classic or low energy) is connected or disconnected. + * + * @param executor is the callback executor + * @param callback is the connection callback you wish to register + * @return true if the callback was registered successfully, false otherwise + * @throws IllegalArgumentException if the callback is already registered + * @hide + */ + public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull BluetoothConnectionCallback callback) { + if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()"); + if (callback == null) { + return false; + } + + // If the callback map is empty, we register the service-to-app callback + if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) { + return false; + } + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + mBluetoothConnectionCallbackExecutorMap.remove(callback); + } finally { + mServiceLock.readLock().unlock(); + } + } + + // Adds the passed in callback to our map of callbacks to executors + synchronized (mBluetoothConnectionCallbackExecutorMap) { + if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) { + throw new IllegalArgumentException("This callback has already been registered"); + } + mBluetoothConnectionCallbackExecutorMap.put(callback, executor); + } + + return true; + } + + /** + * Unregisters the BluetoothConnectionCallback that was previously registered by the application + * + * @param callback is the connection callback you wish to unregister + * @return true if the callback was unregistered successfully, false otherwise + * @hide + */ + public boolean unregisterBluetoothConnectionCallback( + @NonNull BluetoothConnectionCallback callback) { + if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()"); + if (callback == null) { + return false; + } + + synchronized (mBluetoothConnectionCallbackExecutorMap) { + if (mBluetoothConnectionCallbackExecutorMap.remove(callback) != null) { + return false; + } + } + + if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { + return true; + } + + // If the callback map is empty, we unregister the service-to-app callback + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.unregisterBluetoothConnectionCallback(mConnectionCallback); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + + return false; + } + + /** + * This abstract class is used to implement callbacks for when a bluetooth classic or Bluetooth + * Low Energy (BLE) device is either connected or disconnected. + * + * @hide + */ + public abstract class BluetoothConnectionCallback { + /** + * Callback triggered when a bluetooth device (classic or BLE) is connected + * @param device is the connected bluetooth device + */ + public void onDeviceConnected(BluetoothDevice device) {} + + /** + * Callback triggered when a bluetooth device (classic or BLE) is disconnected + * @param device is the disconnected bluetooth device + */ + public void onDeviceDisconnected(BluetoothDevice device) {} + } + /** * Converts old constant of priority to the new for connection policy * diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 1d7a54c3021c..46d4f222a6b4 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3416,6 +3416,7 @@ public abstract class Context { VIBRATOR_SERVICE, //@hide: STATUS_BAR_SERVICE, CONNECTIVITY_SERVICE, + VCN_MANAGEMENT_SERVICE, //@hide: IP_MEMORY_STORE_SERVICE, IPSEC_SERVICE, VPN_MANAGEMENT_SERVICE, @@ -3995,6 +3996,16 @@ public abstract class Context { public static final String CONNECTIVITY_SERVICE = "connectivity"; /** + * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.vcn.VcnManager} + * for managing Virtual Carrier Networks + * + * @see #getSystemService(String) + * @see android.net.vcn.VcnManager + * @hide + */ + public static final String VCN_MANAGEMENT_SERVICE = "vcn_management"; + + /** * Use with {@link #getSystemService(String)} to retrieve a * {@link android.net.INetd} for communicating with the network stack * @hide diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c0f581717641..946c634e3b41 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1712,6 +1712,15 @@ public abstract class PackageManager { public static final int DELETE_FAILED_USED_SHARED_LIBRARY = -6; /** + * Deletion failed return code: this is passed to the + * {@link IPackageDeleteObserver} if the system failed to delete the package + * because there is an app pinned. + * + * @hide + */ + public static final int DELETE_FAILED_APP_PINNED = -7; + + /** * Return code that is passed to the {@link IPackageMoveObserver} when the * package has been successfully moved by the system. * @@ -7739,6 +7748,7 @@ public abstract class PackageManager { case DELETE_FAILED_OWNER_BLOCKED: return "DELETE_FAILED_OWNER_BLOCKED"; case DELETE_FAILED_ABORTED: return "DELETE_FAILED_ABORTED"; case DELETE_FAILED_USED_SHARED_LIBRARY: return "DELETE_FAILED_USED_SHARED_LIBRARY"; + case DELETE_FAILED_APP_PINNED: return "DELETE_FAILED_APP_PINNED"; default: return Integer.toString(status); } } @@ -7753,6 +7763,7 @@ public abstract class PackageManager { case DELETE_FAILED_OWNER_BLOCKED: return PackageInstaller.STATUS_FAILURE_BLOCKED; case DELETE_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED; case DELETE_FAILED_USED_SHARED_LIBRARY: return PackageInstaller.STATUS_FAILURE_CONFLICT; + case DELETE_FAILED_APP_PINNED: return PackageInstaller.STATUS_FAILURE_BLOCKED; default: return PackageInstaller.STATUS_FAILURE; } } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index d2c74e963689..4afe4b3d126b 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -766,6 +766,26 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } /** + * Checks if the specified user has enrollments in any of the specified sensors. + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public boolean hasEnrolledTemplatesForAnySensor(int userId, + @NonNull List<FingerprintSensorPropertiesInternal> sensors) { + if (mService == null) { + Slog.w(TAG, "hasEnrolledTemplatesForAnySensor: no fingerprint service"); + return false; + } + + try { + return mService.hasEnrolledTemplatesForAnySensor(userId, sensors, + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) @@ -778,7 +798,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing try { mService.setUdfpsOverlayController(controller); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -795,7 +815,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing try { mService.onPointerDown(sensorId, x, y, minor, major); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -812,7 +832,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing try { mService.onPointerUp(sensorId); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -885,9 +905,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } return mService.getSensorPropertiesInternal(mContext.getOpPackageName()); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } - return new ArrayList<>(); } /** diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 547de9d3c86a..5b14ef796616 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -100,6 +100,9 @@ interface IFingerprintService { // Determine if a user has at least one enrolled fingerprint boolean hasEnrolledFingerprints(int userId, String opPackageName); + // Determine if a user has at least one enrolled fingerprint in any of the specified sensors + boolean hasEnrolledTemplatesForAnySensor(int userId, in List<FingerprintSensorPropertiesInternal> sensors, String opPackageName); + // Return the LockoutTracker status for the specified user int getLockoutModeForUser(int userId); diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchResultsTest.java b/core/java/android/net/vcn/IVcnManagementService.aidl index acbf11ae8669..af06906ca2e9 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchResultsTest.java +++ b/core/java/android/net/vcn/IVcnManagementService.aidl @@ -1,11 +1,11 @@ /* - * Copyright 2020 The Android Open Source Project + * Copyright 2020, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -14,17 +14,10 @@ * limitations under the License. */ -package android.app.appsearch; +package android.net.vcn; -import static org.testng.Assert.expectThrows; - -import org.junit.Test; - -public class SearchResultsTest { - @Test - public void buildSearchSpecWithoutTermMatchType() { - expectThrows(RuntimeException.class, () -> new SearchSpec.Builder() - .setSchemaTypes("testSchemaType") - .build()); - } +/** + * @hide + */ +interface IVcnManagementService { } diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java new file mode 100644 index 000000000000..d563b0350187 --- /dev/null +++ b/core/java/android/net/vcn/VcnManager.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net.vcn; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.annotation.SystemService; +import android.content.Context; + +/** + * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks + * + * @hide + */ +@SystemService(Context.VCN_MANAGEMENT_SERVICE) +public final class VcnManager { + @NonNull private static final String TAG = VcnManager.class.getSimpleName(); + + @NonNull private final Context mContext; + @NonNull private final IVcnManagementService mService; + + /** + * Construct an instance of VcnManager within an application context. + * + * @param ctx the application context for this manager + * @param service the VcnManagementService binder backing this manager + * + * @hide + */ + public VcnManager(@NonNull Context ctx, @NonNull IVcnManagementService service) { + mContext = requireNonNull(ctx, "missing context"); + mService = requireNonNull(service, "missing service"); + } +} diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index e4ba87c14626..f08756a015b2 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -218,6 +218,7 @@ public final class KeymasterDefs { public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58; public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59; public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66; + public static final int KM_ERROR_HARDWARE_TYPE_UNAVAILABLE = -68; public static final int KM_ERROR_DEVICE_LOCKED = -72; public static final int KM_ERROR_UNIMPLEMENTED = -100; public static final int KM_ERROR_VERSION_MISMATCH = -101; @@ -265,6 +266,8 @@ public final class KeymasterDefs { sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH, "Invalid MAC or authentication tag length"); sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids"); + sErrorCodeToString.put(KM_ERROR_HARDWARE_TYPE_UNAVAILABLE, "Requested security level " + + "(likely Strongbox) is not available."); sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked"); sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented"); sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error"); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 11423e6de93e..924fc6d6dca0 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -153,11 +153,10 @@ interface IWindowManager * WindowlessWindowManager). * * @param client an IWindow used for window-level communication (ime, finish draw, etc.). - * @param windowType used by WM to determine the z-order. This is the same as the window type - * used in {@link WindowManager.LayoutParams}. + * @param shellRootLayer The container's layer. See WindowManager#ShellRootLayer. * @return a SurfaceControl to add things to. */ - SurfaceControl addShellRoot(int displayId, IWindow client, int windowType); + SurfaceControl addShellRoot(int displayId, IWindow client, int shellRootLayer); /** * Sets the window token sent to accessibility for a particular shell root. The @@ -165,7 +164,7 @@ interface IWindowManager * * @param target The IWindow that accessibility service interfaces with. */ - void setShellRootAccessibilityWindow(int displayId, int windowType, IWindow target); + void setShellRootAccessibilityWindow(int displayId, int shellRootLayer, IWindow target); /** * Like overridePendingAppTransitionMultiThumb, but uses a future to supply the specs. This is diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 3a15aa26e357..d37edaa633a5 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -135,6 +135,8 @@ public final class SurfaceControl implements Parcelable { int blurRadius); private static native void nativeSetLayerStack(long transactionObj, long nativeObject, int layerStack); + private static native void nativeSetBlurRegions(long transactionObj, long nativeObj, + float[][] regions, int length); private static native boolean nativeClearContentFrameStats(long nativeObject); private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats); @@ -654,6 +656,7 @@ public final class SurfaceControl implements Parcelable { private final float mFrameScale; private final boolean mCaptureSecureLayers; private final boolean mAllowProtected; + private final long mUid; private CaptureArgs(Builder<? extends Builder<?>> builder) { mPixelFormat = builder.mPixelFormat; @@ -661,6 +664,7 @@ public final class SurfaceControl implements Parcelable { mFrameScale = builder.mFrameScale; mCaptureSecureLayers = builder.mCaptureSecureLayers; mAllowProtected = builder.mAllowProtected; + mUid = builder.mUid; } /** @@ -674,6 +678,7 @@ public final class SurfaceControl implements Parcelable { private float mFrameScale = 1; private boolean mCaptureSecureLayers; private boolean mAllowProtected; + private long mUid = -1; /** * The desired pixel format of the returned buffer. @@ -723,6 +728,15 @@ public final class SurfaceControl implements Parcelable { } /** + * Set the uid of the content that should be screenshot. The code will skip any surfaces + * that don't belong to the specified uid. + */ + public T setUid(long uid) { + mUid = uid; + return getThis(); + } + + /** * Each sub class should return itself to allow the builder to chain properly */ abstract T getThis(); @@ -2942,6 +2956,21 @@ public final class SurfaceControl implements Parcelable { } /** + * Specify what regions should be blurred on the {@link SurfaceControl}. + * + * @param sc SurfaceControl. + * @param regions List of regions that will have blurs. + * @return itself. + * @see BlurRegion#toFloatArray() + * @hide + */ + public Transaction setBlurRegions(SurfaceControl sc, float[][] regions) { + checkPreconditions(sc); + nativeSetBlurRegions(mNativeObject, sc.mNativeObject, regions, regions.length); + return this; + } + + /** * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O) @@ -3470,4 +3499,64 @@ public final class SurfaceControl implements Parcelable { public static Transaction getGlobalTransaction() { return sGlobalTransaction; } + + /** + * Wrapper for sending blur data to SurfaceFlinger. + * @hide + */ + public static final class BlurRegion { + public int blurRadius; + public float cornerRadiusTL; + public float cornerRadiusTR; + public float cornerRadiusBL; + public float cornerRadiusBR; + public float alpha = 1; + public boolean visible = true; + public final Rect rect = new Rect(); + + private final float[] mFloatArray = new float[10]; + + public BlurRegion() { + } + + public BlurRegion(BlurRegion other) { + rect.set(other.rect); + blurRadius = other.blurRadius; + alpha = other.alpha; + cornerRadiusTL = other.cornerRadiusTL; + cornerRadiusTR = other.cornerRadiusTR; + cornerRadiusBL = other.cornerRadiusBL; + cornerRadiusBR = other.cornerRadiusBR; + } + + /** + * Serializes this class into a float array that's more JNI friendly. + */ + public float[] toFloatArray() { + mFloatArray[0] = blurRadius; + mFloatArray[1] = alpha; + mFloatArray[2] = rect.left; + mFloatArray[3] = rect.top; + mFloatArray[4] = rect.right; + mFloatArray[5] = rect.bottom; + mFloatArray[6] = cornerRadiusTL; + mFloatArray[7] = cornerRadiusTR; + mFloatArray[8] = cornerRadiusBL; + mFloatArray[9] = cornerRadiusBR; + return mFloatArray; + } + + @Override + public String toString() { + return "BlurRegion{" + + "blurRadius=" + blurRadius + + ", corners={" + cornerRadiusTL + + "," + cornerRadiusTR + + "," + cornerRadiusBL + + "," + cornerRadiusBR + + "}, alpha=" + alpha + + ", rect=" + rect + + "}"; + } + } } diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java index bce78b572360..b10370aa5d4c 100644 --- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java +++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java @@ -38,6 +38,7 @@ public class SyncRtSurfaceTransactionApplier { public static final int FLAG_CORNER_RADIUS = 1 << 4; public static final int FLAG_BACKGROUND_BLUR_RADIUS = 1 << 5; public static final int FLAG_VISIBILITY = 1 << 6; + public static final int FLAG_TRANSACTION = 1 << 7; private SurfaceControl mTargetSc; private final ViewRootImpl mTargetViewRootImpl; @@ -93,6 +94,10 @@ public class SyncRtSurfaceTransactionApplier { } public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) { + if ((params.flags & FLAG_TRANSACTION) != 0) { + t.merge(params.mergeTransaction); + } + if ((params.flags & FLAG_MATRIX) != 0) { t.setMatrix(params.surface, params.matrix, tmpFloat9); } @@ -161,6 +166,7 @@ public class SyncRtSurfaceTransactionApplier { Rect windowCrop; int layer; boolean visible; + Transaction mergeTransaction; /** * @param surface The surface to modify. @@ -240,17 +246,28 @@ public class SyncRtSurfaceTransactionApplier { } /** + * @param mergeTransaction The transaction to apply to the surface. Note this is applied + * first before all the other properties. + * @return this Builder + */ + public Builder withMergeTransaction(Transaction mergeTransaction) { + this.mergeTransaction = mergeTransaction; + flags |= FLAG_TRANSACTION; + return this; + } + + /** * @return a new SurfaceParams instance */ public SurfaceParams build() { return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer, - cornerRadius, backgroundBlurRadius, visible); + cornerRadius, backgroundBlurRadius, visible, mergeTransaction); } } private SurfaceParams(SurfaceControl surface, int params, float alpha, Matrix matrix, - Rect windowCrop, int layer, float cornerRadius, int backgroundBlurRadius, - boolean visible) { + Rect windowCrop, int layer, float cornerRadius, + int backgroundBlurRadius, boolean visible, Transaction mergeTransaction) { this.flags = params; this.surface = surface; this.alpha = alpha; @@ -260,6 +277,7 @@ public class SyncRtSurfaceTransactionApplier { this.cornerRadius = cornerRadius; this.backgroundBlurRadius = backgroundBlurRadius; this.visible = visible; + this.mergeTransaction = mergeTransaction; } private final int flags; @@ -286,5 +304,7 @@ public class SyncRtSurfaceTransactionApplier { public final int layer; public final boolean visible; + + public final Transaction mergeTransaction; } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 14ba699933f0..0e8cd546a2ae 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -187,6 +187,7 @@ import android.window.ClientWindowFrames; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.graphics.drawable.BackgroundBlurDrawable; import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.os.IResultReceiver; import com.android.internal.os.SomeArgs; @@ -654,6 +655,9 @@ public final class ViewRootImpl implements ViewParent, private ScrollCaptureConnection mScrollCaptureConnection; + private final BackgroundBlurDrawable.Aggregator mBlurRegionAggregator = + new BackgroundBlurDrawable.Aggregator(this); + /** * @return {@link ImeFocusController} for this instance. */ @@ -3819,6 +3823,8 @@ public final class ViewRootImpl implements ViewParent, private HardwareRenderer.FrameCompleteCallback createFrameCompleteCallback(Handler handler, boolean reportNextDraw, ArrayList<Runnable> commitCallbacks) { return frameNr -> { + mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frameNr); + // Use a new transaction here since mRtBLASTSyncTransaction can only be accessed by // the render thread and mSurfaceChangedTransaction can only be accessed by the UI // thread. The temporary transaction is used so mRtBLASTSyncTransaction can be merged @@ -3849,7 +3855,8 @@ public final class ViewRootImpl implements ViewParent, .captureFrameCommitCallbacks(); final boolean needFrameCompleteCallback = mNextDrawUseBLASTSyncTransaction || mReportNextDraw - || (commitCallbacks != null && commitCallbacks.size() > 0); + || (commitCallbacks != null && commitCallbacks.size() > 0) + || mBlurRegionAggregator.hasRegions(); if (needFrameCompleteCallback) { mAttachInfo.mThreadedRenderer.setFrameCompleteCallback( createFrameCompleteCallback(mAttachInfo.mHandler, mReportNextDraw, @@ -9915,6 +9922,36 @@ public final class ViewRootImpl implements ViewParent, } } + /** + * Sends a list of blur regions to SurfaceFlinger, tagged with a frame. + * + * @param regionCopy List of regions + * @param frameNumber Frame where it should be applied (or current when using BLAST) + */ + public void dispatchBlurRegions(float[][] regionCopy, long frameNumber) { + final SurfaceControl surfaceControl = getSurfaceControl(); + if (!surfaceControl.isValid()) { + return; + } + if (useBLAST()) { + synchronized (getBlastTransactionLock()) { + getBLASTSyncTransaction().setBlurRegions(surfaceControl, regionCopy); + } + } else { + SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + transaction.setBlurRegions(surfaceControl, regionCopy); + transaction.deferTransactionUntil(surfaceControl, getSurfaceControl(), frameNumber); + transaction.apply(); + } + } + + /** + * Creates a background blur drawable for the backing {@link Surface}. + */ + public BackgroundBlurDrawable createBackgroundBlurDrawable() { + return mBlurRegionAggregator.createBackgroundBlurDrawable(mContext); + } + SurfaceControl.Transaction getBLASTSyncTransaction() { return mRtBLASTSyncTransaction; } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index f85489871730..f7578abfe4f0 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -133,6 +133,22 @@ public interface WindowManager extends ViewManager { /** @hide */ String INPUT_CONSUMER_RECENTS_ANIMATION = "recents_animation_input_consumer"; + /** @hide */ + int SHELL_ROOT_LAYER_DIVIDER = 0; + /** @hide */ + int SHELL_ROOT_LAYER_PIP = 1; + + /** + * Declares the layer the shell root will belong to. This is for z-ordering. + * @hide + */ + @IntDef(prefix = { "SHELL_ROOT_LAYER_" }, value = { + SHELL_ROOT_LAYER_DIVIDER, + SHELL_ROOT_LAYER_PIP + }) + @Retention(RetentionPolicy.SOURCE) + @interface ShellRootLayer {} + /** * Not set up for a transition. * @hide diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 093dfb4e196e..62b1b1f8cf53 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -502,7 +502,10 @@ public class BaseInputConnection implements InputConnection { * The default implementation returns the given amount of text from the * current cursor position in the buffer. */ - public CharSequence getTextBeforeCursor(int length, int flags) { + @Nullable + public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) { + if (length < 0) return null; + final Editable content = getEditable(); if (content == null) return null; @@ -558,7 +561,10 @@ public class BaseInputConnection implements InputConnection { * The default implementation returns the given amount of text from the * current cursor position in the buffer. */ - public CharSequence getTextAfterCursor(int length, int flags) { + @Nullable + public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) { + if (length < 0) return null; + final Editable content = getEditable(); if (content == null) return null; @@ -594,6 +600,8 @@ public class BaseInputConnection implements InputConnection { @Nullable public SurroundingText getSurroundingText( @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) { + if (beforeLength < 0 || afterLength < 0) return null; + final Editable content = getEditable(); if (content == null) return null; diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index c7acd298cd20..0fe47b7828d9 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -27,6 +27,8 @@ import android.text.TextUtils; import android.view.KeyCharacterMap; import android.view.KeyEvent; +import com.android.internal.util.Preconditions; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -186,13 +188,15 @@ public interface InputConnection { * the current line, and specifically do not return 0 characters unless * the cursor is really at the start of the text.</p> * - * @param n The expected length of the text. + * @param n The expected length of the text. This must be non-negative. * @param flags Supplies additional options controlling how the text is * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}. * @return the text before the cursor position; the length of the * returned text might be less than <var>n</var>. + * @throws IllegalArgumentException if {@code n} is negative. */ - CharSequence getTextBeforeCursor(int n, int flags); + @Nullable + CharSequence getTextBeforeCursor(@IntRange(from = 0) int n, int flags); /** * Get <var>n</var> characters of text after the current cursor @@ -228,14 +232,16 @@ public interface InputConnection { * the current line, and specifically do not return 0 characters unless * the cursor is really at the end of the text.</p> * - * @param n The expected length of the text. + * @param n The expected length of the text. This must be non-negative. * @param flags Supplies additional options controlling how the text is * returned. May be either 0 or {@link #GET_TEXT_WITH_STYLES}. * * @return the text after the cursor position; the length of the * returned text might be less than <var>n</var>. + * @throws IllegalArgumentException if {@code n} is negative. */ - CharSequence getTextAfterCursor(int n, int flags); + @Nullable + CharSequence getTextAfterCursor(@IntRange(from = 0) int n, int flags); /** * Gets the selected text, if any. @@ -307,11 +313,15 @@ public interface InputConnection { * editor can't comply with the request for some reason, or the application does not implement * this method. The length of the returned text might be less than the sum of * <var>beforeLength</var> and <var>afterLength</var> . + * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative. */ @Nullable default SurroundingText getSurroundingText( @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, @GetTextType int flags) { + Preconditions.checkArgumentNonnegative(beforeLength); + Preconditions.checkArgumentNonnegative(afterLength); + CharSequence textBeforeCursor = getTextBeforeCursor(beforeLength, flags); if (textBeforeCursor == null) { textBeforeCursor = ""; diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index ec7fa60a62ba..77956d15e399 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -16,11 +16,14 @@ package android.view.inputmethod; +import android.annotation.IntRange; import android.annotation.Nullable; import android.os.Bundle; import android.os.Handler; import android.view.KeyEvent; +import com.android.internal.util.Preconditions; + /** * <p>Wrapper class for proxying calls to another InputConnection. Subclass and have fun! */ @@ -74,18 +77,24 @@ public class InputConnectionWrapper implements InputConnection { /** * {@inheritDoc} * @throws NullPointerException if the target is {@code null}. + * @throws IllegalArgumentException if {@code length} is negative. */ + @Nullable @Override - public CharSequence getTextBeforeCursor(int n, int flags) { + public CharSequence getTextBeforeCursor(@IntRange(from = 0) int n, int flags) { + Preconditions.checkArgumentNonnegative(n); return mTarget.getTextBeforeCursor(n, flags); } /** * {@inheritDoc} * @throws NullPointerException if the target is {@code null}. + * @throws IllegalArgumentException if {@code length} is negative. */ + @Nullable @Override - public CharSequence getTextAfterCursor(int n, int flags) { + public CharSequence getTextAfterCursor(@IntRange(from = 0) int n, int flags) { + Preconditions.checkArgumentNonnegative(n); return mTarget.getTextAfterCursor(n, flags); } @@ -101,10 +110,13 @@ public class InputConnectionWrapper implements InputConnection { /** * {@inheritDoc} * @throws NullPointerException if the target is {@code null}. + * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative. */ @Nullable @Override public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) { + Preconditions.checkArgumentNonnegative(beforeLength); + Preconditions.checkArgumentNonnegative(afterLength); return mTarget.getSurroundingText(beforeLength, afterLength, flags); } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index cb4f285c7772..2e3d560ccb56 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -1090,7 +1090,7 @@ public class ChooserActivity extends ResolverActivity implements public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); ViewPager viewPager = findViewById(R.id.profile_pager); - if (shouldShowTabs() && viewPager.isLayoutRtl()) { + if (viewPager.isLayoutRtl()) { mMultiProfilePagerAdapter.setupViewPager(viewPager); } diff --git a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java new file mode 100644 index 000000000000..6ea9e665c009 --- /dev/null +++ b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.graphics.drawable; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RenderNode; +import android.graphics.drawable.Drawable; +import android.util.ArrayMap; +import android.util.Log; +import android.view.SurfaceControl; +import android.view.ViewRootImpl; + +import com.android.internal.R; + +/** + * A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state + * to SurfaceFlinger. + */ +public final class BackgroundBlurDrawable extends Drawable { + + private static final String TAG = BackgroundBlurDrawable.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private final Aggregator mAggregator; + private final RenderNode mRenderNode; + private final Paint mPaint = new Paint(); + private final Path mRectPath = new Path(); + private final float[] mTmpRadii = new float[8]; + private final SurfaceControl.BlurRegion mBlurRegion = new SurfaceControl.BlurRegion(); + + // This will be called from a thread pool. + private final RenderNode.PositionUpdateListener mPositionUpdateListener = + new RenderNode.PositionUpdateListener() { + @Override + public void positionChanged(long frameNumber, int left, int top, int right, + int bottom) { + synchronized (mAggregator) { + mBlurRegion.rect.set(left, top, right, bottom); + mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); + } + } + + @Override + public void positionLost(long frameNumber) { + synchronized (mAggregator) { + mBlurRegion.rect.setEmpty(); + mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); + } + } + }; + + private BackgroundBlurDrawable(Aggregator aggregator) { + mAggregator = aggregator; + mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + mRenderNode = new RenderNode("BackgroundBlurDrawable"); + mRenderNode.addPositionUpdateListener(mPositionUpdateListener); + } + + @Override + public void draw(@NonNull Canvas canvas) { + if (mRectPath.isEmpty() || !isVisible() || getAlpha() == 0) { + return; + } + canvas.drawPath(mRectPath, mPaint); + canvas.drawRenderNode(mRenderNode); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + boolean changed = super.setVisible(visible, restart); + if (changed) { + mBlurRegion.visible = visible; + } + return changed; + } + + @Override + public void setAlpha(int alpha) { + mBlurRegion.alpha = alpha / 255f; + invalidateSelf(); + } + + /** + * Blur radius in pixels. + */ + public void setBlurRadius(int blurRadius) { + mBlurRegion.blurRadius = blurRadius; + invalidateSelf(); + } + + /** + * Sets the corner radius, in degrees. + */ + public void setCornerRadius(float cornerRadius) { + setCornerRadius(cornerRadius, cornerRadius, cornerRadius, cornerRadius); + } + + /** + * Sets the corner radius in degrees. + * @param cornerRadiusTL top left radius. + * @param cornerRadiusTR top right radius. + * @param cornerRadiusBL bottom left radius. + * @param cornerRadiusBR bottom right radius. + */ + public void setCornerRadius(float cornerRadiusTL, float cornerRadiusTR, float cornerRadiusBL, + float cornerRadiusBR) { + synchronized (mAggregator) { + mBlurRegion.cornerRadiusTL = cornerRadiusTL; + mBlurRegion.cornerRadiusTR = cornerRadiusTR; + mBlurRegion.cornerRadiusBL = cornerRadiusBL; + mBlurRegion.cornerRadiusBR = cornerRadiusBR; + } + updatePath(); + invalidateSelf(); + } + + @Override + public void setBounds(int left, int top, int right, int bottom) { + super.setBounds(left, top, right, bottom); + mRenderNode.setPosition(left, top, right, bottom); + updatePath(); + } + + private void updatePath() { + synchronized (mAggregator) { + mTmpRadii[0] = mTmpRadii[1] = mBlurRegion.cornerRadiusTL; + mTmpRadii[2] = mTmpRadii[3] = mBlurRegion.cornerRadiusTR; + mTmpRadii[4] = mTmpRadii[5] = mBlurRegion.cornerRadiusBL; + mTmpRadii[6] = mTmpRadii[7] = mBlurRegion.cornerRadiusBR; + } + mRectPath.reset(); + if (getAlpha() == 0 || !isVisible()) { + return; + } + Rect bounds = getBounds(); + mRectPath.addRoundRect(bounds.left, bounds.top, bounds.right, bounds.bottom, mTmpRadii, + Path.Direction.CW); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + throw new IllegalArgumentException("not implemented"); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + /** + * Responsible for keeping track of all blur regions of a {@link ViewRootImpl} and posting a + * message when it's time to propagate them. + */ + public static final class Aggregator { + + private final ArrayMap<BackgroundBlurDrawable, SurfaceControl.BlurRegion> mBlurRegions = + new ArrayMap<>(); + private final ViewRootImpl mViewRoot; + private float[][] mTmpBlurRegionsArray; + private boolean mNeedsUpdate; + + public Aggregator(ViewRootImpl viewRoot) { + mViewRoot = viewRoot; + } + + /** + * Creates a blur region with default radius. + */ + public BackgroundBlurDrawable createBackgroundBlurDrawable(Context context) { + BackgroundBlurDrawable drawable = new BackgroundBlurDrawable(this); + drawable.setBlurRadius(context.getResources().getDimensionPixelSize( + R.dimen.default_background_blur_radius)); + return drawable; + } + + /** + * Called from RenderThread only, already locked. + * @param drawable + * @param blurRegion + */ + void onBlurRegionUpdated(BackgroundBlurDrawable drawable, + SurfaceControl.BlurRegion blurRegion) { + if (blurRegion.rect.isEmpty() || blurRegion.alpha == 0 || blurRegion.blurRadius == 0 + || !blurRegion.visible) { + mBlurRegions.remove(drawable); + mNeedsUpdate = true; + if (DEBUG) { + Log.d(TAG, "Remove " + blurRegion); + } + } else { + mBlurRegions.put(drawable, blurRegion); + mNeedsUpdate = true; + if (DEBUG) { + Log.d(TAG, "Update " + blurRegion); + } + } + } + + /** + * If there are any blur regions visible on the screen at the moment. + */ + public boolean hasRegions() { + return mBlurRegions.size() > 0; + } + + /** + * Dispatch blur updates, if there were any. + * @param frameNumber Frame where the update should happen. + */ + public void dispatchBlurTransactionIfNeeded(long frameNumber) { + synchronized (this) { + if (!mNeedsUpdate) { + return; + } + mNeedsUpdate = false; + + if (mTmpBlurRegionsArray == null + || mTmpBlurRegionsArray.length != mBlurRegions.size()) { + mTmpBlurRegionsArray = new float[mBlurRegions.size()][]; + } + if (DEBUG) { + Log.d(TAG, "onBlurRegionUpdated will dispatch " + mTmpBlurRegionsArray.length + + " regions for frame " + frameNumber); + } + for (int i = 0; i < mTmpBlurRegionsArray.length; i++) { + mTmpBlurRegionsArray[i] = mBlurRegions.valueAt(i).toFloatArray(); + } + + mViewRoot.dispatchBlurRegions(mTmpBlurRegionsArray, frameNumber); + } + } + } +} diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index f086dd79758b..e05aa8351681 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -17,6 +17,7 @@ package com.android.internal.view; import android.annotation.AnyThread; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.inputmethodservice.AbstractInputMethodService; @@ -106,9 +107,13 @@ public class InputConnectionWrapper implements InputConnection { return null; } + /** + * See {@link InputConnection#getTextAfterCursor(int, int)}. + */ + @Nullable @AnyThread - public CharSequence getTextAfterCursor(int length, int flags) { - if (mCancellationGroup.isCanceled()) { + public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) { + if (length < 0 || mCancellationGroup.isCanceled()) { return null; } @@ -122,9 +127,13 @@ public class InputConnectionWrapper implements InputConnection { return getResultOrNull(value, "getTextAfterCursor()"); } + /** + * See {@link InputConnection#getTextBeforeCursor(int, int)}. + */ + @Nullable @AnyThread - public CharSequence getTextBeforeCursor(int length, int flags) { - if (mCancellationGroup.isCanceled()) { + public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) { + if (length < 0 || mCancellationGroup.isCanceled()) { return null; } @@ -171,10 +180,12 @@ public class InputConnectionWrapper implements InputConnection { * not support this protocol. */ @AnyThread - public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) { - if (mCancellationGroup.isCanceled()) { + public SurroundingText getSurroundingText( + @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) { + if (beforeLength < 0 || afterLength < 0 || mCancellationGroup.isCanceled()) { return null; } + if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) { // This method is not implemented. return null; diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 661bbed8abcb..1123f20c3e7e 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -329,7 +329,7 @@ cc_library_shared { ], shared_libs: [ // libbinder needs to be shared since it has global state - // (e.g. gDefaultServiceManager) + // (e.g. gDefaultServiceManager) "libbinder", "libhidlbase", // libhwbinder is in here ], diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 94151b522c9c..f1ec85a8570f 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -40,6 +40,7 @@ #include <private/gui/ComposerService.h> #include <stdio.h> #include <system/graphics.h> +#include <ui/BlurRegion.h> #include <ui/ConfigStoreTypes.h> #include <ui/DeviceProductInfo.h> #include <ui/DisplayConfig.h> @@ -111,6 +112,7 @@ static struct { jfieldID frameScale; jfieldID captureSecureLayers; jfieldID allowProtected; + jfieldID uid; } gCaptureArgsClassInfo; static struct { @@ -371,6 +373,7 @@ static void getCaptureArgs(JNIEnv* env, jobject captureArgsObject, CaptureArgs& env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.captureSecureLayers); captureArgs.allowProtected = env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.allowProtected); + captureArgs.uid = env->GetLongField(captureArgsObject, gCaptureArgsClassInfo.uid); } static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env, @@ -520,6 +523,43 @@ static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, j transaction->setGeometry(ctrl, source, dst, orientation); } +static void nativeSetBlurRegions(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong nativeObject, jobjectArray regions, jint regionsLength) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject); + + std::vector<BlurRegion> blurRegionVector; + const int size = regionsLength; + float region[10]; + for (int i = 0; i < size; i++) { + jfloatArray regionArray = (jfloatArray)env->GetObjectArrayElement(regions, i); + env->GetFloatArrayRegion(regionArray, 0, 10, region); + float blurRadius = region[0]; + float alpha = region[1]; + float left = region[2]; + float top = region[3]; + float right = region[4]; + float bottom = region[5]; + float cornerRadiusTL = region[6]; + float cornerRadiusTR = region[7]; + float cornerRadiusBL = region[8]; + float cornerRadiusBR = region[9]; + + blurRegionVector.push_back(BlurRegion{.blurRadius = static_cast<uint32_t>(blurRadius), + .cornerRadiusTL = cornerRadiusTL, + .cornerRadiusTR = cornerRadiusTR, + .cornerRadiusBL = cornerRadiusBL, + .cornerRadiusBR = cornerRadiusBR, + .alpha = alpha, + .left = static_cast<int>(left), + .top = static_cast<int>(top), + .right = static_cast<int>(right), + .bottom = static_cast<int>(bottom)}); + } + + transaction->setBlurRegions(ctrl, blurRegionVector); +} + static void nativeSetSize(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jint w, jint h) { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); @@ -1624,6 +1664,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetBackgroundBlurRadius }, {"nativeSetLayerStack", "(JJI)V", (void*)nativeSetLayerStack }, + {"nativeSetBlurRegions", "(JJ[[FI)V", + (void*)nativeSetBlurRegions }, {"nativeSetShadowRadius", "(JJF)V", (void*)nativeSetShadowRadius }, {"nativeSetFrameRate", "(JJFI)V", @@ -1895,6 +1937,7 @@ int register_android_view_SurfaceControl(JNIEnv* env) GetFieldIDOrDie(env, captureArgsClazz, "mCaptureSecureLayers", "Z"); gCaptureArgsClassInfo.allowProtected = GetFieldIDOrDie(env, captureArgsClazz, "mAllowProtected", "Z"); + gCaptureArgsClassInfo.uid = GetFieldIDOrDie(env, captureArgsClazz, "mUid", "J"); jclass displayCaptureArgsClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayCaptureArgs"); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3248cfedb523..4df7d589d707 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -669,10 +669,6 @@ <!-- For tether entitlement recheck--> <protected-broadcast android:name="com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM" /> - - <!-- Made protected in S (was added in R) --> - <protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" /> - <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> <!-- ====================================================================== --> diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml index ffb9603529c2..bf66e69b52d3 100644 --- a/core/res/res/layout/notification_material_action_list.xml +++ b/core/res/res/layout/notification_material_action_list.xml @@ -44,6 +44,14 @@ </com.android.internal.widget.NotificationActionListLayout> <ImageView + android:id="@+id/snooze_button" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_gravity="center_vertical|end" + android:visibility="gone" + android:scaleType="centerInside" + /> + <ImageView android:id="@+id/bubble_button" android:layout_width="48dp" android:layout_height="48dp" diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index bebece81c7f4..3b92f87cfc1c 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -1129,10 +1129,8 @@ <string name="capital_off" msgid="7443704171014626777">"বন্ধ আছে"</string> <string name="checked" msgid="9179896827054513119">"টিকচিহ্ন দেওয়া আছে"</string> <string name="not_checked" msgid="7972320087569023342">"টিকচিহ্ন দেওয়া নেই"</string> - <!-- no translation found for selected (6614607926197755875) --> - <skip /> - <!-- no translation found for not_selected (410652016565864475) --> - <skip /> + <string name="selected" msgid="6614607926197755875">"বেছে নেওয়া হয়েছে"</string> + <string name="not_selected" msgid="410652016565864475">"বেছে নেওয়া হয়নি"</string> <string name="whichApplication" msgid="5432266899591255759">"এটি ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string> <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string> <string name="whichApplicationLabel" msgid="7852182961472531728">"ক্রিয়াকলাপ সম্পূর্ণ করুন"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 8c9c674372f8..df447afca7eb 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -314,7 +314,7 @@ <string name="permgrouplab_calllog" msgid="7926834372073550288">"Registres de trucades"</string> <string name="permgroupdesc_calllog" msgid="2026996642917801803">"llegir i editar el registre de trucades del telèfon"</string> <string name="permgrouplab_phone" msgid="570318944091926620">"Telèfon"</string> - <string name="permgroupdesc_phone" msgid="270048070781478204">"fer i gestionar trucades"</string> + <string name="permgroupdesc_phone" msgid="270048070781478204">"fer i gestionar trucades telefòniques"</string> <string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensors corporals"</string> <string name="permgroupdesc_sensors" msgid="2610631290633747752">"accedir a les dades del sensor sobre els signes vitals"</string> <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperar el contingut de la finestra"</string> @@ -343,8 +343,8 @@ <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permet que l\'aplicació suprimeixi les dreceres de la pantalla d\'inici sense la intervenció de l\'usuari."</string> <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"desviació de les trucades sortints"</string> <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Permet que l\'aplicació vegi el número que s\'està marcant durant una trucada sortint, amb l\'opció de redirigir la trucada a un altre número o bé de cancel·lar-la completament."</string> - <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"contestar a trucades"</string> - <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Permet que l\'aplicació contesti a una trucada entrant."</string> + <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"contestar a trucades telefòniques"</string> + <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Permet que l\'aplicació contesti a una trucada telefònica entrant."</string> <string name="permlab_receiveSms" msgid="505961632050451881">"recepció de missatges de text (SMS)"</string> <string name="permdesc_receiveSms" msgid="1797345626687832285">"Permet que l\'aplicació rebi i processi missatges SMS. Això vol dir que l\'aplicació pot controlar o suprimir missatges que s\'han enviat al teu dispositiu sense mostrar-te\'ls."</string> <string name="permlab_receiveMms" msgid="4000650116674380275">"recepció de missatges de text (MMS)"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index d0fceb094355..393f229b75bb 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1129,10 +1129,8 @@ <string name="capital_off" msgid="7443704171014626777">"DESACTIVADO"</string> <string name="checked" msgid="9179896827054513119">"seleccionado"</string> <string name="not_checked" msgid="7972320087569023342">"no seleccionado"</string> - <!-- no translation found for selected (6614607926197755875) --> - <skip /> - <!-- no translation found for not_selected (410652016565864475) --> - <skip /> + <string name="selected" msgid="6614607926197755875">"seleccionado"</string> + <string name="not_selected" msgid="410652016565864475">"no seleccionado"</string> <string name="whichApplication" msgid="5432266899591255759">"Completar acción utilizando"</string> <string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string> <string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index d417c903b5a5..f7f6214cc2b2 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -940,7 +940,7 @@ <string name="double_tap_toast" msgid="7065519579174882778">"ટિપ: ઝૂમ વધારવા અને ઘટાડવા માટે બે વાર ટેપ કરો."</string> <string name="autofill_this_form" msgid="3187132440451621492">"સ્વતઃભરણ"</string> <string name="setup_autofill" msgid="5431369130866618567">"સ્વતઃભરણ સેટ કરો"</string> - <string name="autofill_window_title" msgid="4379134104008111961">"<xliff:g id="SERVICENAME">%1$s</xliff:g> સાથે આપમેળે ભરો"</string> + <string name="autofill_window_title" msgid="4379134104008111961">"<xliff:g id="SERVICENAME">%1$s</xliff:g> સાથે ઑટોમૅટિક રીતે ભરો"</string> <string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string> <string name="autofill_address_summary_name_format" msgid="3402882515222673691">"$1$2$3"</string> <string name="autofill_address_summary_separator" msgid="760522655085707045">", "</string> @@ -1129,10 +1129,8 @@ <string name="capital_off" msgid="7443704171014626777">"બંધ"</string> <string name="checked" msgid="9179896827054513119">"ચેક કર્યું"</string> <string name="not_checked" msgid="7972320087569023342">"ચેક કર્યું નથી"</string> - <!-- no translation found for selected (6614607926197755875) --> - <skip /> - <!-- no translation found for not_selected (410652016565864475) --> - <skip /> + <string name="selected" msgid="6614607926197755875">"પસંદ કરેલી"</string> + <string name="not_selected" msgid="410652016565864475">"પસંદ નહીં કરેલી"</string> <string name="whichApplication" msgid="5432266899591255759">"આના ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string> <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string> <string name="whichApplicationLabel" msgid="7852182961472531728">"ક્રિયા પૂર્ણ કરો"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 82a27a85a264..53c92af2da3f 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -1798,7 +1798,7 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ធ្វើបច្ចុប្បន្នភាពដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"លុបដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"យល់ព្រម"</string> - <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ដើម្បីបង្កើនកម្រិតថាមពលថ្ម កម្មវិធីសន្សំថ្ម៖\n\n• បើករចនាប័ទ្មងងឹត\n• បិទ ឬដាក់កំហិតលើសកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលជារូបភាពមួយចំនួន និងមុខងារផ្សេងទៀតដូចជា “Ok Google” ជាដើម\n\n"<annotation id="url">"ស្វែងយល់បន្ថែម"</annotation></string> + <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"ដើម្បីបង្កើនកម្រិតថាមពលថ្ម មុខងារសន្សំថ្ម៖\n\n• បើករចនាប័ទ្មងងឹត\n• បិទ ឬដាក់កំហិតលើសកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលជារូបភាពមួយចំនួន និងមុខងារផ្សេងទៀតដូចជា “Hey Google” ជាដើម\n\n"<annotation id="url">"ស្វែងយល់បន្ថែម"</annotation></string> <string name="battery_saver_description" msgid="6794188153647295212">"ដើម្បីបង្កើនកម្រិតថាមពលថ្ម មុខងារសន្សំថ្ម៖\n\n• បើករចនាប័ទ្មងងឹត\n• បិទ ឬដាក់កំហិតលើសកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលរូបភាពមួយចំនួន និងមុខងារផ្សេងទៀតដូចជា “Ok Google” ជាដើម"</string> <string name="data_saver_description" msgid="4995164271550590517">"ដើម្បីជួយកាត់បន្ថយការប្រើប្រាស់ទិន្នន័យ កម្មវិធីសន្សំសំចៃទិន្នន័យរារាំងកម្មវិធីមួយចំនួនមិនឲ្យបញ្ជូន ឬទទួលទិន្នន័យនៅផ្ទៃខាងក្រោយទេ។ កម្មវិធីដែលអ្នកកំពុងប្រើនាពេលបច្ចុប្បន្នអាចចូលប្រើប្រាស់ទិន្នន័យបាន ប៉ុន្តែអាចនឹងមិនញឹកញាប់ដូចមុនទេ។ ឧទាហរណ៍ រូបភាពមិនបង្ហាញទេ លុះត្រាតែអ្នកប៉ះរូបភាពទាំងនោះ។"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"បើកកម្មវិធីសន្សំសំចៃទិន្នន័យ?"</string> @@ -2007,9 +2007,9 @@ <string name="notification_feedback_indicator" msgid="663476517711323016">"ផ្ដល់មតិកែលម្អ"</string> <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ការជូនដំណឹងព័ត៌មានរបស់មុខងារទម្លាប់"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ថ្មអាចនឹងអស់ មុនពេលសាកថ្មធម្មតា"</string> - <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"បានបើកដំណើរការកម្មវិធីសន្សំថ្ម ដើម្បីបង្កើនកម្រិតថាមពលថ្ម"</string> + <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"បានបើកដំណើរការមុខងារសន្សំថ្ម ដើម្បីបង្កើនកម្រិតថាមពលថ្ម"</string> <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"មុខងារសន្សំថ្ម"</string> - <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"កម្មវិធីសន្សំថ្មត្រូវបានបិទ"</string> + <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"មុខងារសន្សំថ្មត្រូវបានបិទ"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"ទូរសព្ទមានកម្រិតថ្មគ្រប់គ្រាន់។ មុខងារផ្សេងៗមិនត្រូវបានរឹតបន្តឹងទៀតទេ។"</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"ថេប្លេតមានកម្រិតថ្មគ្រប់គ្រាន់។ មុខងារផ្សេងៗមិនត្រូវបានរឹតបន្តឹងទៀតទេ។"</string> <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"ឧបករណ៍មានកម្រិតថ្មគ្រប់គ្រាន់។ មុខងារផ្សេងៗមិនត្រូវបានរឹតបន្តឹងទៀតទេ។"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index dc8f237308c5..811973b205d5 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -1129,10 +1129,8 @@ <string name="capital_off" msgid="7443704171014626777">"ಆಫ್ ಮಾಡು"</string> <string name="checked" msgid="9179896827054513119">"ಪರಿಶೀಲಿಸಲಾಗಿದೆ"</string> <string name="not_checked" msgid="7972320087569023342">"ಪರಿಶೀಲಿಸಲಾಗಿಲ್ಲ"</string> - <!-- no translation found for selected (6614607926197755875) --> - <skip /> - <!-- no translation found for not_selected (410652016565864475) --> - <skip /> + <string name="selected" msgid="6614607926197755875">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string> + <string name="not_selected" msgid="410652016565864475">"ಆಯ್ಕೆಮಾಡಲಾಗಿಲ್ಲ"</string> <string name="whichApplication" msgid="5432266899591255759">"ಇದನ್ನು ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string> <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string> <string name="whichApplicationLabel" msgid="7852182961472531728">"ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 451821681f0b..b2e2c1df8226 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -2009,8 +2009,8 @@ <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарея кубаттоого чейин отуруп калышы мүмкүн"</string> <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Батареянын отуруп калбашы үчүн Батареяны үнөмдөгүч режими иштетилди"</string> <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Батареяны үнөмдөгүч"</string> - <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Батареяны үнөмдөгүч режими өчүрүлдү"</string> - <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Телефон жеткиликтүү кубатталды. Функцияны колдоно берсеңиз болот."</string> + <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Батареяны үнөмдөө режими өчүк"</string> + <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Телефондун кубаты жетиштүү. Функциялар мындан ары чектелбейт."</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Планшет жеткиликтүү кубатталды. Функцияны колдоно берсеңиз болот."</string> <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Түзмөк жеткиликтүү кубатталды. Функцияны колдоно берсеңиз болот."</string> <string name="mime_type_folder" msgid="2203536499348787650">"Папка"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index db5603c8a624..e39d46dfb6f7 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -1129,10 +1129,8 @@ <string name="capital_off" msgid="7443704171014626777">"बंद"</string> <string name="checked" msgid="9179896827054513119">"तपासले"</string> <string name="not_checked" msgid="7972320087569023342">"तपासले नाही"</string> - <!-- no translation found for selected (6614607926197755875) --> - <skip /> - <!-- no translation found for not_selected (410652016565864475) --> - <skip /> + <string name="selected" msgid="6614607926197755875">"निवडला"</string> + <string name="not_selected" msgid="410652016565864475">"निवडला नाही"</string> <string name="whichApplication" msgid="5432266899591255759">"याचा वापर करून क्रिया पूर्ण करा"</string> <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s वापरून क्रिया पूर्ण करा"</string> <string name="whichApplicationLabel" msgid="7852182961472531728">"क्रिया पूर्ण झाली"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index b5fd7b245555..915e158f3267 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -1129,10 +1129,8 @@ <string name="capital_off" msgid="7443704171014626777">"बन्द"</string> <string name="checked" msgid="9179896827054513119">"जाँच गरिएको"</string> <string name="not_checked" msgid="7972320087569023342">"जाँच गरिएको छैन"</string> - <!-- no translation found for selected (6614607926197755875) --> - <skip /> - <!-- no translation found for not_selected (410652016565864475) --> - <skip /> + <string name="selected" msgid="6614607926197755875">"चयन गरियो"</string> + <string name="not_selected" msgid="410652016565864475">"चयन गरिएन"</string> <string name="whichApplication" msgid="5432266899591255759">"प्रयोग गरेर कारबाही पुरा गर्नुहोस्"</string> <string name="whichApplicationNamed" msgid="6969946041713975681">"निम्न एपको प्रयोग गरी कारबाही पुरा गर्नुहोस्: %1$s"</string> <string name="whichApplicationLabel" msgid="7852182961472531728">"पूर्ण कारबाही"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 2df3c44f59f8..1dec3f977f00 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1129,10 +1129,8 @@ <string name="capital_off" msgid="7443704171014626777">"ਬੰਦ"</string> <string name="checked" msgid="9179896827054513119">"ਨਿਸ਼ਾਨਬੱਧ ਕੀਤਾ ਗਿਆ"</string> <string name="not_checked" msgid="7972320087569023342">"ਨਿਸ਼ਾਨਬੱਧ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string> - <!-- no translation found for selected (6614607926197755875) --> - <skip /> - <!-- no translation found for not_selected (410652016565864475) --> - <skip /> + <string name="selected" msgid="6614607926197755875">"ਚੁਣਿਆ ਗਿਆ"</string> + <string name="not_selected" msgid="410652016565864475">"ਨਹੀਂ ਚੁਣਿਆ ਗਿਆ"</string> <string name="whichApplication" msgid="5432266899591255759">"ਇਸਨੂੰ ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string> <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string> <string name="whichApplicationLabel" msgid="7852182961472531728">"ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 1247dc1c874d..2d56fb18d323 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -1129,10 +1129,8 @@ <string name="capital_off" msgid="7443704171014626777">"ఆఫ్"</string> <string name="checked" msgid="9179896827054513119">"ఎంచుకోబడింది"</string> <string name="not_checked" msgid="7972320087569023342">"ఎంచుకోలేదు"</string> - <!-- no translation found for selected (6614607926197755875) --> - <skip /> - <!-- no translation found for not_selected (410652016565864475) --> - <skip /> + <string name="selected" msgid="6614607926197755875">"ఎంచుకోబడింది"</string> + <string name="not_selected" msgid="410652016565864475">"ఎంచుకోబడలేదు"</string> <string name="whichApplication" msgid="5432266899591255759">"దీన్ని ఉపయోగించి చర్యను పూర్తి చేయండి"</string> <string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sను ఉపయోగించి చర్యను పూర్తి చేయి"</string> <string name="whichApplicationLabel" msgid="7852182961472531728">"చర్యను పూర్తి చేయి"</string> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 84e7d42f2ed9..544be54eb785 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -207,9 +207,6 @@ <!-- Default padding for dialogs. --> <dimen name="dialog_padding">16dp</dimen> - <!-- The horizontal margin of the content in the notification shade --> - <dimen name="notification_shade_content_margin_horizontal">16dp</dimen> - <!-- The margin on the start of the content view --> <dimen name="notification_content_margin_start">16dp</dimen> @@ -861,6 +858,7 @@ <dimen name="waterfall_display_right_edge_size">0px</dimen> <dimen name="waterfall_display_bottom_edge_size">0px</dimen> + <dimen name="default_background_blur_radius">100dp</dimen> <!-- The maximum height of a thumbnail in a ThumbnailTemplate. The image will be reduced to that height in case they are bigger. --> <dimen name="controls_thumbnail_image_max_height">140dp</dimen> <!-- The maximum width of a thumbnail in a ThumbnailTemplate. The image will be reduced to that width in case they are bigger.--> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a716875b068f..a294c9d1e537 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2864,7 +2864,6 @@ <java-symbol type="drawable" name="ic_collapse_bundle" /> <java-symbol type="dimen" name="notification_min_content_height" /> <java-symbol type="dimen" name="notification_header_shrink_min_width" /> - <java-symbol type="dimen" name="notification_shade_content_margin_horizontal" /> <java-symbol type="dimen" name="notification_content_margin_start" /> <java-symbol type="dimen" name="notification_content_margin_end" /> <java-symbol type="dimen" name="notification_reply_inset" /> @@ -3554,6 +3553,7 @@ <java-symbol type="id" name="clip_to_padding_tag" /> <java-symbol type="id" name="clip_children_tag" /> <java-symbol type="id" name="bubble_button" /> + <java-symbol type="id" name="snooze_button" /> <java-symbol type="dimen" name="bubble_visible_padding_end" /> <java-symbol type="dimen" name="bubble_gone_padding_end" /> <java-symbol type="dimen" name="text_size_body_2_material" /> @@ -4079,6 +4079,7 @@ <java-symbol type="integer" name="config_defaultBinderHeavyHitterAutoSamplerBatchSize" /> <java-symbol type="dimen" name="config_defaultBinderHeavyHitterAutoSamplerThreshold" /> + <java-symbol type="dimen" name="default_background_blur_radius" /> <java-symbol type="array" name="config_keep_warming_services" /> <java-symbol type="string" name="config_display_features" /> <java-symbol type="array" name="config_internalFoldedPhysicalDisplayIds" /> diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java index ac2d4b5c5f7d..2f2bef8bc5cc 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java @@ -30,7 +30,7 @@ public class AppSearchEmailTest { // Score and Property are mixed into the middle to make sure DocumentBuilder's // methods can be interleaved with EmailBuilder's methods. .setScore(1) - .setProperty("propertyKey", "propertyValue1", "propertyValue2") + .setPropertyString("propertyKey", "propertyValue1", "propertyValue2") .setSubject("subject") .setBody("EmailBody") .build(); diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java index 1f2c12bca028..9c29943dbef4 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java @@ -39,22 +39,22 @@ public class GenericDocumentTest { GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setTtlMillis(1L) - .setProperty("longKey1", 1L, 2L, 3L) - .setProperty("doubleKey1", 1.0, 2.0, 3.0) - .setProperty("booleanKey1", true, false, true) - .setProperty("stringKey1", "test-value1", "test-value2", "test-value3") - .setProperty("byteKey1", sByteArray1, sByteArray2) - .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) .build(); GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setTtlMillis(1L) - .setProperty("longKey1", 1L, 2L, 3L) - .setProperty("doubleKey1", 1.0, 2.0, 3.0) - .setProperty("booleanKey1", true, false, true) - .setProperty("stringKey1", "test-value1", "test-value2", "test-value3") - .setProperty("byteKey1", sByteArray1, sByteArray2) - .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) .build(); assertThat(document1).isEqualTo(document2); assertThat(document1.hashCode()).isEqualTo(document2.hashCode()); @@ -64,23 +64,23 @@ public class GenericDocumentTest { public void testDocumentEquals_DifferentOrder() { GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) - .setProperty("longKey1", 1L, 2L, 3L) - .setProperty("byteKey1", sByteArray1, sByteArray2) - .setProperty("doubleKey1", 1.0, 2.0, 3.0) - .setProperty("booleanKey1", true, false, true) - .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2) - .setProperty("stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) + .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") .build(); // Create second document with same parameter but different order. GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) - .setProperty("booleanKey1", true, false, true) - .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2) - .setProperty("stringKey1", "test-value1", "test-value2", "test-value3") - .setProperty("doubleKey1", 1.0, 2.0, 3.0) - .setProperty("byteKey1", sByteArray1, sByteArray2) - .setProperty("longKey1", 1L, 2L, 3L) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) + .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyLong("longKey1", 1L, 2L, 3L) .build(); assertThat(document1).isEqualTo(document2); assertThat(document1.hashCode()).isEqualTo(document2.hashCode()); @@ -90,13 +90,13 @@ public class GenericDocumentTest { public void testDocumentEquals_Failure() { GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) - .setProperty("longKey1", 1L, 2L, 3L) + .setPropertyLong("longKey1", 1L, 2L, 3L) .build(); // Create second document with same order but different value. GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) - .setProperty("longKey1", 1L, 2L, 4L) // Different + .setPropertyLong("longKey1", 1L, 2L, 4L) // Different .build(); assertThat(document1).isNotEqualTo(document2); assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode()); @@ -106,13 +106,13 @@ public class GenericDocumentTest { public void testDocumentEquals_Failure_RepeatedFieldOrder() { GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) - .setProperty("booleanKey1", true, false, true) + .setPropertyBoolean("booleanKey1", true, false, true) .build(); // Create second document with same order but different value. GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) - .setProperty("booleanKey1", true, true, false) // Different + .setPropertyBoolean("booleanKey1", true, true, false) // Different .build(); assertThat(document1).isNotEqualTo(document2); assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode()); @@ -124,12 +124,12 @@ public class GenericDocumentTest { .setCreationTimestampMillis(5L) .setScore(1) .setTtlMillis(1L) - .setProperty("longKey1", 1L) - .setProperty("doubleKey1", 1.0) - .setProperty("booleanKey1", true) - .setProperty("stringKey1", "test-value1") - .setProperty("byteKey1", sByteArray1) - .setProperty("documentKey1", sDocumentProperties1) + .setPropertyLong("longKey1", 1L) + .setPropertyDouble("doubleKey1", 1.0) + .setPropertyBoolean("booleanKey1", true) + .setPropertyString("stringKey1", "test-value1") + .setPropertyBytes("byteKey1", sByteArray1) + .setPropertyDocument("documentKey1", sDocumentProperties1) .build(); assertThat(document.getUri()).isEqualTo("uri1"); assertThat(document.getTtlMillis()).isEqualTo(1L); @@ -149,12 +149,12 @@ public class GenericDocumentTest { public void testDocumentGetArrayValues() { GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) - .setProperty("longKey1", 1L, 2L, 3L) - .setProperty("doubleKey1", 1.0, 2.0, 3.0) - .setProperty("booleanKey1", true, false, true) - .setProperty("stringKey1", "test-value1", "test-value2", "test-value3") - .setProperty("byteKey1", sByteArray1, sByteArray2) - .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) .build(); assertThat(document.getUri()).isEqualTo("uri1"); @@ -176,12 +176,12 @@ public class GenericDocumentTest { public void testDocument_ToString() throws Exception { GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) - .setProperty("longKey1", 1L, 2L, 3L) - .setProperty("doubleKey1", 1.0, 2.0, 3.0) - .setProperty("booleanKey1", true, false, true) - .setProperty("stringKey1", "String1", "String2", "String3") - .setProperty("byteKey1", sByteArray1, sByteArray2) - .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString("stringKey1", "String1", "String2", "String3") + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) .build(); String exceptedString = "{ key: 'creationTimestampMillis' value: 5 } " + "{ key: 'namespace' value: } " @@ -219,9 +219,9 @@ public class GenericDocumentTest { public void testDocumentGetValues_DifferentTypes() { GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1") .setScore(1) - .setProperty("longKey1", 1L) - .setProperty("booleanKey1", true, false, true) - .setProperty("stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyLong("longKey1", 1L) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") .build(); // Get a value for a key that doesn't exist @@ -246,6 +246,7 @@ public class GenericDocumentTest { public void testDocumentInvalid() { GenericDocument.Builder builder = new GenericDocument.Builder("uri1", "schemaType1"); expectThrows( - IllegalArgumentException.class, () -> builder.setProperty("test", new boolean[]{})); + IllegalArgumentException.class, + () -> builder.setPropertyBoolean("test", new boolean[]{})); } } diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java new file mode 100644 index 000000000000..d4635fdda052 --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.expectThrows; + +import android.os.Bundle; + +import org.junit.Test; + +public class SearchSpecTest { + @Test + public void buildSearchSpecWithoutTermMatchType() { + expectThrows(RuntimeException.class, () -> new SearchSpec.Builder() + .addSchema("testSchemaType") + .build()); + } + + @Test + public void testBuildSearchSpec() { + SearchSpec searchSpec = new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_PREFIX) + .addNamespace("namespace1", "namespace2") + .addSchema("schemaTypes1", "schemaTypes2") + .setSnippetCount(5) + .setSnippetCountPerProperty(10) + .setMaxSnippetSize(15) + .setNumPerPage(42) + .setOrder(SearchSpec.ORDER_ASCENDING) + .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE) + .build(); + + assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX); + assertThat(searchSpec.getNamespaces()) + .containsExactly("namespace1", "namespace2").inOrder(); + assertThat(searchSpec.getSchemas()) + .containsExactly("schemaTypes1", "schemaTypes2").inOrder(); + assertThat(searchSpec.getSnippetCount()).isEqualTo(5); + assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10); + assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15); + assertThat(searchSpec.getNumPerPage()).isEqualTo(42); + assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING); + assertThat(searchSpec.getRankingStrategy()) + .isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE); + } + + @Test + public void testGetBundle() { + SearchSpec searchSpec = new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_PREFIX) + .addNamespace("namespace1", "namespace2") + .addSchema("schemaTypes1", "schemaTypes2") + .setSnippetCount(5) + .setSnippetCountPerProperty(10) + .setMaxSnippetSize(15) + .setNumPerPage(42) + .setOrder(SearchSpec.ORDER_ASCENDING) + .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE) + .build(); + + Bundle bundle = searchSpec.getBundle(); + assertThat(bundle.getInt(SearchSpec.TERM_MATCH_TYPE_FIELD)) + .isEqualTo(SearchSpec.TERM_MATCH_PREFIX); + assertThat(bundle.getStringArrayList(SearchSpec.NAMESPACE_FIELD)).containsExactly( + "namespace1", "namespace2"); + assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_TYPE_FIELD)).containsExactly( + "schemaTypes1", "schemaTypes2"); + assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_FIELD)).isEqualTo(5); + assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_PER_PROPERTY_FIELD)).isEqualTo(10); + assertThat(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD)).isEqualTo(15); + assertThat(bundle.getInt(SearchSpec.NUM_PER_PAGE_FIELD)).isEqualTo(42); + assertThat(bundle.getInt(SearchSpec.ORDER_FIELD)).isEqualTo(SearchSpec.ORDER_ASCENDING); + assertThat(bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD)) + .isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE); + } +} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java index 2c7c35feface..d56d0c3c5c8e 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java @@ -46,12 +46,12 @@ public class CustomerDocumentTest { CustomerDocument customerDocument = new CustomerDocument.Builder("uri1") .setScore(1) .setCreationTimestampMillis(0) - .setProperty("longKey1", 1L, 2L, 3L) - .setProperty("doubleKey1", 1.0, 2.0, 3.0) - .setProperty("booleanKey1", true, false, true) - .setProperty("stringKey1", "test-value1", "test-value2", "test-value3") - .setProperty("byteKey1", sByteArray1, sByteArray2) - .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) .build(); assertThat(customerDocument.getUri()).isEqualTo("uri1"); diff --git a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java index ef659af6c570..8efd3b4f0135 100644 --- a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java +++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java @@ -63,12 +63,12 @@ import org.mockito.Mockito; /** * Tests for {@link TextViewOnReceiveContentCallback}. Most of the test cases are in the CTS test - * {@link android.widget.cts.TextViewOnReceiveContentCallbackTest}. This class tests some internal + * {@link android.widget.cts.TextViewOnReceiveContentTest}. This class tests some internal * implementation details, e.g. fallback to the keyboard image API. */ @MediumTest @RunWith(AndroidJUnit4.class) -public class TextViewOnReceiveContentCallbackTest { +public class TextViewOnReceiveContentTest { private static final Uri SAMPLE_CONTENT_URI = Uri.parse("content://com.example/path"); @Rule diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index 8f5982cd4528..7abcfdc98bc6 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -49,18 +49,38 @@ public class Credentials { public static final String INSTALL_AS_USER_ACTION = "android.credentials.INSTALL_AS_USER"; - /** Key prefix for CA certificates. */ + /** + * Key prefix for CA certificates. + * + * @deprecated Keystore no longer supports unstructured blobs. Public certificates are + * stored in typed slots associated with a given alias. + */ + @Deprecated public static final String CA_CERTIFICATE = "CACERT_"; - /** Key prefix for user certificates. */ + /** + * Key prefix for user certificates. + * + * @deprecated Keystore no longer supports unstructured blobs. Public certificates are + * stored in typed slots associated with a given alias. + */ + @Deprecated public static final String USER_CERTIFICATE = "USRCERT_"; - /** Key prefix for user private and secret keys. */ + /** + * Key prefix for user private and secret keys. + * + * @deprecated Keystore no longer uses alias prefixes to discriminate between entry types. + */ + @Deprecated public static final String USER_PRIVATE_KEY = "USRPKEY_"; - /** Key prefix for user secret keys. - * @deprecated use {@code USER_PRIVATE_KEY} for this category instead. + /** + * Key prefix for user secret keys. + * + * @deprecated use {@code USER_PRIVATE_KEY} for this category instead. */ + @Deprecated public static final String USER_SECRET_KEY = "USRSKEY_"; /** Key prefix for VPN. */ @@ -72,7 +92,13 @@ public class Credentials { /** Key prefix for WIFI. */ public static final String WIFI = "WIFI_"; - /** Key prefix for App Source certificates. */ + /** + * Key prefix for App Source certificates. + * + * @deprecated This was intended for FS-verity but never used. FS-verity is not + * going to use this constant moving forward. + */ + @Deprecated public static final String APP_SOURCE_CERTIFICATE = "FSV_"; /** Key containing suffix of lockdown VPN profile. */ @@ -150,6 +176,7 @@ public class Credentials { pw.close(); return bao.toByteArray(); } + /** * Convert objects from PEM format, which is used for * CA_CERTIFICATE and USER_CERTIFICATE entries. @@ -167,7 +194,8 @@ public class Credentials { PemObject o; while ((o = pr.readPemObject()) != null) { if (o.getType().equals("CERTIFICATE")) { - Certificate c = cf.generateCertificate(new ByteArrayInputStream(o.getContent())); + Certificate c = cf.generateCertificate( + new ByteArrayInputStream(o.getContent())); result.add((X509Certificate) c); } else { throw new IllegalArgumentException("Unknown type " + o.getType()); diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index d5b34c432e79..1c1c2eeee794 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -16,9 +16,9 @@ package android.security; -import android.app.KeyguardManager; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.KeyguardManager; import android.content.Context; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; @@ -78,8 +78,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private final Date mEndDate; - private final int mFlags; - /** * Parameter specification for the "{@code AndroidKeyPairGenerator}" * instance of the {@link java.security.KeyPairGenerator} API. The @@ -144,7 +142,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mSerialNumber = serialNumber; mStartDate = startDate; mEndDate = endDate; - mFlags = flags; } /** @@ -229,7 +226,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * @hide */ public int getFlags() { - return mFlags; + return 0; } /** @@ -243,9 +240,15 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * screen after boot. * * @see KeyguardManager#isDeviceSecure() + * + * @deprecated Encryption at rest is on by default. If extra binding to the lockscreen screen + * credential is desired use + * {@link KeyGenParameterSpec.Builder#setUserAuthenticationRequired(boolean)}. + * This flag will be ignored from Android S. */ + @Deprecated public boolean isEncryptionRequired() { - return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0; + return false; } /** @@ -292,8 +295,6 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private Date mEndDate; - private int mFlags; - /** * Creates a new instance of the {@code Builder} with the given * {@code context}. The {@code context} passed in may be used to pop up @@ -431,10 +432,15 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * secure lock screen after boot. * * @see KeyguardManager#isDeviceSecure() + * + * @deprecated Data at rest encryption is enabled by default. If extra binding to the + * lockscreen credential is desired, use + * {@link KeyGenParameterSpec.Builder#setUserAuthenticationRequired(boolean)}. + * This flag will be ignored from Android S. */ @NonNull + @Deprecated public Builder setEncryptionRequired() { - mFlags |= KeyStore.FLAG_ENCRYPTED; return this; } @@ -455,7 +461,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mSerialNumber, mStartDate, mEndDate, - mFlags); + 0); } } } diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java index 66c87ed2ec1e..51d29b13ce80 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -48,18 +48,16 @@ import java.security.KeyStore.ProtectionParameter; */ @Deprecated public final class KeyStoreParameter implements ProtectionParameter { - private final int mFlags; private KeyStoreParameter( int flags) { - mFlags = flags; } /** * @hide */ public int getFlags() { - return mFlags; + return 0; } /** @@ -74,9 +72,16 @@ public final class KeyStoreParameter implements ProtectionParameter { * screen after boot. * * @see KeyguardManager#isDeviceSecure() + * + * @deprecated Data at rest encryption is enabled by default. If extra binding to the + * lockscreen credential is desired, use + * {@link android.security.keystore.KeyGenParameterSpec + * .Builder#setUserAuthenticationRequired(boolean)}. + * This flag will be ignored from Android S. */ + @Deprecated public boolean isEncryptionRequired() { - return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0; + return false; } /** @@ -100,7 +105,6 @@ public final class KeyStoreParameter implements ProtectionParameter { */ @Deprecated public final static class Builder { - private int mFlags; /** * Creates a new instance of the {@code Builder} with the given @@ -126,14 +130,15 @@ public final class KeyStoreParameter implements ProtectionParameter { * the user unlocks the secure lock screen after boot. * * @see KeyguardManager#isDeviceSecure() + * + * @deprecated Data at rest encryption is enabled by default. If extra binding to the + * lockscreen credential is desired, use + * {@link android.security.keystore.KeyGenParameterSpec + * .Builder#setUserAuthenticationRequired(boolean)}. + * This flag will be ignored from Android S. */ @NonNull public Builder setEncryptionRequired(boolean required) { - if (required) { - mFlags |= KeyStore.FLAG_ENCRYPTED; - } else { - mFlags &= ~KeyStore.FLAG_ENCRYPTED; - } return this; } @@ -145,8 +150,7 @@ public final class KeyStoreParameter implements ProtectionParameter { */ @NonNull public KeyStoreParameter build() { - return new KeyStoreParameter( - mFlags); + return new KeyStoreParameter(0 /* flags */); } } } diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 1d85e9fceac9..b87a642a8dc7 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -57,6 +57,9 @@ <dimen name="pip_resize_handle_margin">4dp</dimen> <dimen name="pip_resize_handle_padding">0dp</dimen> + <!-- PIP stash offset size, which is the width of visible PIP region when stashed. --> + <dimen name="pip_stash_offset">32dp</dimen> + <dimen name="dismiss_target_x_size">24dp</dimen> <dimen name="floating_dismiss_bottom_margin">50dp</dimen> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index d4ff275d426d..51ddb17daa00 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -335,6 +335,12 @@ public class ShellTaskOrganizer extends TaskOrganizer { listener = mTaskListeners.get(taskId); if (listener != null) return listener; + // Next priority goes to the listener listening to its parent. + if (runningTaskInfo.hasParentTask()) { + listener = mTaskListeners.get(runningTaskInfo.parentTaskId); + if (listener != null) return listener; + } + // Next we try type specific listeners. final int taskListenerType = taskInfoToTaskListenerType(runningTaskInfo); return mTaskListeners.get(taskListenerType); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index 24381d937e2f..4c06a2ca3bca 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -101,13 +101,13 @@ public class SystemWindows { * Adds a view to system-ui window management. */ public void addView(View view, WindowManager.LayoutParams attrs, int displayId, - int windowType) { + @WindowManager.ShellRootLayer int shellRootLayer) { PerDisplay pd = mPerDisplay.get(displayId); if (pd == null) { pd = new PerDisplay(displayId); mPerDisplay.put(displayId, pd); } - pd.addView(view, attrs, windowType); + pd.addView(view, attrs, shellRootLayer); } /** @@ -149,18 +149,6 @@ public class SystemWindows { } /** - * Adds a root for system-ui window management with no views. Only useful for IME. - */ - public void addRoot(int displayId, int windowType) { - PerDisplay pd = mPerDisplay.get(displayId); - if (pd == null) { - pd = new PerDisplay(displayId); - mPerDisplay.put(displayId, pd); - } - pd.addRoot(windowType); - } - - /** * Get the IWindow token for a specific root. * * @param windowType A window type from {@link WindowManager}. @@ -198,8 +186,9 @@ public class SystemWindows { mDisplayId = displayId; } - public void addView(View view, WindowManager.LayoutParams attrs, int windowType) { - SysUiWindowManager wwm = addRoot(windowType); + public void addView(View view, WindowManager.LayoutParams attrs, + @WindowManager.ShellRootLayer int shellRootLayer) { + SysUiWindowManager wwm = addRoot(shellRootLayer); if (wwm == null) { Slog.e(TAG, "Unable to create systemui root"); return; @@ -213,23 +202,22 @@ public class SystemWindows { mViewRoots.put(view, viewRoot); try { - mWmService.setShellRootAccessibilityWindow(mDisplayId, windowType, + mWmService.setShellRootAccessibilityWindow(mDisplayId, shellRootLayer, viewRoot.getWindowToken()); } catch (RemoteException e) { Slog.e(TAG, "Error setting accessibility window for " + mDisplayId + ":" - + windowType, e); + + shellRootLayer, e); } } - - SysUiWindowManager addRoot(int windowType) { - SysUiWindowManager wwm = mWwms.get(windowType); + SysUiWindowManager addRoot(@WindowManager.ShellRootLayer int shellRootLayer) { + SysUiWindowManager wwm = mWwms.get(shellRootLayer); if (wwm != null) { return wwm; } SurfaceControl rootSurface = null; ContainerWindow win = new ContainerWindow(); try { - rootSurface = mWmService.addShellRoot(mDisplayId, win, windowType); + rootSurface = mWmService.addShellRoot(mDisplayId, win, shellRootLayer); } catch (RemoteException e) { } if (rootSurface == null) { @@ -238,7 +226,7 @@ public class SystemWindows { } Context displayContext = mDisplayController.getDisplayContext(mDisplayId); wwm = new SysUiWindowManager(mDisplayId, displayContext, rootSurface, win); - mWwms.put(windowType, wwm); + mWwms.put(shellRootLayer, wwm); return wwm; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java index d0ab31dd72c8..fc523aef9d16 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java @@ -37,6 +37,7 @@ public final class PipBoundsState { private final @NonNull Rect mBounds = new Rect(); private float mAspectRatio; + private boolean mIsStashed; private PipReentryState mPipReentryState; private ComponentName mLastPipComponentName; private final DisplayInfo mDisplayInfo = new DisplayInfo(); @@ -54,6 +55,20 @@ public final class PipBoundsState { return new Rect(mBounds); } + /** + * Dictate where PiP currently should be stashed or not. + */ + public void setStashed(boolean isStashed) { + mIsStashed = isStashed; + } + + /** + * Whether PiP is stashed or not. + */ + public boolean isStashed() { + return mIsStashed; + } + public void setAspectRatio(float aspectRatio) { mAspectRatio = aspectRatio; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index d223934fcf34..f44ebf3a3a5c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -52,9 +52,6 @@ import android.util.Log; import android.util.Rational; import android.util.Size; import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.View; -import android.view.WindowManager; import android.window.TaskOrganizer; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -66,6 +63,7 @@ import com.android.internal.os.SomeArgs; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.pip.phone.PipMenuActivityController; import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.pip.phone.PipUpdateThread; @@ -138,6 +136,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final Handler mUpdateHandler; private final PipBoundsState mPipBoundsState; private final PipBoundsHandler mPipBoundsHandler; + private final PipMenuActivityController mMenuActivityController; + private final SystemWindows mSystemWindows; private final PipAnimationController mPipAnimationController; private final PipUiEventLogger mPipUiEventLoggerLogger; private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); @@ -146,8 +146,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final Map<IBinder, Configuration> mInitialState = new HashMap<>(); private final Optional<SplitScreen> mSplitScreenOptional; protected final ShellTaskOrganizer mTaskOrganizer; - private SurfaceControlViewHost mPipViewHost; - private SurfaceControl mPipMenuSurface; // These callbacks are called on the update thread private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = @@ -267,15 +265,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState, @NonNull PipBoundsHandler boundsHandler, + PipMenuActivityController menuActivityController, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, @NonNull DisplayController displayController, @NonNull PipUiEventLogger pipUiEventLogger, - @NonNull ShellTaskOrganizer shellTaskOrganizer) { + @NonNull ShellTaskOrganizer shellTaskOrganizer, + @NonNull SystemWindows systemWindows) { mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsState = pipBoundsState; mPipBoundsHandler = boundsHandler; + mMenuActivityController = menuActivityController; + mSystemWindows = systemWindows; mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); mSurfaceTransactionHelper = surfaceTransactionHelper; @@ -640,60 +642,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } /** - * Setup the ViewHost and attach the provided menu view to the ViewHost. - * @return The input token belonging to the PipMenuView. - */ - public IBinder attachPipMenuViewHost(View menuView, WindowManager.LayoutParams lp) { - if (mPipMenuSurface != null) { - Log.e(TAG, "PIP Menu View already created and attached."); - return null; - } - - if (mLeash == null) { - Log.e(TAG, "PiP Leash is not yet ready."); - return null; - } - - if (Looper.getMainLooper() != Looper.myLooper()) { - throw new RuntimeException("PipMenuView needs to be attached on the main thread."); - } - final Context context = menuView.getContext(); - mPipViewHost = new SurfaceControlViewHost(context, context.getDisplay(), - (android.os.Binder) null); - mPipMenuSurface = mPipViewHost.getSurfacePackage().getSurfaceControl(); - SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); - transaction.reparent(mPipMenuSurface, mLeash); - transaction.show(mPipMenuSurface); - transaction.setRelativeLayer(mPipMenuSurface, mLeash, 1); - transaction.apply(); - mPipViewHost.setView(menuView, lp); - - return mPipViewHost.getSurfacePackage().getInputToken(); - } - - - /** - * Releases the PIP Menu's View host, remove it from PIP task surface. - */ - public void detachPipMenuViewHost() { - if (mPipMenuSurface != null) { - SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); - transaction.remove(mPipMenuSurface); - transaction.apply(); - mPipMenuSurface = null; - mPipViewHost = null; - } - } - - /** - * Return whether the PiP Menu has been attached to the leash yet. - */ - public boolean isPipMenuViewHostAttached() { - return mPipViewHost != null; - } - - - /** * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}. * Meanwhile this callback is invoked whenever the task is removed. For instance: * - as a result of removeRootTasksInWindowingModes from WM @@ -998,7 +946,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceTransactionHelper .crop(tx, mLeash, destinationBounds) .round(tx, mLeash, mState.isInPip()); - tx.apply(); + if (mMenuActivityController.isMenuVisible()) { + runOnMainHandler(() -> { + mMenuActivityController.resizePipMenu(mLeash, tx, destinationBounds); + }); + } else { + tx.apply(); + } } private void userResizePip(Rect startBounds, Rect destinationBounds) { @@ -1019,7 +973,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds); - tx.apply(); + if (mMenuActivityController.isMenuVisible()) { + runOnMainHandler(() -> { + mMenuActivityController.movePipMenu(mLeash, tx, destinationBounds); + }); + } else { + tx.apply(); + } } private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds, @@ -1034,6 +994,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, removePipImmediately(); return; } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) { + // TODO: Synchronize this correctly in #applyEnterPipSyncTransaction + runOnMainHandler(() -> { + mMenuActivityController.movePipMenu(null, null, destinationBounds); + mMenuActivityController.updateMenuBounds(destinationBounds); + }); return; } @@ -1041,10 +1006,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, prepareFinishResizeTransaction(destinationBounds, direction, tx, wct); applyFinishBoundsResize(wct, direction); runOnMainHandler(() -> { - if (mPipViewHost != null) { - mPipViewHost.relayout(PipMenuActivityController.getPipMenuLayoutParams( - destinationBounds.width(), destinationBounds.height())); - } + mMenuActivityController.movePipMenu(null, null, destinationBounds); + mMenuActivityController.updateMenuBounds(destinationBounds); }); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java index a87fa20a7f11..a5252654aa23 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java @@ -18,25 +18,37 @@ package com.android.wm.shell.pip.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; - +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; + +import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.RemoteAction; import android.content.Context; import android.content.pm.ParceledListSlice; +import android.graphics.Matrix; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.MotionEvent; +import android.view.SurfaceControl; +import android.view.SyncRtSurfaceTransactionApplier; +import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.WindowManager; import android.view.WindowManagerGlobal; +import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipMediaController.ActionListener; -import com.android.wm.shell.pip.PipTaskOrganizer; import java.io.PrintWriter; import java.util.ArrayList; @@ -51,6 +63,7 @@ import java.util.List; public class PipMenuActivityController { private static final String TAG = "PipMenuActController"; + private static final String MENU_WINDOW_TITLE = "PipMenuView"; private static final boolean DEBUG = false; public static final int MENU_STATE_NONE = 0; @@ -85,13 +98,18 @@ public class PipMenuActivityController { void onPipShowMenu(); } - private Context mContext; - private PipTaskOrganizer mPipTaskOrganizer; - private PipMediaController mMediaController; + private final Matrix mMoveTransform = new Matrix(); + private final Rect mTmpSourceBounds = new Rect(); + private final RectF mTmpSourceRectF = new RectF(); + private final RectF mTmpDestinationRectF = new RectF(); + private final Context mContext; + private final PipMediaController mMediaController; - private ArrayList<Listener> mListeners = new ArrayList<>(); + private final ArrayList<Listener> mListeners = new ArrayList<>(); + private final SystemWindows mSystemWindows; private ParceledListSlice<RemoteAction> mAppActions; private ParceledListSlice<RemoteAction> mMediaActions; + private SyncRtSurfaceTransactionApplier mApplier; private int mMenuState; private PipMenuView mPipMenuView; @@ -106,10 +124,10 @@ public class PipMenuActivityController { }; public PipMenuActivityController(Context context, - PipMediaController mediaController, PipTaskOrganizer pipTaskOrganizer) { + PipMediaController mediaController, SystemWindows systemWindows) { mContext = context; mMediaController = mediaController; - mPipTaskOrganizer = pipTaskOrganizer; + mSystemWindows = systemWindows; } public boolean isMenuVisible() { @@ -122,9 +140,7 @@ public class PipMenuActivityController { public void onActivityUnpinned() { hideMenu(); - mPipTaskOrganizer.detachPipMenuViewHost(); - mPipMenuView = null; - mPipMenuInputToken = null; + detachPipMenuView(); } public void onPinnedStackAnimationEnded() { @@ -136,15 +152,39 @@ public class PipMenuActivityController { private void attachPipMenuView() { if (mPipMenuView == null) { mPipMenuView = new PipMenuView(mContext, this); - } - // If we haven't gotten the input toekn, that means we haven't had a success attempt - // yet at attaching the PipMenuView - if (mPipMenuInputToken == null) { - mPipMenuInputToken = mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, - getPipMenuLayoutParams(0, 0)); + mSystemWindows.addView(mPipMenuView, getPipMenuLayoutParams(0, 0), 0, SHELL_ROOT_LAYER_PIP); + } + + private void detachPipMenuView() { + if (mPipMenuView == null) { + return; } + + mApplier = null; + mSystemWindows.removeView(mPipMenuView); + mPipMenuView = null; + mPipMenuInputToken = null; + } + + /** + * Updates the layout parameters of the menu. + * @param destinationBounds New Menu bounds. + */ + public void updateMenuBounds(Rect destinationBounds) { + mSystemWindows.updateViewLayout(mPipMenuView, + getPipMenuLayoutParams(destinationBounds.width(), destinationBounds.height())); + } + + /** + * Tries to grab a surface control from {@link PipMenuView}. If this isn't available for some + * reason (ie. the window isn't ready yet, thus {@link android.view.ViewRootImpl} is + * {@code null}), it will get the leash that the WindowlessWM has assigned to it. + */ + public SurfaceControl getSurfaceControl() { + SurfaceControl sf = mPipMenuView.getWindowSurfaceControl(); + return sf != null ? sf : mSystemWindows.getViewSurface(mPipMenuView); } /** @@ -190,16 +230,95 @@ public class PipMenuActivityController { + " callers=\n" + Debug.getCallers(5, " ")); } - if (!mPipTaskOrganizer.isPipMenuViewHostAttached()) { - Log.d(TAG, "PipMenu has not been attached yet. Attaching now at showMenuInternal()."); - attachPipMenuView(); - } + maybeCreateSyncApplier(); mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, showResizeHandle); } /** + * Move the PiP menu, which does a translation and possibly a scale transformation. + */ + public void movePipMenu(@Nullable SurfaceControl pipLeash, + @Nullable SurfaceControl.Transaction t, + Rect destinationBounds) { + if (destinationBounds.isEmpty()) { + return; + } + + if (!maybeCreateSyncApplier()) { + return; + } + + // If there is no pip leash supplied, that means the PiP leash is already finalized + // resizing and the PiP menu is also resized. We then want to do a scale from the current + // new menu bounds. + if (pipLeash != null && t != null) { + mPipMenuView.getBoundsOnScreen(mTmpSourceBounds); + } else { + mTmpSourceBounds.set(0, 0, destinationBounds.width(), destinationBounds.height()); + } + + mTmpSourceRectF.set(mTmpSourceBounds); + mTmpDestinationRectF.set(destinationBounds); + mMoveTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); + SurfaceControl surfaceControl = getSurfaceControl(); + SurfaceParams params = new SurfaceParams.Builder(surfaceControl) + .withMatrix(mMoveTransform) + .build(); + if (pipLeash != null && t != null) { + SurfaceParams pipParams = new SurfaceParams.Builder(pipLeash) + .withMergeTransaction(t) + .build(); + mApplier.scheduleApply(params, pipParams); + } else { + mApplier.scheduleApply(params); + } + } + + /** + * Does an immediate window crop of the PiP menu. + */ + public void resizePipMenu(@Nullable SurfaceControl pipLeash, + @Nullable SurfaceControl.Transaction t, + Rect destinationBounds) { + if (destinationBounds.isEmpty()) { + return; + } + + if (!maybeCreateSyncApplier()) { + return; + } + + SurfaceControl surfaceControl = getSurfaceControl(); + SurfaceParams params = new SurfaceParams.Builder(surfaceControl) + .withWindowCrop(destinationBounds) + .build(); + if (pipLeash != null && t != null) { + SurfaceParams pipParams = new SurfaceParams.Builder(pipLeash) + .withMergeTransaction(t) + .build(); + mApplier.scheduleApply(params, pipParams); + } else { + mApplier.scheduleApply(params); + } + } + + private boolean maybeCreateSyncApplier() { + if (mPipMenuView == null) { + Log.v(TAG, "Not going to move PiP since the menu is not created."); + return false; + } + + if (mApplier == null) { + mApplier = new SyncRtSurfaceTransactionApplier(mPipMenuView); + mPipMenuInputToken = mPipMenuView.getViewRootImpl().getInputToken(); + } + + return mApplier != null; + } + + /** * Pokes the menu, indicating that the user is interacting with it. */ public void pokeMenu() { @@ -296,8 +415,13 @@ public class PipMenuActivityController { * @param height the PIP stack height. */ public static WindowManager.LayoutParams getPipMenuLayoutParams(int width, int height) { - return new WindowManager.LayoutParams(width, height, - WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSLUCENT); + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, + TYPE_APPLICATION_OVERLAY, + FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY | FLAG_NOT_TOUCHABLE, + PixelFormat.TRANSLUCENT); + + lp.setTitle(MENU_WINDOW_TITLE); + return lp; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 3e06ec44989e..6e3cd8e9aa09 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -32,6 +32,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.annotation.Nullable; import android.app.PendingIntent.CanceledException; import android.app.RemoteAction; import android.content.ComponentName; @@ -50,8 +51,10 @@ import android.util.Pair; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.SurfaceControl; import android.view.View; import android.view.ViewGroup; +import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; @@ -268,10 +271,12 @@ public class PipMenuView extends FrameLayout { return; } mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY); + setVisibility(VISIBLE); mMenuContainerAnimator.start(); }); } else { notifyMenuStateChange(menuState, resizeMenuOnShow, null); + setVisibility(VISIBLE); mMenuContainerAnimator.start(); } } else { @@ -283,6 +288,18 @@ public class PipMenuView extends FrameLayout { } } + @Nullable SurfaceControl getWindowSurfaceControl() { + final ViewRootImpl root = getViewRootImpl(); + if (root == null) { + return null; + } + final SurfaceControl out = root.getSurfaceControl(); + if (out != null && out.isValid()) { + return out; + } + return null; + } + /** * Different from {@link #hideMenu()}, this function does not try to finish this menu activity * and instead, it fades out the controls by setting the alpha to 0 directly without menu @@ -338,6 +355,7 @@ public class PipMenuView extends FrameLayout { mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + setVisibility(GONE); if (animationFinishedRunnable != null) { animationFinishedRunnable.run(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index 9240b3f41ff4..9247c683a082 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -33,6 +33,7 @@ import androidx.dynamicanimation.animation.AnimationHandler; import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler; import androidx.dynamicanimation.animation.SpringForce; +import com.android.wm.shell.R; import com.android.wm.shell.animation.FloatProperties; import com.android.wm.shell.animation.PhysicsAnimator; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -60,7 +61,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private static final int EXPAND_STACK_TO_MENU_DURATION = 250; private static final int LEAVE_PIP_DURATION = 300; private static final int SHIFT_DURATION = 300; - private static final float STASH_RATIO = 0.25f; /** Friction to use for PIP when it moves via physics fling animations. */ private static final float DEFAULT_FRICTION = 2f; @@ -94,6 +94,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, /** The destination bounds to which PIP is animating. */ private final Rect mAnimatingToBounds = new Rect(); + private int mStashOffset = 0; + /** Coordinator instance for resolving conflicts with other floating content. */ private FloatingContentCoordinator mFloatingContentCoordinator; @@ -189,6 +191,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback); mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler( mSfAnimationHandlerThreadLocal.get()); + reloadResources(); mResizePipUpdateListener = (target, values) -> { if (!mTemporaryBounds.isEmpty()) { @@ -198,6 +201,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, }; } + void reloadResources() { + mStashOffset = mContext.getResources() + .getDimensionPixelSize(R.dimen.pip_stash_offset); + } + @NonNull @Override public Rect getFloatingBoundsOnScreen() { @@ -414,9 +422,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig) .withEndActions(endAction); - final float offset = ((float) getBounds().width()) * (1.0f - STASH_RATIO); - final float leftEdge = isStash ? mMovementBounds.left - offset : mMovementBounds.left; - final float rightEdge = isStash ? mMovementBounds.right + offset : mMovementBounds.right; + final float leftEdge = isStash ? mStashOffset - mPipBoundsState.getBounds().width() + : mMovementBounds.left; + final float rightEdge = isStash ? mPipBoundsState.getDisplayBounds().right - mStashOffset + : mMovementBounds.right; final float xEndValue = velocityX < 0 ? leftEdge : rightEdge; final float estimatedFlingYEndValue = @@ -524,9 +533,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, DEFAULT_FRICTION, mMovementBounds.left, mMovementBounds.right); mFlingConfigY = new PhysicsAnimator.FlingConfig( DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom); - final float offset = ((float) getBounds().width()) * (1.0f - STASH_RATIO); mStashConfigX = new PhysicsAnimator.FlingConfig( - DEFAULT_FRICTION, mMovementBounds.left - offset, mMovementBounds.right + offset); + DEFAULT_FRICTION, mStashOffset - mPipBoundsState.getBounds().width(), + mPipBoundsState.getDisplayBounds().right - mStashOffset); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index ef3875597aa2..f3d8c7b0f598 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -47,6 +47,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.policy.TaskResizingAlgorithm; import com.android.wm.shell.R; import com.android.wm.shell.pip.PipBoundsHandler; +import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipUiEventLogger; @@ -67,6 +68,7 @@ public class PipResizeGestureHandler { private final Context mContext; private final PipBoundsHandler mPipBoundsHandler; private final PipMotionHelper mMotionHelper; + private final PipBoundsState mPipBoundsState; private final int mDisplayId; private final Executor mMainExecutor; private final ScaleGestureDetector mScaleGestureDetector; @@ -107,13 +109,15 @@ public class PipResizeGestureHandler { private int mCtrlType; public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler, - PipMotionHelper motionHelper, PipTaskOrganizer pipTaskOrganizer, - Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable, - PipUiEventLogger pipUiEventLogger, PipMenuActivityController menuActivityController) { + PipBoundsState pipBoundsState, PipMotionHelper motionHelper, + PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier, + Runnable updateMovementBoundsRunnable, PipUiEventLogger pipUiEventLogger, + PipMenuActivityController menuActivityController) { mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); mPipBoundsHandler = pipBoundsHandler; + mPipBoundsState = pipBoundsState; mMotionHelper = motionHelper; mPipTaskOrganizer = pipTaskOrganizer; mMovementBoundsSupplier = movementBoundsSupplier; @@ -263,6 +267,11 @@ public class PipResizeGestureHandler { } private void onInputEvent(InputEvent ev) { + // Don't allow resize when PiP is stashed. + if (mPipBoundsState.isStashed()) { + return; + } + if (ev instanceof MotionEvent) { if (mUsingPinchToZoom) { mScaleGestureDetector.onTouchEvent((MotionEvent) ev); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index e95e4a07ed92..d820e772d490 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -179,8 +179,8 @@ public class PipTouchHandler { mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer, mMenuController, mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator); mPipResizeGestureHandler = - new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, - pipTaskOrganizer, this::getMovementBounds, + new PipResizeGestureHandler(context, pipBoundsHandler, pipBoundsState, + mMotionHelper, pipTaskOrganizer, this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger, menuController); mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger, mMotionHelper, mHandler); @@ -221,6 +221,7 @@ public class PipTouchHandler { R.dimen.pip_expanded_shortest_edge_size); mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); mPipDismissTargetHandler.updateMagneticTargetSize(); + mMotionHelper.reloadResources(); } private boolean shouldShowResizeHandle() { @@ -463,7 +464,7 @@ public class PipTouchHandler { } MotionEvent ev = (MotionEvent) inputEvent; - if (mPipResizeGestureHandler.willStartResizeGesture(ev)) { + if (!mPipBoundsState.isStashed() && mPipResizeGestureHandler.willStartResizeGesture(ev)) { // Initialize the touch state for the gesture, but immediately reset to invalidate the // gesture mTouchState.onTouchEvent(ev); @@ -553,6 +554,8 @@ public class PipTouchHandler { } } + shouldDeliverToMenu |= !mPipBoundsState.isStashed(); + // Deliver the event to PipMenuActivity to handle button click if the menu has shown. if (shouldDeliverToMenu) { final MotionEvent cloneEvent = MotionEvent.obtain(ev); @@ -715,7 +718,7 @@ public class PipTouchHandler { // If the menu is still visible then just poke the menu // so that it will timeout after the user stops touching it - if (mMenuState != MENU_STATE_NONE) { + if (mMenuState != MENU_STATE_NONE && !mPipBoundsState.isStashed()) { mMenuController.pokeMenu(); } } @@ -727,6 +730,7 @@ public class PipTouchHandler { } if (touchState.startedDragging()) { + mPipBoundsState.setStashed(false); mSavedSnapFraction = -1f; mPipDismissTargetHandler.showDismissTargetMaybe(); } @@ -785,12 +789,13 @@ public class PipTouchHandler { if (mEnableStash && (animatingBounds.right > mPipBoundsState.getDisplayBounds().right || animatingBounds.left < mPipBoundsState.getDisplayBounds().left)) { + mPipBoundsState.setStashed(true); mMotionHelper.stashToEdge(vel.x, vel.y, this::flingEndAction /* endAction */); } else { mMotionHelper.flingToSnapTarget(vel.x, vel.y, this::flingEndAction /* endAction */); } - } else if (mTouchState.isDoubleTap()) { + } else if (mTouchState.isDoubleTap() && !mPipBoundsState.isStashed()) { // If using pinch to zoom, double-tap functions as resizing between max/min size if (mPipResizeGestureHandler.isUsingPinchToZoom()) { final boolean toExpand = @@ -809,7 +814,7 @@ public class PipTouchHandler { setTouchEnabled(false); mMotionHelper.expandLeavePip(); } - } else if (mMenuState != MENU_STATE_FULL) { + } else if (mMenuState != MENU_STATE_FULL && !mPipBoundsState.isStashed()) { if (!mTouchState.isWaitingForDoubleTap()) { // User has stalled long enough for this not to be a drag or a double tap, just // expand the menu diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java index 0b4e17c27398..f2becc99eae8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java @@ -25,6 +25,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.SHELL_ROOT_LAYER_DIVIDER; import android.graphics.PixelFormat; import android.graphics.Region; @@ -63,7 +64,7 @@ final class DividerWindowManager { view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - mSystemWindows.addView(view, mLp, displayId, TYPE_DOCK_DIVIDER); + mSystemWindows.addView(view, mLp, displayId, SHELL_ROOT_LAYER_DIVIDER); mView = view; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 69d428a3ae1f..8b616e8fd1ee 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -117,7 +117,7 @@ public class SplitScreenController implements SplitScreen, mTransactionPool = transactionPool; mWindowManagerProxy = new WindowManagerProxy(syncQueue, shellTaskOrganizer); mTaskOrganizer = shellTaskOrganizer; - mSplits = new SplitScreenTaskListener(this, shellTaskOrganizer); + mSplits = new SplitScreenTaskListener(this, shellTaskOrganizer, syncQueue); mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler, shellTaskOrganizer); mRotationController = diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java index 191a317452e3..f709fed78b44 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java @@ -27,8 +27,10 @@ import static com.android.wm.shell.ShellTaskOrganizer.getWindowingMode; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; import android.app.ActivityManager.RunningTaskInfo; +import android.graphics.Point; import android.graphics.Rect; import android.util.Log; +import android.util.SparseArray; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -36,6 +38,8 @@ import androidx.annotation.NonNull; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.Transitions; +import com.android.wm.shell.common.SyncTransactionQueue; import java.io.PrintWriter; @@ -44,6 +48,8 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { private static final boolean DEBUG = SplitScreenController.DEBUG; private final ShellTaskOrganizer mTaskOrganizer; + private final SyncTransactionQueue mSyncQueue; + private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>(); RunningTaskInfo mPrimary; RunningTaskInfo mSecondary; @@ -58,9 +64,11 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { final SurfaceSession mSurfaceSession = new SurfaceSession(); SplitScreenTaskListener(SplitScreenController splitScreenController, - ShellTaskOrganizer shellTaskOrganizer) { + ShellTaskOrganizer shellTaskOrganizer, + SyncTransactionQueue syncQueue) { mSplitScreenController = splitScreenController; mTaskOrganizer = shellTaskOrganizer; + mSyncQueue = syncQueue; } void init() { @@ -93,6 +101,11 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { synchronized (this) { + if (taskInfo.hasParentTask()) { + handleChildTaskAppeared(taskInfo, leash); + return; + } + final int winMode = getWindowingMode(taskInfo); if (winMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { ProtoLog.v(WM_SHELL_TASK_ORG, @@ -139,6 +152,11 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { @Override public void onTaskVanished(RunningTaskInfo taskInfo) { synchronized (this) { + if (taskInfo.hasParentTask()) { + mLeashByTaskId.remove(taskInfo.taskId); + return; + } + final boolean isPrimaryTask = mPrimary != null && taskInfo.token.equals(mPrimary.token); final boolean isSecondaryTask = mSecondary != null @@ -165,7 +183,41 @@ class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { if (taskInfo.displayId != DEFAULT_DISPLAY) { return; } - mSplitScreenController.post(() -> handleTaskInfoChanged(taskInfo)); + synchronized (this) { + if (taskInfo.hasParentTask()) { + handleChildTaskChanged(taskInfo); + return; + } + + mSplitScreenController.post(() -> handleTaskInfoChanged(taskInfo)); + } + } + + private void handleChildTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { + mLeashByTaskId.put(taskInfo.taskId, leash); + updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */); + } + + private void handleChildTaskChanged(RunningTaskInfo taskInfo) { + final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId); + updateChildTaskSurface(taskInfo, leash, false /* firstAppeared */); + } + + private void updateChildTaskSurface( + RunningTaskInfo taskInfo, SurfaceControl leash, boolean firstAppeared) { + final Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds(); + final Point taskPositionInParent = taskInfo.positionInParent; + final Rect corp = new Rect(taskBounds); + corp.offset(-taskBounds.left, -taskBounds.top); + mSyncQueue.runInSync(t -> { + t.setWindowCrop(leash, corp); + t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y); + if (firstAppeared && !Transitions.ENABLE_SHELL_TRANSITIONS) { + t.setAlpha(leash, 1f); + t.setMatrix(leash, 1, 0, 0, 1); + t.show(leash); + } + }); } /** diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index 35a2293cbf13..e4155a257cee 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -248,6 +248,20 @@ public class ShellTaskOrganizerTests { } @Test + public void testGetParentTaskListener() { + RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); + TrackingTaskListener mwListener = new TrackingTaskListener(); + mOrganizer.onTaskAppeared(task1, null); + mOrganizer.addListenerForTaskId(mwListener, task1.taskId); + RunningTaskInfo task2 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); + task2.parentTaskId = task1.taskId; + + mOrganizer.onTaskAppeared(task2, null); + + assertTrue(mwListener.appeared.contains(task2)); + } + + @Test public void testTaskInfoToTaskListenerType_whenLetterboxBoundsPassed_returnsLetterboxType() { RunningTaskInfo taskInfo = createTaskInfo( /* taskId */ 1, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 1d10a84c53b9..ccc679797472 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -41,6 +41,8 @@ import android.window.WindowContainerToken; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.pip.phone.PipMenuActivityController; import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; @@ -62,10 +64,12 @@ public class PipTaskOrganizerTest extends PipTestCase { @Mock private DisplayController mMockdDisplayController; @Mock private PipBoundsHandler mMockPipBoundsHandler; + @Mock private PipMenuActivityController mMenuActivityController; @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper; @Mock private PipUiEventLogger mMockPipUiEventLogger; @Mock private Optional<SplitScreen> mMockOptionalSplitScreen; @Mock private ShellTaskOrganizer mMockShellTaskOrganizer; + @Mock private SystemWindows mSystemWindows; private PipBoundsState mPipBoundsState; private ComponentName mComponent1; @@ -78,8 +82,9 @@ public class PipTaskOrganizerTest extends PipTestCase { mComponent2 = new ComponentName(mContext, "component2"); mPipBoundsState = new PipBoundsState(); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState, - mMockPipBoundsHandler, mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, - mMockdDisplayController, mMockPipUiEventLogger, mMockShellTaskOrganizer)); + mMockPipBoundsHandler, mMenuActivityController, mMockPipSurfaceTransactionHelper, + mMockOptionalSplitScreen, mMockdDisplayController, mMockPipUiEventLogger, + mMockShellTaskOrganizer, mSystemWindows)); preparePipTaskOrg(); } diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java index 79b861136b64..610bffe13eae 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.Nullable; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Rect; import android.hardware.HardwareBuffer; @@ -58,6 +59,7 @@ public abstract class Image implements AutoCloseable { * @hide */ @UnsupportedAppUsage + @TestApi protected Image() { } @@ -387,6 +389,7 @@ public abstract class Image implements AutoCloseable { * @hide */ @UnsupportedAppUsage + @TestApi protected Plane() { } diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java index e5163ce51a90..39511a573da2 100644 --- a/media/java/android/media/MediaTranscodeManager.java +++ b/media/java/android/media/MediaTranscodeManager.java @@ -489,8 +489,8 @@ public final class MediaTranscodeManager { mContext = context; mContentResolver = mContext.getContentResolver(); mPackageName = mContext.getPackageName(); - mPid = Os.getuid(); - mUid = Os.getpid(); + mUid = Os.getuid(); + mPid = Os.getpid(); IMediaTranscodingService service = getService(false /*retry*/); mTranscodingClient = registerClient(service); } @@ -615,7 +615,7 @@ public final class MediaTranscodeManager { } /* Writes the TranscodingRequest to a parcel. */ - private TranscodingRequestParcel writeToParcel() { + private TranscodingRequestParcel writeToParcel(@NonNull Context context) { TranscodingRequestParcel parcel = new TranscodingRequestParcel(); // TODO(hkuang): Implement all the fields here to pass to service. parcel.priority = mPriority; @@ -624,6 +624,8 @@ public final class MediaTranscodeManager { parcel.destinationFilePath = mDestinationUri.toString(); parcel.clientUid = mClientUid; parcel.clientPid = mClientPid; + parcel.clientPackageName = mClientUid < 0 ? context.getPackageName() : + context.getPackageManager().getNameForUid(mClientUid); parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat); if (mTestConfig != null) { parcel.isForTesting = true; @@ -1203,7 +1205,8 @@ public final class MediaTranscodeManager { try { // Submits the request to MediaTranscoding service. TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel(); - if (!client.submitRequest(mRequest.writeToParcel(), sessionParcel)) { + if (!client.submitRequest(mRequest.writeToParcel(mManager.mContext), + sessionParcel)) { mHasRetried = true; throw new UnsupportedOperationException("Failed to enqueue request"); } @@ -1383,7 +1386,7 @@ public final class MediaTranscodeManager { Objects.requireNonNull(listener, "listener must not be null"); // Converts the request to TranscodingRequestParcel. - TranscodingRequestParcel requestParcel = transcodingRequest.writeToParcel(); + TranscodingRequestParcel requestParcel = transcodingRequest.writeToParcel(mContext); Log.i(TAG, "Getting transcoding request " + transcodingRequest.getSourceUri()); diff --git a/packages/DynamicSystemInstallationService/res/values-af/strings.xml b/packages/DynamicSystemInstallationService/res/values-af/strings.xml index 1829d342e9c5..1b300358adb1 100644 --- a/packages/DynamicSystemInstallationService/res/values-af/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-af/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Herbegin"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Het dinamiese stelsel weggegooi"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Kan nie dinamiese stelsel herbegin of laai nie"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-am/strings.xml b/packages/DynamicSystemInstallationService/res/values-am/strings.xml index 7fcc40de38d2..a8a7b00dd03e 100644 --- a/packages/DynamicSystemInstallationService/res/values-am/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-am/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ዳግም ጀምር"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"የተጣለ ተለዋዋጭ ሥርዓት"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ዳግም ማስጀመር አይቻልም ወይም ተለዋዋጭ ሥርዓትን ይስቀሉ"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ar/strings.xml b/packages/DynamicSystemInstallationService/res/values-ar/strings.xml index be705c37ef1d..44a6503eec08 100644 --- a/packages/DynamicSystemInstallationService/res/values-ar/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ar/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"إعادة التشغيل"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"تم تجاهل النظام الديناميكي."</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"لا يمكن إعادة التشغيل أو تحميل النظام الديناميكي."</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-as/strings.xml b/packages/DynamicSystemInstallationService/res/values-as/strings.xml index 14eead9f9753..e93ad9bec77c 100644 --- a/packages/DynamicSystemInstallationService/res/values-as/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-as/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ৰিষ্টাৰ্ট কৰক"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"বাতিল কৰা ডায়নামিক ছিষ্টেম"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ডায়নামিক ছিষ্টেম ৰিষ্টার্ট অথবা ল\'ড কৰিব নোৱাৰি"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-az/strings.xml b/packages/DynamicSystemInstallationService/res/values-az/strings.xml index d1f0a4b78ebb..ad3d3e7b1d28 100644 --- a/packages/DynamicSystemInstallationService/res/values-az/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-az/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Yenidən başladın"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamik sistemdən imtina edildi"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dinamik sistemi yenidən başlatmaq və ya yükləmək mümkün deyil"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml b/packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml index ea23a28bc9e5..01070479cd53 100644 --- a/packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restartuj"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamični sistem je odbačen"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Restartovanje ili učitavanje dinamičnog sistema nije uspelo"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-be/strings.xml b/packages/DynamicSystemInstallationService/res/values-be/strings.xml index 7eef297be292..5a52f735aee1 100644 --- a/packages/DynamicSystemInstallationService/res/values-be/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-be/strings.xml @@ -5,7 +5,7 @@ <string name="notification_install_completed" msgid="6252047868415172643">"Дынамічная сістэма гатовая. Каб пачаць выкарыстоўваць яе, перазапусціце прыладу."</string> <string name="notification_install_inprogress" msgid="7383334330065065017">"Ідзе ўсталёўка"</string> <string name="notification_install_failed" msgid="4066039210317521404">"Збой усталёўкі"</string> - <string name="notification_image_validation_failed" msgid="2720357826403917016">"Збой пры праверцы відарыса. Усталёўка спынена."</string> + <string name="notification_image_validation_failed" msgid="2720357826403917016">"Збой пры праверцы вобраза дыска. Усталёўка спынена."</string> <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Цяпер запушчана дынамічная сістэма. Перазапусціце, каб скарыстаць арыгінальную версію Android."</string> <string name="notification_action_cancel" msgid="5929299408545961077">"Скасаваць"</string> <string name="notification_action_discard" msgid="1817481003134947493">"Адхіліць"</string> @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Перазапусціць"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Дынамічная сістэма адхілена"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Не ўдалося перазапусціць або загрузіць дынамічную сістэму"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-bg/strings.xml b/packages/DynamicSystemInstallationService/res/values-bg/strings.xml index 9176676c0618..194bf2dd5a58 100644 --- a/packages/DynamicSystemInstallationService/res/values-bg/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-bg/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Рестартиране"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамичната система е отхвърлена"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Динамичната система не може да се рестартира или зареди"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-bn/strings.xml b/packages/DynamicSystemInstallationService/res/values-bn/strings.xml index 38ef64935fb4..43886b348888 100644 --- a/packages/DynamicSystemInstallationService/res/values-bn/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-bn/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"রিস্টার্ট করুন"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ডায়নামিক সিস্টেম বাতিল করা হয়েছে"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ডায়নামিক সিস্টেম রিস্টার্ট বা লোড করা যাচ্ছে না"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-bs/strings.xml b/packages/DynamicSystemInstallationService/res/values-bs/strings.xml index 84ba540bb822..342230b25f9d 100644 --- a/packages/DynamicSystemInstallationService/res/values-bs/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-bs/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Ponovo pokreni"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamični sistem je odbačen"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nije moguće ponovo pokrenuti ili učitati dinamični sistem"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ca/strings.xml b/packages/DynamicSystemInstallationService/res/values-ca/strings.xml index 787e4960513b..e9e4d3573f58 100644 --- a/packages/DynamicSystemInstallationService/res/values-ca/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ca/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reinicia"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"S\'ha descartat el sistema dinàmic"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"No es pot reiniciar ni carregar el sistema dinàmic"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-cs/strings.xml b/packages/DynamicSystemInstallationService/res/values-cs/strings.xml index 3dfb23f7d2ff..6ae71c6c438b 100644 --- a/packages/DynamicSystemInstallationService/res/values-cs/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-cs/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restartovat"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Zahodit dynamický systém"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dynamický systém nelze znovu spustit nebo načíst"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-da/strings.xml b/packages/DynamicSystemInstallationService/res/values-da/strings.xml index 20005e739205..10b798c2ce29 100644 --- a/packages/DynamicSystemInstallationService/res/values-da/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-da/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Genstart"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Det dynamiske system blev slettet"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Det dynamiske system kan ikke genstartes eller indlæses"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-de/strings.xml b/packages/DynamicSystemInstallationService/res/values-de/strings.xml index 3f000eaea251..82459b2f368d 100644 --- a/packages/DynamicSystemInstallationService/res/values-de/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-de/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Neu starten"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynamisches System verworfen"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Neustart und Laden des dynamischen Systems nicht möglich"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-el/strings.xml b/packages/DynamicSystemInstallationService/res/values-el/strings.xml index 4d830dde12e5..ef029063f544 100644 --- a/packages/DynamicSystemInstallationService/res/values-el/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-el/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Επανεκκίνηση"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Οι δυναμικές συστήματος απορρίφθηκαν."</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Δεν είναι δυνατή η επανεκκίνηση ή η φόρτωση δυναμικών συστήματος"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-en-rAU/strings.xml b/packages/DynamicSystemInstallationService/res/values-en-rAU/strings.xml index d72863128257..6f3f88763597 100644 --- a/packages/DynamicSystemInstallationService/res/values-en-rAU/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-en-rAU/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restart"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Discarded dynamic system"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Can’t restart or load dynamic system"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-en-rCA/strings.xml b/packages/DynamicSystemInstallationService/res/values-en-rCA/strings.xml index d72863128257..6f3f88763597 100644 --- a/packages/DynamicSystemInstallationService/res/values-en-rCA/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-en-rCA/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restart"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Discarded dynamic system"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Can’t restart or load dynamic system"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-en-rGB/strings.xml b/packages/DynamicSystemInstallationService/res/values-en-rGB/strings.xml index d72863128257..6f3f88763597 100644 --- a/packages/DynamicSystemInstallationService/res/values-en-rGB/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-en-rGB/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restart"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Discarded dynamic system"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Can’t restart or load dynamic system"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-en-rIN/strings.xml b/packages/DynamicSystemInstallationService/res/values-en-rIN/strings.xml index d72863128257..6f3f88763597 100644 --- a/packages/DynamicSystemInstallationService/res/values-en-rIN/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-en-rIN/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restart"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Discarded dynamic system"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Can’t restart or load dynamic system"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-en-rXC/strings.xml b/packages/DynamicSystemInstallationService/res/values-en-rXC/strings.xml index 6ac376322ceb..2e83672f1927 100644 --- a/packages/DynamicSystemInstallationService/res/values-en-rXC/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-en-rXC/strings.xml @@ -13,4 +13,5 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restart"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Discarded dynamic system"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Can’t restart or load dynamic system"</string> + <string name="toast_failed_to_disable_dynsystem" msgid="3285742944977744413">"Failed to disable dynamic system"</string> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-es-rUS/strings.xml b/packages/DynamicSystemInstallationService/res/values-es-rUS/strings.xml index 9ec819656495..aeb65be46f27 100644 --- a/packages/DynamicSystemInstallationService/res/values-es-rUS/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-es-rUS/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Se descartó el sistema dinámico"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"No se puede reiniciar o cargar el sistema dinámico"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-es/strings.xml b/packages/DynamicSystemInstallationService/res/values-es/strings.xml index cd9db07168a2..0c3ae5381453 100644 --- a/packages/DynamicSystemInstallationService/res/values-es/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-es/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Se ha descartado el sistema dinámico"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"No se ha podido reiniciar o cargar el sistema dinámico"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-et/strings.xml b/packages/DynamicSystemInstallationService/res/values-et/strings.xml index 64968b60ac6f..ab20a04493ef 100644 --- a/packages/DynamicSystemInstallationService/res/values-et/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-et/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Taaskäivita"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dünaamilisest süsteemist loobuti"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dünaamilist süsteemi ei saa taaskäivitada ega laadida"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-eu/strings.xml b/packages/DynamicSystemInstallationService/res/values-eu/strings.xml index 7c4a67d4e6c2..18637786ebea 100644 --- a/packages/DynamicSystemInstallationService/res/values-eu/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-eu/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Berrabiarazi"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Baztertu da sistema dinamikoa"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Ezin da berrabiarazi edo kargatu sistema dinamikoa"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-fa/strings.xml b/packages/DynamicSystemInstallationService/res/values-fa/strings.xml index 7533e71cb26c..5b0b21891a09 100644 --- a/packages/DynamicSystemInstallationService/res/values-fa/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-fa/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"بازراهاندازی"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"از سیستم پویا صرفنظر شد"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"نمیتوان سیستم پویا را بازراهاندازی یا بار کرد"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-fi/strings.xml b/packages/DynamicSystemInstallationService/res/values-fi/strings.xml index 948c3336cea8..b4315e7a22ef 100644 --- a/packages/DynamicSystemInstallationService/res/values-fi/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-fi/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Käynn. uudelleen"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynaaminen järjestelmä hylätty"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dynaamista järjestelmää ei voi käynnistää uudelleen tai ladata"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-fr-rCA/strings.xml b/packages/DynamicSystemInstallationService/res/values-fr-rCA/strings.xml index 6e2f235bcb76..973efef68a47 100644 --- a/packages/DynamicSystemInstallationService/res/values-fr-rCA/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-fr-rCA/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Redémarrer"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Système dynamique supprimé"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Impossible de redémarrer ou de charger le système dynamique"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-fr/strings.xml b/packages/DynamicSystemInstallationService/res/values-fr/strings.xml index 67f799731c6d..5422e8e4ec5b 100644 --- a/packages/DynamicSystemInstallationService/res/values-fr/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-fr/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Redémarrer"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Système dynamique supprimé"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Impossible de redémarrer ou de charger le système dynamique"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-gl/strings.xml b/packages/DynamicSystemInstallationService/res/values-gl/strings.xml index 8ea6d1c6c3b6..e24f495a425f 100644 --- a/packages/DynamicSystemInstallationService/res/values-gl/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-gl/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Descartouse o sistema dinámico"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Non se puido reiniciar nin cargar o sistema dinámico"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-gu/strings.xml b/packages/DynamicSystemInstallationService/res/values-gu/strings.xml index aec18049fdb0..6c2e67336c21 100644 --- a/packages/DynamicSystemInstallationService/res/values-gu/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-gu/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ફરી શરૂ કરો"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ડાઇનૅમિક સિસ્ટમ કાઢી નાખવામાં આવી"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ડાઇનૅમિક સિસ્ટમને ફરી શરૂ અથવા લોડ કરી શકાતી નથી"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-hi/strings.xml b/packages/DynamicSystemInstallationService/res/values-hi/strings.xml index efedbe8e7ee6..13dc9e8a3984 100644 --- a/packages/DynamicSystemInstallationService/res/values-hi/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-hi/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"रीस्टार्ट करें"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"डाइनैमिक सिस्टम खारिज किया गया"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"डाइनैमिक सिस्टम रीस्टार्ट या लोड नहीं हो सका"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-hr/strings.xml b/packages/DynamicSystemInstallationService/res/values-hr/strings.xml index 50ceaa16f764..3318d2040196 100644 --- a/packages/DynamicSystemInstallationService/res/values-hr/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-hr/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Pokreni"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Odbačeni dinamični sustav"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nije moguće ponovno pokretanje ili učitavanje dinamičnog sustava"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-hu/strings.xml b/packages/DynamicSystemInstallationService/res/values-hu/strings.xml index 94afa3b927b3..208ab3bdd260 100644 --- a/packages/DynamicSystemInstallationService/res/values-hu/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-hu/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Újraindítás"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Elvetett dinamikus rendszer"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nem lehet újraindítani vagy betölteni a dinamikus rendszert"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-hy/strings.xml b/packages/DynamicSystemInstallationService/res/values-hy/strings.xml index b0cd740d3e8d..4dcc72e38121 100644 --- a/packages/DynamicSystemInstallationService/res/values-hy/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-hy/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Վերագործարկել"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Դինամիկ համակարգի գործարկումը չեղարկվեց"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Չհաջողվեց վերագործարկել կամ բեռնել դինամիկ համակարգը"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-in/strings.xml b/packages/DynamicSystemInstallationService/res/values-in/strings.xml index 44b4aeec8b78..2fa4f113341a 100644 --- a/packages/DynamicSystemInstallationService/res/values-in/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-in/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Mulai ulang"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynamic System dihapus"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Tidak dapat memulai ulang atau memuat Dynamic System"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-is/strings.xml b/packages/DynamicSystemInstallationService/res/values-is/strings.xml index 048d1bca5518..ef7484c1d6cc 100644 --- a/packages/DynamicSystemInstallationService/res/values-is/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-is/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Endurræsa"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Breytilegu kerfi fleygt"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Ekki tókst að endurræsa eða hlaða breytilegu kerfi"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-it/strings.xml b/packages/DynamicSystemInstallationService/res/values-it/strings.xml index f70b38178021..c8fa41b3c92c 100644 --- a/packages/DynamicSystemInstallationService/res/values-it/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-it/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Riavvia"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistema dinamico annullato"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Impossibile riavviare o caricare il sistema dinamico"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-iw/strings.xml b/packages/DynamicSystemInstallationService/res/values-iw/strings.xml index aff7c824ba1d..d9bf983d4d76 100644 --- a/packages/DynamicSystemInstallationService/res/values-iw/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-iw/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"הפעלה מחדש"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"המערכת הדינמית נסגרה"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"לא ניתן להפעיל מחדש או לטעון את המערכת הדינמית"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ja/strings.xml b/packages/DynamicSystemInstallationService/res/values-ja/strings.xml index 46c093057a49..2082d91294da 100644 --- a/packages/DynamicSystemInstallationService/res/values-ja/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ja/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"再起動"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"動的システムを破棄しました"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"動的システムの再起動や読み込みを行えません"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ka/strings.xml b/packages/DynamicSystemInstallationService/res/values-ka/strings.xml index f841a59801e4..e57de2c1016a 100644 --- a/packages/DynamicSystemInstallationService/res/values-ka/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ka/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"გადატვირთვა"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"გაუქმებული დინამიური სისტემა"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"დინამიური სისტემის გადატვირთვა ან ჩატვირთვა ვერ ხერხდება"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-kk/strings.xml b/packages/DynamicSystemInstallationService/res/values-kk/strings.xml index d367b614e528..da10e9c582c0 100644 --- a/packages/DynamicSystemInstallationService/res/values-kk/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-kk/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Қайта қосу"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамикалық жүйе өшірілді."</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Динамикалық жүйені қайта қосу не жүктеу мүмкін емес."</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-km/strings.xml b/packages/DynamicSystemInstallationService/res/values-km/strings.xml index 56a37164fa6e..7bb5980ca1d4 100644 --- a/packages/DynamicSystemInstallationService/res/values-km/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-km/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ចាប់ផ្ដើមឡើងវិញ"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"បានលុបចោលប្រព័ន្ធឌីណាមិច"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"មិនអាចចាប់ផ្ដើមឡើងវិញ ឬផ្ទុកប្រព័ន្ធឌីណាមិចបានទេ"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-kn/strings.xml b/packages/DynamicSystemInstallationService/res/values-kn/strings.xml index b4063df9c10d..f41f2eff5cde 100644 --- a/packages/DynamicSystemInstallationService/res/values-kn/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-kn/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ಮರುಪ್ರಾರಂಭಿಸಿ"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ಡೈನಮಿಕ್ ಸಿಸ್ಟಂ ಅನ್ನು ತ್ಯಜಿಸಲಾಗಿದೆ"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ಡೈನಾಮಿಕ್ ಸಿಸ್ಟಂ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಅಥವಾ ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ko/strings.xml b/packages/DynamicSystemInstallationService/res/values-ko/strings.xml index 24ac9245e22c..bca9e0725a6e 100644 --- a/packages/DynamicSystemInstallationService/res/values-ko/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ko/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"다시 시작"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"동적 시스템 삭제됨"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"동적 시스템을 다시 시작하거나 로드할 수 없음"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ky/strings.xml b/packages/DynamicSystemInstallationService/res/values-ky/strings.xml index a4387e7bf4a1..d2ad56a44897 100644 --- a/packages/DynamicSystemInstallationService/res/values-ky/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ky/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Өчүрүп күйгүзүү"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамикалык система жоюлду"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Динамикалык система өчүрүлүп күйгүзүлбөй же жүктөлбөй жатат"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-lo/strings.xml b/packages/DynamicSystemInstallationService/res/values-lo/strings.xml index f17ca16bf777..a732aa414780 100644 --- a/packages/DynamicSystemInstallationService/res/values-lo/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-lo/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ຣີສະຕາດ"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ລະບົບໄດນາມິກທີ່ຍົກເລີກແລ້ວ"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ບໍ່ສາມາດຣີສະຕາດ ຫຼື ໂຫຼດລະບົບໄດນາມິກໄດ້"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-lt/strings.xml b/packages/DynamicSystemInstallationService/res/values-lt/strings.xml index 8128eb7b0a72..b25c62a5f4ff 100644 --- a/packages/DynamicSystemInstallationService/res/values-lt/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-lt/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Pal. iš naujo"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinaminė sistema atmesta"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nepavyko paleisti iš naujo ar įkelti dinaminės sistemos"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-lv/strings.xml b/packages/DynamicSystemInstallationService/res/values-lv/strings.xml index cfe7a087e148..4ca6ace3f5b3 100644 --- a/packages/DynamicSystemInstallationService/res/values-lv/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-lv/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restartēt"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamiskā sistēma tika atmesta"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nevar restartēt vai ielādēt dinamisko sistēmu"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-mk/strings.xml b/packages/DynamicSystemInstallationService/res/values-mk/strings.xml index 21215aa77631..52a52a5ca10d 100644 --- a/packages/DynamicSystemInstallationService/res/values-mk/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-mk/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Рестартирај"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Отфрлен динамичен систем"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Не може да го рестартира или вчита динамичниот систем"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ml/strings.xml b/packages/DynamicSystemInstallationService/res/values-ml/strings.xml index 951a0b95f7e4..25040694bbda 100644 --- a/packages/DynamicSystemInstallationService/res/values-ml/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ml/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"റീസ്റ്റാർട്ട് ചെയ്യൂ"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ഡെെനാമിക് സിസ്റ്റം നിരസിച്ചു"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"റീസ്റ്റാർട്ട് ചെയ്യാനോ ഡെെനാമിക് സിസ്റ്റം ലോഡ് ചെയ്യാനോ ആവില്ല"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-mn/strings.xml b/packages/DynamicSystemInstallationService/res/values-mn/strings.xml index d0965d021c65..fe93f65939b3 100644 --- a/packages/DynamicSystemInstallationService/res/values-mn/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-mn/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Дахин эхлүүлэх"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамик системийг устгасан"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Динамик системийг дахин эхлүүлэх эсвэл ачаалах боломжгүй байна"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-mr/strings.xml b/packages/DynamicSystemInstallationService/res/values-mr/strings.xml index 268e1d344620..5f27af01f635 100644 --- a/packages/DynamicSystemInstallationService/res/values-mr/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-mr/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"रीस्टार्ट करा"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"डायनॅमिक सिस्टम काढून टाकली"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"डायनॅमिक सिस्टम रीस्टार्ट किंवा लोड करू शकत नाही"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ms/strings.xml b/packages/DynamicSystemInstallationService/res/values-ms/strings.xml index bba8b9724f0d..797152c937ed 100644 --- a/packages/DynamicSystemInstallationService/res/values-ms/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ms/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Mulakan semula"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistem dinamik dibuang"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Tidak dapat memulakan semula atau memuatkan sistem dinamik"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-my/strings.xml b/packages/DynamicSystemInstallationService/res/values-my/strings.xml index b2488ece9ce3..3ee85b2136ee 100644 --- a/packages/DynamicSystemInstallationService/res/values-my/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-my/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ပြန်စရန်"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ပြောင်းလဲနိုင်သောစနစ်ကို ဖယ်လိုက်သည်"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ပြန်စ၍ မရပါ (သို့) ပြောင်းလဲနိုင်သောစနစ် ဖွင့်၍မရပါ"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-nb/strings.xml b/packages/DynamicSystemInstallationService/res/values-nb/strings.xml index 36e3d6912e23..88087e542b0c 100644 --- a/packages/DynamicSystemInstallationService/res/values-nb/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-nb/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Start på nytt"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Det dynamiske systemet er forkastet"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Det dynamiske systemet kan ikke startes på nytt eller lastes inn"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ne/strings.xml b/packages/DynamicSystemInstallationService/res/values-ne/strings.xml index ee9267852250..da98fee056e5 100644 --- a/packages/DynamicSystemInstallationService/res/values-ne/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ne/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"रिस्टार्ट गर्नु…"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynamic System खारेज गरियो"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"रिस्टार्ट गर्न वा Dynamic System लोड गर्न सकिएन"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-nl/strings.xml b/packages/DynamicSystemInstallationService/res/values-nl/strings.xml index 2b9fa414dcc5..5120c90eadc6 100644 --- a/packages/DynamicSystemInstallationService/res/values-nl/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-nl/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Herstarten"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynamisch systeem niet opgeslagen"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Kan dynamisch systeem niet opnieuw opstarten of laden"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-or/strings.xml b/packages/DynamicSystemInstallationService/res/values-or/strings.xml index e0c847094844..878947e48579 100644 --- a/packages/DynamicSystemInstallationService/res/values-or/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-or/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ଡାଇନାମିକ୍ ସିଷ୍ଟମ୍ ଖାରଜ କରାଯାଇଛି"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ଡାଇନାମିକ୍ ସିଷ୍ଟମ୍ ରିଷ୍ଟାର୍ଟ କିମ୍ବା ଲୋଡ୍ କରାଯାଇପାରିବ ନାହିଁ"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-pa/strings.xml b/packages/DynamicSystemInstallationService/res/values-pa/strings.xml index c5f7a3d38cdc..2695aaf39b25 100644 --- a/packages/DynamicSystemInstallationService/res/values-pa/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-pa/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ਪਰਿਵਰਤਨਸ਼ੀਲ ਸਿਸਟਮ ਰੱਦ ਕੀਤਾ ਗਿਆ"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ਪਰਿਵਰਤਨਸ਼ੀਲ ਸਿਸਟਮ ਮੁੜ-ਸ਼ੁਰੂ ਜਾਂ ਲੋਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-pl/strings.xml b/packages/DynamicSystemInstallationService/res/values-pl/strings.xml index bc7d5fe5b222..33592c86a090 100644 --- a/packages/DynamicSystemInstallationService/res/values-pl/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-pl/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Uruchom ponownie"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Usunięto system dynamiczny"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nie można ponownie uruchomić lub wczytać systemu dynamicznego"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-pt-rBR/strings.xml b/packages/DynamicSystemInstallationService/res/values-pt-rBR/strings.xml index 31a9bb439235..43bd02135672 100644 --- a/packages/DynamicSystemInstallationService/res/values-pt-rBR/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-pt-rBR/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistema dinâmico descartado"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Não é possível reiniciar ou carregar o sistema dinâmico"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-pt-rPT/strings.xml b/packages/DynamicSystemInstallationService/res/values-pt-rPT/strings.xml index d917c6afec6e..dc29a659d24c 100644 --- a/packages/DynamicSystemInstallationService/res/values-pt-rPT/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-pt-rPT/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistema dinâmico rejeitado"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Não é possível reiniciar ou carregar o sistema dinâmico."</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-pt/strings.xml b/packages/DynamicSystemInstallationService/res/values-pt/strings.xml index 31a9bb439235..43bd02135672 100644 --- a/packages/DynamicSystemInstallationService/res/values-pt/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-pt/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistema dinâmico descartado"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Não é possível reiniciar ou carregar o sistema dinâmico"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ro/strings.xml b/packages/DynamicSystemInstallationService/res/values-ro/strings.xml index c21131857830..c9d391a7fb88 100644 --- a/packages/DynamicSystemInstallationService/res/values-ro/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ro/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reporniți"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"S-a renunțat la sistemul dinamic"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nu se poate reporni sau încărca sistemul dinamic"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ru/strings.xml b/packages/DynamicSystemInstallationService/res/values-ru/strings.xml index bf94c99b8fb5..cba9a713f8fa 100644 --- a/packages/DynamicSystemInstallationService/res/values-ru/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ru/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Перезапустить"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамическая система удалена."</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Не удается запустить или загрузить динамическую систему."</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-si/strings.xml b/packages/DynamicSystemInstallationService/res/values-si/strings.xml index e6a6ea2fe351..7aab6e9b0ada 100644 --- a/packages/DynamicSystemInstallationService/res/values-si/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-si/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"යළි අරඹන්න"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ගතික පද්ධතිය ඉවත දමන ලදි"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ගතික පද්ධතිය නැවත ආරම්භ කිරීමට හෝ පූරණය කිරීමට නොහැක"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-sk/strings.xml b/packages/DynamicSystemInstallationService/res/values-sk/strings.xml index 99390cf07c74..61c176fb8905 100644 --- a/packages/DynamicSystemInstallationService/res/values-sk/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-sk/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reštartovať"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Zahodený dynamický systém"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nie je možné reštartovať alebo načítať dynamický systém"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-sl/strings.xml b/packages/DynamicSystemInstallationService/res/values-sl/strings.xml index 3ffd741ea4f6..0be1248bcff1 100644 --- a/packages/DynamicSystemInstallationService/res/values-sl/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-sl/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Znova zaženi"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamični sistem je bil zavržen"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dinamičnega sistema ni mogoče znova zagnati ali naložiti"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-sq/strings.xml b/packages/DynamicSystemInstallationService/res/values-sq/strings.xml index 704b512ad9ab..43a5f7bbc876 100644 --- a/packages/DynamicSystemInstallationService/res/values-sq/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-sq/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Rinis"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistemi dinamik u hoq"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Sistemi dinamik nuk mund të rinisej ose të ngarkohej"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-sr/strings.xml b/packages/DynamicSystemInstallationService/res/values-sr/strings.xml index 5e4540a5eff0..a5ba6565d1ea 100644 --- a/packages/DynamicSystemInstallationService/res/values-sr/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-sr/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Рестартуј"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамични систем је одбачен"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Рестартовање или учитавање динамичног система није успело"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-sv/strings.xml b/packages/DynamicSystemInstallationService/res/values-sv/strings.xml index 546ffddde639..99e6752c0bed 100644 --- a/packages/DynamicSystemInstallationService/res/values-sv/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-sv/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Starta om"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Det dynamiska systemet ignorerades"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Det gick inte att starta om eller läsa in det dynamiska systemet"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-sw/strings.xml b/packages/DynamicSystemInstallationService/res/values-sw/strings.xml index 53414d5f6b0a..8af9ad35a763 100644 --- a/packages/DynamicSystemInstallationService/res/values-sw/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-sw/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Zima kisha uwashe"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Umeondoa Dynamic System"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Imeshindwa kuzima na kuwasha au kupakia Dynamic System"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ta/strings.xml b/packages/DynamicSystemInstallationService/res/values-ta/strings.xml index e0aaaf7458fc..bbe8c4e5e03e 100644 --- a/packages/DynamicSystemInstallationService/res/values-ta/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ta/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"மீண்டும் தொடங்கு"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynamic system நிராகரிக்கப்பட்டது"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dynamic systemமை மீண்டும் தொடங்கவோ ஏற்றவோ முடியவில்லை"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-te/strings.xml b/packages/DynamicSystemInstallationService/res/values-te/strings.xml index d497630660e3..4a9433e7116e 100644 --- a/packages/DynamicSystemInstallationService/res/values-te/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-te/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"రీస్టార్ట్ చేయి"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"డైనమిక్ సిస్టమ్ విస్మరించబడింది"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"డైనమిక్ సిస్టమ్ను రీస్టార్ట్ చేయడం లేదా లోడ్ చేయడం సాధ్యపడలేదు"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-th/strings.xml b/packages/DynamicSystemInstallationService/res/values-th/strings.xml index 786324f57488..5b81a03c51e3 100644 --- a/packages/DynamicSystemInstallationService/res/values-th/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-th/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"รีสตาร์ท"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ยกเลิกระบบแบบไดนามิกแล้ว"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"รีสตาร์ทหรือโหลดระบบแบบไดนามิกไม่ได้"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-tl/strings.xml b/packages/DynamicSystemInstallationService/res/values-tl/strings.xml index df39f7b1886a..d6c2f1508f75 100644 --- a/packages/DynamicSystemInstallationService/res/values-tl/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-tl/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"I-restart"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Na-discard ang dynamic system"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Hindi ma-restart o ma-load ang dynamic system"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-tr/strings.xml b/packages/DynamicSystemInstallationService/res/values-tr/strings.xml index 1446f9632456..a98526e0cdbe 100644 --- a/packages/DynamicSystemInstallationService/res/values-tr/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-tr/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Yeniden başlat"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamik sistem silindi"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dinamik sistem yeniden başlatılamıyor veya yüklenemiyor"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-uk/strings.xml b/packages/DynamicSystemInstallationService/res/values-uk/strings.xml index 9a44d9764014..65267872b724 100644 --- a/packages/DynamicSystemInstallationService/res/values-uk/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-uk/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Перезапустити"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамічну систему видалено"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Не вдається перезапустити пристрій або завантажити динамічну систему"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-ur/strings.xml b/packages/DynamicSystemInstallationService/res/values-ur/strings.xml index 48dddbea3281..f5e2b744e5b2 100644 --- a/packages/DynamicSystemInstallationService/res/values-ur/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-ur/strings.xml @@ -5,7 +5,7 @@ <string name="notification_install_completed" msgid="6252047868415172643">"ڈائنیمک سسٹم تیار ہے۔ اس کا استعمال شروع کرنے کے لیے، اپنا آلہ ری سٹارٹ کریں۔"</string> <string name="notification_install_inprogress" msgid="7383334330065065017">"انسٹال جاری ہے"</string> <string name="notification_install_failed" msgid="4066039210317521404">"انسٹال ناکام ہو گیا"</string> - <string name="notification_image_validation_failed" msgid="2720357826403917016">"تصویر کی توثیق ناکام ہو گئی۔ انسٹالیشن منسوخ کریں-"</string> + <string name="notification_image_validation_failed" msgid="2720357826403917016">"ڈسک امیج کی توثیق ناکام ہو گئی۔ انسٹالیشن منسوخ کریں۔"</string> <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"فی الحال ہم ایک ڈائنیمک سسٹم چلا رہے ہیں۔ Android کا اصل ورژن استعمال کرنے کے لیے ری سٹارٹ کریں۔"</string> <string name="notification_action_cancel" msgid="5929299408545961077">"منسوخ کریں"</string> <string name="notification_action_discard" msgid="1817481003134947493">"مسترد کریں"</string> @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ری سٹارٹ کریں"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"مسترد کردہ ڈائنیمک سسٹم"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ڈائنیمک سسٹم کو ری سٹارٹ یا لوڈ نہیں کر سکتے"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-uz/strings.xml b/packages/DynamicSystemInstallationService/res/values-uz/strings.xml index 3f0227c1c383..3c347e2e87ba 100644 --- a/packages/DynamicSystemInstallationService/res/values-uz/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-uz/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Boshidan"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamik tizim bekor qilindi"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dinamik tizim qayta ishga tushmadi yoki yuklanmadi"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-vi/strings.xml b/packages/DynamicSystemInstallationService/res/values-vi/strings.xml index 18c051c893f8..a93d29e383ca 100644 --- a/packages/DynamicSystemInstallationService/res/values-vi/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-vi/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Khởi động lại"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Đã hủy hệ thống động"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Không thể khởi động lại hoặc tải hệ thống động"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-zh-rCN/strings.xml b/packages/DynamicSystemInstallationService/res/values-zh-rCN/strings.xml index b41d4e2bf912..c27718ea8c76 100644 --- a/packages/DynamicSystemInstallationService/res/values-zh-rCN/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-zh-rCN/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"重启"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"已舍弃动态系统"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"无法重启或加载动态系统"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-zh-rHK/strings.xml b/packages/DynamicSystemInstallationService/res/values-zh-rHK/strings.xml index c830dae24e97..656c2af188c4 100644 --- a/packages/DynamicSystemInstallationService/res/values-zh-rHK/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-zh-rHK/strings.xml @@ -5,7 +5,7 @@ <string name="notification_install_completed" msgid="6252047868415172643">"動態系統已可供使用。如要開始使用,請重新啟動裝置。"</string> <string name="notification_install_inprogress" msgid="7383334330065065017">"安裝中"</string> <string name="notification_install_failed" msgid="4066039210317521404">"無法安裝"</string> - <string name="notification_image_validation_failed" msgid="2720357826403917016">"圖片驗證失敗,系統將取消安裝。"</string> + <string name="notification_image_validation_failed" msgid="2720357826403917016">"磁碟影像驗證失敗,系統將取消安裝。"</string> <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"系統目前正在執行動態系統。如要使用原本的 Android 版本,請重新啟動裝置。"</string> <string name="notification_action_cancel" msgid="5929299408545961077">"取消"</string> <string name="notification_action_discard" msgid="1817481003134947493">"捨棄"</string> @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"重新啟動"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"已捨棄動態系統"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"無法重新啟動或載入動態系統"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-zh-rTW/strings.xml b/packages/DynamicSystemInstallationService/res/values-zh-rTW/strings.xml index e43c0f2b08bb..a6f9b56e438c 100644 --- a/packages/DynamicSystemInstallationService/res/values-zh-rTW/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-zh-rTW/strings.xml @@ -5,7 +5,7 @@ <string name="notification_install_completed" msgid="6252047868415172643">"動態系統已可供使用。如要開始使用,請重新啟動裝置。"</string> <string name="notification_install_inprogress" msgid="7383334330065065017">"安裝中"</string> <string name="notification_install_failed" msgid="4066039210317521404">"無法安裝"</string> - <string name="notification_image_validation_failed" msgid="2720357826403917016">"圖片驗證失敗,系統將取消安裝作業。"</string> + <string name="notification_image_validation_failed" msgid="2720357826403917016">"映像檔驗證失敗,系統將取消安裝作業。"</string> <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"系統目前正在執行動態系統。如要使用原本的 Android 版本,請重新啟動裝置。"</string> <string name="notification_action_cancel" msgid="5929299408545961077">"取消"</string> <string name="notification_action_discard" msgid="1817481003134947493">"捨棄"</string> @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"重新啟動"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"已捨棄動態系統"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"無法重新啟動或載入動態系統"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values-zu/strings.xml b/packages/DynamicSystemInstallationService/res/values-zu/strings.xml index 4a48444cb9a0..0cf79ba51b45 100644 --- a/packages/DynamicSystemInstallationService/res/values-zu/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-zu/strings.xml @@ -13,4 +13,6 @@ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Qala kabusha"</string> <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Kulahlwe uhlole olunhlobonhlobo"</string> <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Ayikwazi ukuqalisa kabusha noma ukulayisha uhlole olunhlobonhlobo"</string> + <!-- no translation found for toast_failed_to_disable_dynsystem (3285742944977744413) --> + <skip /> </resources> diff --git a/packages/DynamicSystemInstallationService/res/values/strings.xml b/packages/DynamicSystemInstallationService/res/values/strings.xml index 719fc73bd225..bfeef0aa8c7b 100644 --- a/packages/DynamicSystemInstallationService/res/values/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values/strings.xml @@ -37,6 +37,9 @@ <string name="toast_dynsystem_discarded">Discarded dynamic system</string> <!-- Toast when we fail to launch into Dynamic System [CHAR LIMIT=128] --> <string name="toast_failed_to_reboot_to_dynsystem">Can\u2019t restart or load dynamic system</string> + <!-- Toast when we fail to disable Dynamic System [CHAR LIMIT=128] --> + <string name="toast_failed_to_disable_dynsystem">Failed to disable dynamic system</string> + <!-- URL of Dynamic System Key Revocation List [DO NOT TRANSLATE] --> <string name="key_revocation_list_url" translatable="false">https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json</string> diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index 12505bc55b15..ac2758011816 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -375,8 +375,17 @@ public class DynamicSystemInstallationService extends Service return; } - // Per current design, we don't have disable() API. AOT is disabled on next reboot. - // TODO: Use better status query when b/125079548 is done. + if (!mDynSystem.setEnable(/* enable = */ false, /* oneShot = */ false)) { + Log.e(TAG, "Failed to disable DynamicSystem."); + + // Dismiss status bar and show a toast. + sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + Toast.makeText(this, + getString(R.string.toast_failed_to_disable_dynsystem), + Toast.LENGTH_LONG).show(); + return; + } + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); if (powerManager != null) { diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java index bc40903d88e4..f9bb90e9616a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java @@ -24,6 +24,7 @@ import static android.os.BatteryManager.EXTRA_LEVEL; import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT; import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE; import static android.os.BatteryManager.EXTRA_PLUGGED; +import static android.os.BatteryManager.EXTRA_PRESENT; import static android.os.BatteryManager.EXTRA_STATUS; import android.content.Context; @@ -49,14 +50,16 @@ public class BatteryStatus { public final int plugged; public final int health; public final int maxChargingWattage; + public final boolean present; public BatteryStatus(int status, int level, int plugged, int health, - int maxChargingWattage) { + int maxChargingWattage, boolean present) { this.status = status; this.level = level; this.plugged = plugged; this.health = health; this.maxChargingWattage = maxChargingWattage; + this.present = present; } public BatteryStatus(Intent batteryChangedIntent) { @@ -64,6 +67,7 @@ public class BatteryStatus { plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0); level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0); health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN); + present = batteryChangedIntent.getBooleanExtra(EXTRA_PRESENT, true); final int maxChargingMicroAmp = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1); diff --git a/packages/SettingsProvider/src/android/provider/settings/OWNERS b/packages/SettingsProvider/src/android/provider/settings/OWNERS index 541dd8787545..7e7710b4d550 100644 --- a/packages/SettingsProvider/src/android/provider/settings/OWNERS +++ b/packages/SettingsProvider/src/android/provider/settings/OWNERS @@ -1,5 +1,4 @@ -# Please reach out to Android B&R when making Settings backup changes -alsutton@google.com -nathch@google.com -rthakohov@google.com +# Bug component: 656484 + +include platform/frameworks/base/services/backup:/OWNERS diff --git a/packages/SettingsProvider/test/src/android/provider/OWNERS b/packages/SettingsProvider/test/src/android/provider/OWNERS index f3241ea9d1f9..7e7710b4d550 100644 --- a/packages/SettingsProvider/test/src/android/provider/OWNERS +++ b/packages/SettingsProvider/test/src/android/provider/OWNERS @@ -1,4 +1,4 @@ -per-file * = * +# Bug component: 656484 + +include platform/frameworks/base/services/backup:/OWNERS -# Please reach out to the Android B&R team for settings backup changes -per-file SettingsBackupTest.java = alsutton@google.com, nathch@google.com, rthakohov@google.com diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 3714db324fb4..939c5f9f7ecb 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -73,7 +73,7 @@ android:id="@+id/gradient_clock_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="100dp" + android:textSize="80dp" android:letterSpacing="0.02" android:lineSpacingMultiplier=".8" android:includeFontPadding="false" diff --git a/packages/SystemUI/res/drawable/ic_battery_unknown.xml b/packages/SystemUI/res/drawable/ic_battery_unknown.xml new file mode 100644 index 000000000000..8b2ba12fe0be --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_unknown.xml @@ -0,0 +1,24 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="12dp" + android:height="24dp" + android:viewportWidth="12.0" + android:viewportHeight="24.0"> + <path + android:pathData="M10.404,2.4L8.4,2.4L8.4,0L3.6,0L3.6,2.4L1.596,2.4C0.72,2.4 0,3.12 0,3.996L0,22.392C0,23.28 0.72,24 1.596,24L10.392,24C11.28,24 12,23.28 12,22.404L12,3.996C12,3.12 11.28,2.4 10.404,2.4ZM7.14,19.14L4.86,19.14L4.86,16.86L7.14,16.86L7.14,19.14ZM8.76,12.828C8.76,12.828 8.304,13.332 7.956,13.68C7.38,14.256 6.96,15.06 6.96,15.6L5.04,15.6C5.04,14.604 5.592,13.776 6.156,13.2L7.272,12.072C7.596,11.748 7.8,11.292 7.8,10.8C7.8,9.804 6.996,9 6,9C5.004,9 4.2,9.804 4.2,10.8L2.4,10.8C2.4,8.808 4.008,7.2 6,7.2C7.992,7.2 9.6,8.808 9.6,10.8C9.6,11.592 9.276,12.312 8.76,12.828L8.76,12.828Z" + android:fillColor="#ffffff" /> +</vector> diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 0a33d5e4429b..b2c968c0292d 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -24,7 +24,7 @@ android:clipChildren="false" android:clipToPadding="true" android:orientation="vertical" - android:paddingStart="@*android:dimen/notification_content_margin_start"> + android:paddingStart="@dimen/notification_shade_content_margin_horizontal"> <!-- Package Info --> <LinearLayout @@ -120,7 +120,7 @@ asked for it --> android:id="@+id/inline_controls" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingEnd="@*android:dimen/notification_content_margin_end" + android:paddingEnd="@dimen/notification_shade_content_margin_horizontal" android:layout_marginTop="@dimen/notification_guts_option_vertical_padding" android:clipChildren="false" android:clipToPadding="false" diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml index af66f8ba4cd6..9ed3f92a7d74 100644 --- a/packages/SystemUI/res/layout/partial_conversation_info.xml +++ b/packages/SystemUI/res/layout/partial_conversation_info.xml @@ -24,7 +24,7 @@ android:clipChildren="false" android:clipToPadding="true" android:orientation="vertical" - android:paddingStart="@*android:dimen/notification_content_margin_start"> + android:paddingStart="@dimen/notification_shade_content_margin_horizontal"> <!-- Package Info --> <LinearLayout @@ -93,7 +93,7 @@ android:id="@+id/inline_controls" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingEnd="@*android:dimen/notification_content_margin_end" + android:paddingEnd="@dimen/notification_shade_content_margin_horizontal" android:layout_marginTop="@dimen/notification_guts_option_vertical_padding" android:clipChildren="false" android:clipToPadding="false" diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 57ce0b5da7c1..1a72fc231815 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -568,4 +568,9 @@ <!-- The duraction of the hide animation for the volume dialog in milliseconds --> <integer name="config_dialogHideAnimationDurationMs">250</integer> + + <!-- Whether or not to show a notification for an unknown battery state --> + <bool name="config_showNotificationForUnknownBatteryState">false</bool> + <!-- content URL in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false --> + <string translatable="false" name="config_batteryStateUnknownUrl"></string> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1ab776b2a0a8..17dc4004ae9b 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -623,6 +623,9 @@ @dimen/notification_divider_height </dimen> + <!-- The horizontal margin of the content in the notification shade --> + <dimen name="notification_shade_content_margin_horizontal">16dp</dimen> + <!-- The top margin for the notification children container in its non-expanded form. --> <dimen name="notification_children_container_margin_top"> @*android:dimen/notification_content_margin_top diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 2be89c1dff63..d5c98233b952 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -439,6 +439,8 @@ <string name="accessibility_battery_three_bars">Battery three bars.</string> <!-- Content description of the battery when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_battery_full">Battery full.</string> + <!-- Content description of the battery when battery state is unknown for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_battery_unknown">Battery percentage unknown.</string> <!-- Content description of the phone signal when no signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_no_phone">No phone.</string> @@ -2862,4 +2864,10 @@ <!-- Status for conversation without interaction data [CHAR LIMIT=120] --> <string name="basic_status" translatable="false">Open conversation</string> + <!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false + [CHAR LIMIT=NONE] --> + <string name="battery_state_unknown_notification_title">Problem reading your battery meter</string> + <!-- Text to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false + [CHAR LIMIT=NONE] --> + <string name="battery_state_unknown_notification_text">Tap for more information</string> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 9beb7db40096..0e6bc24b02d6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -94,6 +94,7 @@ import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -235,6 +236,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final Context mContext; private final boolean mIsPrimaryUser; private final boolean mIsAutomotive; + private final AuthController mAuthController; private final StatusBarStateController mStatusBarStateController; HashMap<Integer, SimData> mSimDatas = new HashMap<>(); HashMap<Integer, ServiceState> mServiceStates = new HashMap<Integer, ServiceState>(); @@ -1581,7 +1583,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab RingerModeTracker ringerModeTracker, @Background Executor backgroundExecutor, StatusBarStateController statusBarStateController, - LockPatternUtils lockPatternUtils) { + LockPatternUtils lockPatternUtils, + AuthController authController) { mContext = context; mSubscriptionManager = SubscriptionManager.from(context); mDeviceProvisioned = isDeviceProvisionedInSettingsDb(); @@ -1591,6 +1594,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mRingerModeTracker = ringerModeTracker; mStatusBarStateController = statusBarStateController; mLockPatternUtils = lockPatternUtils; + mAuthController = authController; dumpManager.registerDumpable(getClass().getName(), this); mHandler = new Handler(mainLooper) { @@ -1717,7 +1721,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } // Take a guess at initial SIM state, battery status and PLMN until we get an update - mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0); + mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0, true); // Watch for interesting updates final IntentFilter filter = new IntentFilter(); @@ -1853,7 +1857,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private void updateLockScreenMode() { mLockScreenMode = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.SHOW_NEW_LOCKSCREEN, 0); + Settings.Global.SHOW_NEW_LOCKSCREEN, mAuthController.isUdfpsEnrolled() ? 1 : 0); } private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() { @@ -2621,6 +2625,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean wasPluggedIn = old.isPluggedIn(); final boolean stateChangedWhilePluggedIn = wasPluggedIn && nowPluggedIn && (old.status != current.status); + final boolean nowPresent = current.present; + final boolean wasPresent = old.present; // change in plug state is always interesting if (wasPluggedIn != nowPluggedIn || stateChangedWhilePluggedIn) { @@ -2637,6 +2643,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return true; } + // Battery either showed up or disappeared + if (wasPresent != nowPresent) { + return true; + } + return false; } diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 521bb8d58c2b..caaee5fd3f37 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -31,6 +31,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; import android.provider.Settings; @@ -91,12 +92,15 @@ public class BatteryMeterView extends LinearLayout implements private int mTextColor; private int mLevel; private int mShowPercentMode = MODE_DEFAULT; - private boolean mForceShowPercent; private boolean mShowPercentAvailable; // Some places may need to show the battery conditionally, and not obey the tuner private boolean mIgnoreTunerUpdates; private boolean mIsSubscribedForTunerUpdates; private boolean mCharging; + // Error state where we know nothing about the current battery state + private boolean mBatteryStateUnknown; + // Lazily-loaded since this is expected to be a rare-if-ever state + private Drawable mUnknownStateDrawable; private DualToneHandler mDualToneHandler; private int mUser; @@ -341,6 +345,11 @@ public class BatteryMeterView extends LinearLayout implements } private void updatePercentText() { + if (mBatteryStateUnknown) { + setContentDescription(getContext().getString(R.string.accessibility_battery_unknown)); + return; + } + if (mBatteryController == null) { return; } @@ -381,9 +390,13 @@ public class BatteryMeterView extends LinearLayout implements final boolean systemSetting = 0 != whitelistIpcs(() -> Settings.System .getIntForUser(getContext().getContentResolver(), SHOW_BATTERY_PERCENT, 0, mUser)); + boolean shouldShow = + (mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF) + || mShowPercentMode == MODE_ON + || mShowPercentMode == MODE_ESTIMATE; + shouldShow = shouldShow && !mBatteryStateUnknown; - if ((mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF) - || mShowPercentMode == MODE_ON || mShowPercentMode == MODE_ESTIMATE) { + if (shouldShow) { if (!showing) { mBatteryPercentView = loadPercentView(); if (mPercentageStyleId != 0) { // Only set if specified as attribute @@ -409,6 +422,32 @@ public class BatteryMeterView extends LinearLayout implements scaleBatteryMeterViews(); } + private Drawable getUnknownStateDrawable() { + if (mUnknownStateDrawable == null) { + mUnknownStateDrawable = mContext.getDrawable(R.drawable.ic_battery_unknown); + mUnknownStateDrawable.setTint(mTextColor); + } + + return mUnknownStateDrawable; + } + + @Override + public void onBatteryUnknownStateChanged(boolean isUnknown) { + if (mBatteryStateUnknown == isUnknown) { + return; + } + + mBatteryStateUnknown = isUnknown; + + if (mBatteryStateUnknown) { + mBatteryIconView.setImageDrawable(getUnknownStateDrawable()); + } else { + mBatteryIconView.setImageDrawable(mDrawable); + } + + updateShowPercent(); + } + /** * Looks up the scale factor for status bar icons and scales the battery view by that amount. */ @@ -449,6 +488,10 @@ public class BatteryMeterView extends LinearLayout implements if (mBatteryPercentView != null) { mBatteryPercentView.setTextColor(singleToneColor); } + + if (mUnknownStateDrawable != null) { + mUnknownStateDrawable.setTint(singleToneColor); + } } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -458,8 +501,8 @@ public class BatteryMeterView extends LinearLayout implements pw.println(" mDrawable.getPowerSave: " + powerSave); pw.println(" mBatteryPercentView.getText(): " + percent); pw.println(" mTextColor: #" + Integer.toHexString(mTextColor)); + pw.println(" mBatteryStateUnknown: " + mBatteryStateUnknown); pw.println(" mLevel: " + mLevel); - pw.println(" mForceShowPercent: " + mForceShowPercent); } private final class SettingObserver extends ContentObserver { diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 4f9d84de36a2..d8e94bb45b75 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -16,6 +16,7 @@ package com.android.systemui; +import android.app.ActivityThread; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Resources; @@ -85,8 +86,10 @@ public class SystemUIFactory { @VisibleForTesting public void init(Context context, boolean fromTest) throws ExecutionException, InterruptedException { + // Only initialize components for the main system ui process running as the primary user final boolean initializeComponents = !fromTest - && android.os.Process.myUserHandle().isSystem(); + && android.os.Process.myUserHandle().isSystem() + && ActivityThread.currentProcessName().equals(ActivityThread.currentPackageName()); mRootComponent = buildGlobalRootComponent(context); // Stand up WMComponent mWMComponent = mRootComponent.getWMComponentBuilder().build(); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java index 708002d5b946..1f41038c260f 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java @@ -32,6 +32,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpHandler; import com.android.systemui.dump.LogBufferFreezer; import com.android.systemui.dump.SystemUIAuxiliaryDumpService; +import com.android.systemui.statusbar.policy.BatteryStateNotifier; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -44,18 +45,21 @@ public class SystemUIService extends Service { private final DumpHandler mDumpHandler; private final BroadcastDispatcher mBroadcastDispatcher; private final LogBufferFreezer mLogBufferFreezer; + private final BatteryStateNotifier mBatteryStateNotifier; @Inject public SystemUIService( @Main Handler mainHandler, DumpHandler dumpHandler, BroadcastDispatcher broadcastDispatcher, - LogBufferFreezer logBufferFreezer) { + LogBufferFreezer logBufferFreezer, + BatteryStateNotifier batteryStateNotifier) { super(); mMainHandler = mainHandler; mDumpHandler = dumpHandler; mBroadcastDispatcher = broadcastDispatcher; mLogBufferFreezer = logBufferFreezer; + mBatteryStateNotifier = batteryStateNotifier; } @Override @@ -68,6 +72,11 @@ public class SystemUIService extends Service { // Finish initializing dump logic mLogBufferFreezer.attach(mBroadcastDispatcher); + // If configured, set up a battery notification + if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) { + mBatteryStateNotifier.startListening(); + } + // For debugging RescueParty if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) { throw new RuntimeException(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 874c73baf146..c72bc2543b36 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -29,6 +29,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; +import android.graphics.RectF; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.IBiometricSysuiReceiver; @@ -42,6 +43,7 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.util.Log; +import android.view.MotionEvent; import android.view.WindowManager; import com.android.internal.R; @@ -243,16 +245,25 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } /** + * @return where the UDFPS exists on the screen in pixels. + */ + public RectF getUdfpsRegion() { + return mUdfpsController == null ? null : mUdfpsController.getSensorLocation(); + } + + /** * Requests fingerprint scan. * * @param screenX X position of long press * @param screenY Y position of long press + * @param major length of the major axis. See {@link MotionEvent#AXIS_TOOL_MAJOR}. + * @param minor length of the minor axis. See {@link MotionEvent#AXIS_TOOL_MINOR}. */ - public void onAodInterrupt(int screenX, int screenY) { + public void onAodInterrupt(int screenX, int screenY, float major, float minor) { if (mUdfpsController == null) { return; } - mUdfpsController.onAodInterrupt(screenX, screenY); + mUdfpsController.onAodInterrupt(screenX, screenY, major, minor); } /** @@ -475,7 +486,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, /** * Whether the current user has a UDFP enrolled. */ - public boolean hasUdfpsEnrolled() { + public boolean isUdfpsEnrolled() { // TODO: (b/171392825) right now only checks whether the UDFPS sensor exists on this device // but not whether user has enrolled or not return mUdfpsController != null; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 3c2e00869ab8..a4b407d2785d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -25,6 +25,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.PixelFormat; import android.graphics.Point; +import android.graphics.RectF; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IUdfpsOverlayController; @@ -244,6 +245,13 @@ class UdfpsController implements DozeReceiver { mView.dozeTimeTick(); } + /** + * @return where the UDFPS exists on the screen in pixels. + */ + public RectF getSensorLocation() { + return mView.getSensorRect(); + } + private void setShowOverlay(boolean show) { if (show == mIsOverlayRequested) { return; @@ -337,7 +345,7 @@ class UdfpsController implements DozeReceiver { * This is intented to be called in response to a sensor that triggers an AOD interrupt for the * fingerprint sensor. */ - void onAodInterrupt(int screenX, int screenY) { + void onAodInterrupt(int screenX, int screenY, float major, float minor) { if (mIsAodInterruptActive) { return; } @@ -348,7 +356,7 @@ class UdfpsController implements DozeReceiver { mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelAodInterrupt, AOD_INTERRUPT_TIMEOUT_MILLIS); // using a hard-coded value for major and minor until it is available from the sensor - onFingerDown(screenX, screenY, 13.0f, 13.0f); + onFingerDown(screenX, screenY, minor, major); } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 0ed3bda40c4b..7edcf66196e4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -196,6 +196,10 @@ public class UdfpsView extends View implements DozeReceiver, canvas.restore(); } + RectF getSensorRect() { + return new RectF(mSensorRect); + } + void setHbmSupported(boolean hbmSupported) { mHbmSupported = hbmSupported; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 8220835ac509..f07e5afdd887 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -156,7 +156,7 @@ public class DozeSensors { findSensorWithType(config.udfpsLongPressSensorType()), "doze_pulse_on_auth", true /* settingDef */, - authController.hasUdfpsEnrolled() /* configured */, + authController.isUdfpsEnrolled() /* configured */, DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true /* reports touch coordinates */, true /* touchscreen */, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index c581e85ec444..58e49f896931 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -131,7 +131,10 @@ public class DozeTriggers implements DozeMachine.Part { DOZING_UPDATE_SENSOR_WAKE_LOCKSCREEN(440), @UiEvent(doc = "Dozing updated because sensor was tapped.") - DOZING_UPDATE_SENSOR_TAP(441); + DOZING_UPDATE_SENSOR_TAP(441), + + @UiEvent(doc = "Dozing updated because on display auth was triggered from AOD.") + DOZING_UPDATE_AUTH_TRIGGERED(442); private final int mId; @@ -155,6 +158,7 @@ public class DozeTriggers implements DozeMachine.Part { case 7: return DOZING_UPDATE_SENSOR_WAKEUP; case 8: return DOZING_UPDATE_SENSOR_WAKE_LOCKSCREEN; case 9: return DOZING_UPDATE_SENSOR_TAP; + case 10: return DOZING_UPDATE_AUTH_TRIGGERED; default: return null; } } @@ -289,7 +293,8 @@ public class DozeTriggers implements DozeMachine.Part { requestPulse(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true, null); // Since the gesture won't be received by the UDFPS view, manually inject an // event. - mAuthController.onAodInterrupt((int) screenX, (int) screenY); + mAuthController.onAodInterrupt((int) screenX, (int) screenY, + rawValues[2] /* major */, rawValues[3] /* minor */); } else { mDozeHost.extendPulse(pulseReason); } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 6f6ee4c8091d..c6ed9c096544 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -641,7 +641,8 @@ class MediaDataManager( // Move to resume key (aka package name) if that key doesn't already exist. val resumeAction = getResumeMediaAction(removed.resumeAction!!) val updated = removed.copy(token = null, actions = listOf(resumeAction), - actionsToShowInCompact = listOf(0), active = false, resumption = true) + actionsToShowInCompact = listOf(0), active = false, resumption = true, + isClearable = true) val pkg = removed.packageName val migrate = mediaEntries.put(pkg, updated) == null // Notify listeners of "new" controls when migrating or removed and update when not diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index 80928d6da978..451bd42bd053 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -16,10 +16,12 @@ package com.android.systemui.media.dialog; +import android.app.Notification; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.media.MediaMetadata; import android.media.MediaRoute2Info; import android.media.RoutingSessionInfo; @@ -221,9 +223,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { } for (NotificationEntry entry : mNotificationEntryManager.getActiveNotificationsForCurrentUser()) { - if (entry.getSbn().getNotification().hasMediaSession() + final Notification notification = entry.getSbn().getNotification(); + if (notification.hasMediaSession() && TextUtils.equals(entry.getSbn().getPackageName(), mPackageName)) { - return IconCompat.createFromIcon(entry.getSbn().getNotification().getLargeIcon()); + final Icon icon = notification.getLargeIcon(); + if (icon == null) { + break; + } + return IconCompat.createFromIcon(icon); } } return null; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 8e0e4ac7c8ef..a35151068bee 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -71,8 +71,7 @@ public class QSContainerImpl extends FrameLayout { private int mSideMargins; private boolean mQsDisabled; - private int mContentPaddingStart = -1; - private int mContentPaddingEnd = -1; + private int mContentPadding = -1; private boolean mAnimateBottomOnNextLayout; public QSContainerImpl(Context context, AttributeSet attrs) { @@ -206,10 +205,9 @@ public class QSContainerImpl extends FrameLayout { mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings); int padding = getResources().getDimensionPixelSize( - com.android.internal.R.dimen.notification_shade_content_margin_horizontal); - boolean marginsChanged = padding != mContentPaddingStart || padding != mContentPaddingEnd; - mContentPaddingStart = padding; - mContentPaddingEnd = padding; + R.dimen.notification_shade_content_margin_horizontal); + boolean marginsChanged = padding != mContentPadding; + mContentPadding = padding; if (marginsChanged) { updatePaddingsAndMargins(); } @@ -290,19 +288,19 @@ public class QSContainerImpl extends FrameLayout { lp.leftMargin = mSideMargins; if (view == mQSPanelContainer) { // QS panel lays out some of its content full width - mQSPanel.setContentMargins(mContentPaddingStart, mContentPaddingEnd); + mQSPanel.setContentMargins(mContentPadding, mContentPadding); Pair<Integer, Integer> margins = mQSPanel.getVisualSideMargins(); // Apply paddings based on QSPanel mQSCustomizer.setContentPaddings(margins.first, margins.second); } else if (view == mHeader) { // The header contains the QQS panel which needs to have special padding, to // visually align them. - mHeader.setContentMargins(mContentPaddingStart, mContentPaddingEnd); + mHeader.setContentMargins(mContentPadding, mContentPadding); } else { view.setPaddingRelative( - mContentPaddingStart, + mContentPadding, view.getPaddingTop(), - mContentPaddingEnd, + mContentPadding, view.getPaddingBottom()); } } diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index e6f43c1ff1d2..45564b0bfa6b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -30,6 +30,8 @@ import android.graphics.Bitmap; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.media.MediaCodecInfo; +import android.media.MediaCodecList; +import android.media.MediaFormat; import android.media.MediaMuxer; import android.media.MediaRecorder; import android.media.ThumbnailUtils; @@ -124,17 +126,19 @@ public class ScreenMediaRecorder { DisplayMetrics metrics = new DisplayMetrics(); WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getRealMetrics(metrics); - int screenWidth = metrics.widthPixels; - int screenHeight = metrics.heightPixels; - int refereshRate = (int) wm.getDefaultDisplay().getRefreshRate(); - int vidBitRate = screenHeight * screenWidth * refereshRate / VIDEO_FRAME_RATE + int refreshRate = (int) wm.getDefaultDisplay().getRefreshRate(); + int[] dimens = getSupportedSize(metrics.widthPixels, metrics.heightPixels, refreshRate); + int width = dimens[0]; + int height = dimens[1]; + refreshRate = dimens[2]; + int vidBitRate = width * height * refreshRate / VIDEO_FRAME_RATE * VIDEO_FRAME_RATE_TO_RESOLUTION_RATIO; mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setVideoEncodingProfileLevel( MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, MediaCodecInfo.CodecProfileLevel.AVCLevel3); - mMediaRecorder.setVideoSize(screenWidth, screenHeight); - mMediaRecorder.setVideoFrameRate(refereshRate); + mMediaRecorder.setVideoSize(width, height); + mMediaRecorder.setVideoFrameRate(refreshRate); mMediaRecorder.setVideoEncodingBitRate(vidBitRate); mMediaRecorder.setMaxDuration(MAX_DURATION_MS); mMediaRecorder.setMaxFileSize(MAX_FILESIZE_BYTES); @@ -153,8 +157,8 @@ public class ScreenMediaRecorder { mInputSurface = mMediaRecorder.getSurface(); mVirtualDisplay = mMediaProjection.createVirtualDisplay( "Recording Display", - screenWidth, - screenHeight, + width, + height, metrics.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mInputSurface, @@ -173,6 +177,84 @@ public class ScreenMediaRecorder { } /** + * Find the highest supported screen resolution and refresh rate for the given dimensions on + * this device, up to actual size and given rate. + * If possible this will return the same values as given, but values may be smaller on some + * devices. + * + * @param screenWidth Actual pixel width of screen + * @param screenHeight Actual pixel height of screen + * @param refreshRate Desired refresh rate + * @return array with supported width, height, and refresh rate + */ + private int[] getSupportedSize(int screenWidth, int screenHeight, int refreshRate) { + double maxScale = 0; + + MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); + MediaCodecInfo.VideoCapabilities maxInfo = null; + for (MediaCodecInfo codec : codecList.getCodecInfos()) { + String videoType = MediaFormat.MIMETYPE_VIDEO_AVC; + String[] types = codec.getSupportedTypes(); + for (String t : types) { + if (!t.equalsIgnoreCase(videoType)) { + continue; + } + MediaCodecInfo.CodecCapabilities capabilities = + codec.getCapabilitiesForType(videoType); + if (capabilities != null && capabilities.getVideoCapabilities() != null) { + MediaCodecInfo.VideoCapabilities vc = capabilities.getVideoCapabilities(); + + int width = vc.getSupportedWidths().getUpper(); + int height = vc.getSupportedHeights().getUpper(); + + if (width >= screenWidth && height >= screenHeight + && vc.isSizeSupported(screenWidth, screenHeight)) { + + // Desired size is supported, now get the rate + int maxRate = vc.getSupportedFrameRatesFor(screenWidth, screenHeight) + .getUpper().intValue(); + + if (maxRate < refreshRate) { + refreshRate = maxRate; + } + Log.d(TAG, "Screen size supported at rate " + refreshRate); + return new int[]{screenWidth, screenHeight, refreshRate}; + } + + // Otherwise, continue searching + double scale = Math.min(((double) width / screenWidth), + ((double) height / screenHeight)); + if (scale > maxScale) { + maxScale = scale; + maxInfo = vc; + } + } + } + } + + // Resize for max supported size + int scaledWidth = (int) (screenWidth * maxScale); + int scaledHeight = (int) (screenHeight * maxScale); + if (scaledWidth % maxInfo.getWidthAlignment() != 0) { + scaledWidth -= (scaledWidth % maxInfo.getWidthAlignment()); + } + if (scaledHeight % maxInfo.getHeightAlignment() != 0) { + scaledHeight -= (scaledHeight % maxInfo.getHeightAlignment()); + } + + // Find max supported rate for size + int maxRate = maxInfo.getSupportedFrameRatesFor(scaledWidth, scaledHeight) + .getUpper().intValue(); + if (maxRate < refreshRate) { + refreshRate = maxRate; + } + + Log.d(TAG, "Resized by " + maxScale + ": " + scaledWidth + ", " + scaledHeight + + ", " + refreshRate); + return new int[]{scaledWidth, scaledHeight, refreshRate}; + } + + /** * Start screen recording */ void start() throws IOException, RemoteException, IllegalStateException { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index a59ff38896dc..a252a7a12274 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -121,6 +121,7 @@ public class KeyguardIndicationController implements StateListener, private int mChargingSpeed; private int mChargingWattage; private int mBatteryLevel; + private boolean mBatteryPresent = true; private long mChargingTimeRemaining; private float mDisclosureMaxAlpha; private String mMessageToShowOnScreenOn; @@ -389,86 +390,103 @@ public class KeyguardIndicationController implements StateListener, mWakeLock.setAcquired(false); } - if (mVisible) { - // Walk down a precedence-ordered list of what indication - // should be shown based on user or device state - if (mDozing) { - // When dozing we ignore any text color and use white instead, because - // colors can be hard to read in low brightness. - mTextView.setTextColor(Color.WHITE); - if (!TextUtils.isEmpty(mTransientIndication)) { - mTextView.switchIndication(mTransientIndication); - } else if (!TextUtils.isEmpty(mAlignmentIndication)) { - mTextView.switchIndication(mAlignmentIndication); - mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); - } else if (mPowerPluggedIn) { - String indication = computePowerIndication(); - if (animate) { - animateText(mTextView, indication); - } else { - mTextView.switchIndication(indication); - } - } else { - String percentage = NumberFormat.getPercentInstance() - .format(mBatteryLevel / 100f); - mTextView.switchIndication(percentage); - } - return; - } + if (!mVisible) { + return; + } - int userId = KeyguardUpdateMonitor.getCurrentUser(); - String trustGrantedIndication = getTrustGrantedIndication(); - String trustManagedIndication = getTrustManagedIndication(); + // A few places might need to hide the indication, so always start by making it visible + mIndicationArea.setVisibility(View.VISIBLE); - String powerIndication = null; - if (mPowerPluggedIn) { - powerIndication = computePowerIndication(); - } - - boolean isError = false; - if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { - mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked); - } else if (!TextUtils.isEmpty(mTransientIndication)) { - if (powerIndication != null && !mTransientIndication.equals(powerIndication)) { - String indication = mContext.getResources().getString( - R.string.keyguard_indication_trust_unlocked_plugged_in, - mTransientIndication, powerIndication); - mTextView.switchIndication(indication); - } else { - mTextView.switchIndication(mTransientIndication); - } - isError = mTransientTextIsError; - } else if (!TextUtils.isEmpty(trustGrantedIndication) - && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { - if (powerIndication != null) { - String indication = mContext.getResources().getString( - R.string.keyguard_indication_trust_unlocked_plugged_in, - trustGrantedIndication, powerIndication); - mTextView.switchIndication(indication); - } else { - mTextView.switchIndication(trustGrantedIndication); - } + // Walk down a precedence-ordered list of what indication + // should be shown based on user or device state + if (mDozing) { + // When dozing we ignore any text color and use white instead, because + // colors can be hard to read in low brightness. + mTextView.setTextColor(Color.WHITE); + if (!TextUtils.isEmpty(mTransientIndication)) { + mTextView.switchIndication(mTransientIndication); + } else if (!mBatteryPresent) { + // If there is no battery detected, hide the indication and bail + mIndicationArea.setVisibility(View.GONE); } else if (!TextUtils.isEmpty(mAlignmentIndication)) { mTextView.switchIndication(mAlignmentIndication); - isError = true; + mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); } else if (mPowerPluggedIn) { - if (DEBUG_CHARGING_SPEED) { - powerIndication += ", " + (mChargingWattage / 1000) + " mW"; - } + String indication = computePowerIndication(); if (animate) { - animateText(mTextView, powerIndication); + animateText(mTextView, indication); } else { - mTextView.switchIndication(powerIndication); + mTextView.switchIndication(indication); } - } else if (!TextUtils.isEmpty(trustManagedIndication) - && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) - && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { - mTextView.switchIndication(trustManagedIndication); } else { - mTextView.switchIndication(mRestingIndication); + String percentage = NumberFormat.getPercentInstance() + .format(mBatteryLevel / 100f); + mTextView.switchIndication(percentage); } - mTextView.setTextColor(isError ? Utils.getColorError(mContext) - : mInitialTextColorState); + return; + } + + int userId = KeyguardUpdateMonitor.getCurrentUser(); + String trustGrantedIndication = getTrustGrantedIndication(); + String trustManagedIndication = getTrustManagedIndication(); + + String powerIndication = null; + if (mPowerPluggedIn) { + powerIndication = computePowerIndication(); + } + + // Some cases here might need to hide the indication (if the battery is not present) + boolean hideIndication = false; + boolean isError = false; + if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { + mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked); + } else if (!TextUtils.isEmpty(mTransientIndication)) { + if (powerIndication != null && !mTransientIndication.equals(powerIndication)) { + String indication = mContext.getResources().getString( + R.string.keyguard_indication_trust_unlocked_plugged_in, + mTransientIndication, powerIndication); + mTextView.switchIndication(indication); + hideIndication = !mBatteryPresent; + } else { + mTextView.switchIndication(mTransientIndication); + } + isError = mTransientTextIsError; + } else if (!TextUtils.isEmpty(trustGrantedIndication) + && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + if (powerIndication != null) { + String indication = mContext.getResources().getString( + R.string.keyguard_indication_trust_unlocked_plugged_in, + trustGrantedIndication, powerIndication); + mTextView.switchIndication(indication); + hideIndication = !mBatteryPresent; + } else { + mTextView.switchIndication(trustGrantedIndication); + } + } else if (!TextUtils.isEmpty(mAlignmentIndication)) { + mTextView.switchIndication(mAlignmentIndication); + isError = true; + hideIndication = !mBatteryPresent; + } else if (mPowerPluggedIn) { + if (DEBUG_CHARGING_SPEED) { + powerIndication += ", " + (mChargingWattage / 1000) + " mW"; + } + if (animate) { + animateText(mTextView, powerIndication); + } else { + mTextView.switchIndication(powerIndication); + } + hideIndication = !mBatteryPresent; + } else if (!TextUtils.isEmpty(trustManagedIndication) + && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) + && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + mTextView.switchIndication(trustManagedIndication); + } else { + mTextView.switchIndication(mRestingIndication); + } + mTextView.setTextColor(isError ? Utils.getColorError(mContext) + : mInitialTextColorState); + if (hideIndication) { + mIndicationArea.setVisibility(View.GONE); } } @@ -647,6 +665,7 @@ public class KeyguardIndicationController implements StateListener, pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn); pw.println(" mDozing: " + mDozing); pw.println(" mBatteryLevel: " + mBatteryLevel); + pw.println(" mBatteryPresent: " + mBatteryPresent); pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText())); pw.println(" computePowerIndication(): " + computePowerIndication()); } @@ -685,6 +704,7 @@ public class KeyguardIndicationController implements StateListener, mChargingWattage = status.maxChargingWattage; mChargingSpeed = status.getChargingSpeed(mContext); mBatteryLevel = status.level; + mBatteryPresent = status.present; try { mChargingTimeRemaining = mPowerPluggedIn ? mBatteryInfo.computeChargeTimeRemaining() : -1; 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 5cee5bd0cc4e..2a2a0b14a2d2 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 @@ -835,7 +835,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (!mShouldDrawNotificationBackground) { return; } - + final boolean clearUndershelf = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.SHOW_NEW_NOTIF_DISMISS, 0 /* show background by default */) == 1; + if (clearUndershelf) { + mBackgroundPaint.setColor(Color.TRANSPARENT); + invalidate(); + return; + } // Interpolate between semi-transparent notification panel background color // and white AOD separator. float colorInterpolation = MathUtils.smoothStep(0.4f /* start */, 1f /* end */, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index ef4108b9762d..bd228933c8a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -86,9 +86,14 @@ public class KeyguardClockPositionAlgorithm { private int mMaxShadeBottom; /** - * Minimum distance from the status bar. + * Recommended distance from the status bar without the lock icon. */ - private int mContainerTopPadding; + private int mContainerTopPaddingWithoutLockIcon; + + /** + * Recommended distance from the status bar with the lock icon. + */ + private int mContainerTopPaddingWithLockIcon; /** * @see NotificationPanelViewController#getExpandedFraction() @@ -131,24 +136,31 @@ public class KeyguardClockPositionAlgorithm { public void loadDimens(Resources res) { mClockNotificationsMargin = res.getDimensionPixelSize( R.dimen.keyguard_clock_notifications_margin); + + mContainerTopPaddingWithoutLockIcon = + res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) / 2; // Consider the lock icon when determining the minimum top padding between the status bar // and top of the clock. - mContainerTopPadding = Math.max(res.getDimensionPixelSize( - R.dimen.keyguard_clock_top_margin), - res.getDimensionPixelSize(R.dimen.keyguard_lock_height) - + res.getDimensionPixelSize(R.dimen.keyguard_lock_padding) - + res.getDimensionPixelSize(R.dimen.keyguard_clock_lock_margin)); + mContainerTopPaddingWithLockIcon = + Math.max(res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin), + res.getDimensionPixelSize(R.dimen.keyguard_lock_height) + + res.getDimensionPixelSize(R.dimen.keyguard_lock_padding) + + res.getDimensionPixelSize(R.dimen.keyguard_clock_lock_margin)); mBurnInPreventionOffsetX = res.getDimensionPixelSize( R.dimen.burn_in_prevention_offset_x); mBurnInPreventionOffsetY = res.getDimensionPixelSize( R.dimen.burn_in_prevention_offset_y); } - public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight, + /** + * Sets up algorithm values. + */ + public void setup(int statusBarMinHeight, int maxShadeBottom, int notificationStackHeight, float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount, - boolean bypassEnabled, int unlockedStackScrollerPadding) { - mMinTopMargin = minTopMargin + mContainerTopPadding; + boolean bypassEnabled, int unlockedStackScrollerPadding, boolean udfpsEnrolled) { + mMinTopMargin = statusBarMinHeight + (udfpsEnrolled ? mContainerTopPaddingWithoutLockIcon : + mContainerTopPaddingWithLockIcon); mMaxShadeBottom = maxShadeBottom; mNotificationStackHeight = notificationStackHeight; mPanelExpansion = panelExpansion; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 77ae059a0cb9..5e883bee13a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static android.view.View.GONE; + import static com.android.systemui.statusbar.phone.LockIcon.STATE_BIOMETRICS_ERROR; import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCKED; import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCK_OPEN; @@ -37,6 +39,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; @@ -74,6 +77,7 @@ public class LockscreenLockIconController { private final KeyguardStateController mKeyguardStateController; private final Resources mResources; private final HeadsUpManagerPhone mHeadsUpManagerPhone; + private final AuthController mAuthController; private boolean mKeyguardShowing; private boolean mKeyguardJustShown; private boolean mBlockUpdates; @@ -322,7 +326,8 @@ public class LockscreenLockIconController { @Nullable DockManager dockManager, KeyguardStateController keyguardStateController, @Main Resources resources, - HeadsUpManagerPhone headsUpManagerPhone) { + HeadsUpManagerPhone headsUpManagerPhone, + AuthController authController) { mLockscreenGestureLogger = lockscreenGestureLogger; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; @@ -337,6 +342,7 @@ public class LockscreenLockIconController { mKeyguardStateController = keyguardStateController; mResources = resources; mHeadsUpManagerPhone = headsUpManagerPhone; + mAuthController = authController; mKeyguardIndicationController.setLockIconController(this); } @@ -502,6 +508,11 @@ public class LockscreenLockIconController { * @return true if the visibility changed */ private boolean updateIconVisibility() { + if (mAuthController.isUdfpsEnrolled()) { + boolean changed = mLockIcon.getVisibility() == GONE; + mLockIcon.setVisibility(GONE); + return changed; + } boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked; boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance; boolean fingerprintOrBypass = mFingerprintUnlock 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 b185f48b0245..e9a7132f4a5c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -77,6 +77,7 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.DejankUtils; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.classifier.Classifier; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; @@ -263,6 +264,7 @@ public class NotificationPanelViewController extends PanelViewController { private final KeyguardBypassController mKeyguardBypassController; private final KeyguardUpdateMonitor mUpdateMonitor; private final ConversationNotificationManager mConversationNotificationManager; + private final AuthController mAuthController; private final MediaHierarchyManager mMediaHierarchyManager; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; @@ -516,7 +518,8 @@ public class NotificationPanelViewController extends PanelViewController { NotificationStackScrollLayoutController notificationStackScrollLayoutController, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, NotificationGroupManagerLegacy groupManager, - NotificationIconAreaController notificationIconAreaController) { + NotificationIconAreaController notificationIconAreaController, + AuthController authController) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager); @@ -581,6 +584,7 @@ public class NotificationPanelViewController extends PanelViewController { mLockscreenUserManager = notificationLockscreenUserManager; mEntryManager = notificationEntryManager; mConversationNotificationManager = conversationNotificationManager; + mAuthController = authController; mView.setBackgroundColor(Color.TRANSPARENT); OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener(); @@ -868,7 +872,8 @@ public class NotificationPanelViewController extends PanelViewController { - mShelfHeight / 2.0f - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(), hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount, - bypassEnabled, getUnlockedStackScrollerPadding()); + bypassEnabled, getUnlockedStackScrollerPadding(), + mAuthController.isUdfpsEnrolled()); mClockPositionAlgorithm.run(mClockPositionResult); mKeyguardStatusViewController.updatePosition( mClockPositionResult.clockX, mClockPositionResult.clockY, animateClock); @@ -908,6 +913,13 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationStackScrollLayoutController.getHeight() - minPadding - shelfSize - Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding) - mKeyguardStatusViewController.getLogoutButtonHeight(); + + if (mAuthController.isUdfpsEnrolled()) { + availableSpace = mNotificationStackScrollLayoutController.getHeight() + - minPadding - shelfSize + - (mStatusBar.getDisplayHeight() - mAuthController.getUdfpsRegion().top); + } + int count = 0; ExpandableView previousView = null; for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index 06e4731265e3..08e70a97e0ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -97,6 +97,9 @@ public interface BatteryController extends DemoMode, Dumpable, default void onPowerSaveChanged(boolean isPowerSave) { } + default void onBatteryUnknownStateChanged(boolean isUnknown) { + } + default void onReverseChanged(boolean isReverse, int level, String name) { } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index 57ac85e1e86d..d8710bf85a6c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static android.os.BatteryManager.EXTRA_PRESENT; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -75,6 +77,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC protected int mLevel; protected boolean mPluggedIn; protected boolean mCharging; + private boolean mStateUnknown = false; private boolean mCharged; protected boolean mPowerSave; private boolean mAodPowerSave; @@ -138,6 +141,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC pw.print(" mCharging="); pw.println(mCharging); pw.print(" mCharged="); pw.println(mCharged); pw.print(" mPowerSave="); pw.println(mPowerSave); + pw.print(" mStateUnknown="); pw.println(mStateUnknown); } @Override @@ -180,6 +184,13 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC mWirelessCharging = mCharging && intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) == BatteryManager.BATTERY_PLUGGED_WIRELESS; + boolean present = intent.getBooleanExtra(EXTRA_PRESENT, true); + boolean unknown = !present; + if (unknown != mStateUnknown) { + mStateUnknown = unknown; + fireBatteryUnknownStateChanged(); + } + fireBatteryLevelChanged(); } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) { updatePowerSave(); @@ -328,6 +339,15 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } } + private void fireBatteryUnknownStateChanged() { + synchronized (mChangeCallbacks) { + final int n = mChangeCallbacks.size(); + for (int i = 0; i < n; i++) { + mChangeCallbacks.get(i).onBatteryUnknownStateChanged(mStateUnknown); + } + } + } + private void firePowerSaveChanged() { synchronized (mChangeCallbacks) { final int N = mChangeCallbacks.size(); @@ -346,6 +366,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC String level = args.getString("level"); String plugged = args.getString("plugged"); String powerSave = args.getString("powersave"); + String present = args.getString("present"); if (level != null) { mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100); } @@ -356,6 +377,10 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC mPowerSave = powerSave.equals("true"); firePowerSaveChanged(); } + if (present != null) { + mStateUnknown = !present.equals("true"); + fireBatteryUnknownStateChanged(); + } fireBatteryLevelChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt new file mode 100644 index 000000000000..92e5b78f776a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.net.Uri +import com.android.systemui.R +import com.android.systemui.util.concurrency.DelayableExecutor +import javax.inject.Inject + +/** + * Listens for important battery states and sends non-dismissible system notifications if there is a + * problem + */ +class BatteryStateNotifier @Inject constructor( + val controller: BatteryController, + val noMan: NotificationManager, + val delayableExecutor: DelayableExecutor, + val context: Context +) : BatteryController.BatteryStateChangeCallback { + var stateUnknown = false + + fun startListening() { + controller.addCallback(this) + } + + fun stopListening() { + controller.removeCallback(this) + } + + override fun onBatteryUnknownStateChanged(isUnknown: Boolean) { + stateUnknown = isUnknown + if (stateUnknown) { + val channel = NotificationChannel("battery_status", "Battery status", + NotificationManager.IMPORTANCE_DEFAULT) + noMan.createNotificationChannel(channel) + + val intent = Intent(Intent.ACTION_VIEW, + Uri.parse(context.getString(R.string.config_batteryStateUnknownUrl))) + val pi = PendingIntent.getActivity(context, 0, intent, 0) + + val builder = Notification.Builder(context, channel.id) + .setAutoCancel(false) + .setContentTitle( + context.getString(R.string.battery_state_unknown_notification_title)) + .setContentText( + context.getString(R.string.battery_state_unknown_notification_text)) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setContentIntent(pi) + .setAutoCancel(true) + .setOngoing(true) + + noMan.notify(TAG, ID, builder.build()) + } else { + scheduleNotificationCancel() + } + } + + private fun scheduleNotificationCancel() { + val r = { + if (!stateUnknown) { + noMan.cancel(ID) + } + } + delayableExecutor.executeDelayed(r, DELAY_MILLIS) + } +} + +private const val TAG = "BatteryStateNotifier" +private const val ID = 666 +private const val DELAY_MILLIS: Long = 4 * 60 * 60 * 1000
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 6b991f8ec521..49be648755c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -427,7 +427,8 @@ public class MobileSignalController extends SignalController< return false; } if (isCdma()) { - return mPhone.getCdmaEnhancedRoamingIndicatorIconIndex() != TelephonyManager.ERI_OFF; + return mPhone.getCdmaEnhancedRoamingIndicatorDisplayNumber() + != TelephonyManager.ERI_OFF; } else { return mServiceState != null && mServiceState.getRoaming(); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index b7efa7c4c5ac..bbabaf429859 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -22,6 +22,7 @@ import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipBoundsState; @@ -106,9 +107,10 @@ public abstract class TvPipModule { PipBoundsHandler pipBoundsHandler, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, DisplayController displayController, - PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) { + PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, + SystemWindows systemWindows) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler, - pipSurfaceTransactionHelper, splitScreenOptional, displayController, - pipUiEventLogger, shellTaskOrganizer); + null /* menuActivityController */, pipSurfaceTransactionHelper, splitScreenOptional, + displayController, pipUiEventLogger, shellTaskOrganizer, systemWindows); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index b6fbd589ae2c..1ca04af393ff 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -106,9 +106,9 @@ public class WMShellModule { @WMSingleton @Provides - static PipMenuActivityController providePipMenuActivityController(Context context, - PipMediaController pipMediaController, PipTaskOrganizer pipTaskOrganizer) { - return new PipMenuActivityController(context, pipMediaController, pipTaskOrganizer); + static PipMenuActivityController providesPipMenuActivityController(Context context, + PipMediaController pipMediaController, SystemWindows systemWindows) { + return new PipMenuActivityController(context, pipMediaController, systemWindows); } @WMSingleton @@ -128,11 +128,13 @@ public class WMShellModule { static PipTaskOrganizer providePipTaskOrganizer(Context context, PipBoundsState pipBoundsState, PipBoundsHandler pipBoundsHandler, + PipMenuActivityController menuActivityController, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, DisplayController displayController, - PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) { + PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, + SystemWindows systemWindows) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler, - pipSurfaceTransactionHelper, splitScreenOptional, displayController, - pipUiEventLogger, shellTaskOrganizer); + menuActivityController, pipSurfaceTransactionHelper, splitScreenOptional, + displayController, pipUiEventLogger, shellTaskOrganizer, systemWindows); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index e6479ddaedb3..caab2abd7ec6 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -79,6 +79,7 @@ import com.android.internal.widget.ILockSettings; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated; import com.android.systemui.SysuiTestCase; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -156,6 +157,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private LiveData<Integer> mRingerModeLiveData; @Mock private StatusBarStateController mStatusBarStateController; + @Mock + private AuthController mAuthController; // Direct executor private Executor mBackgroundExecutor = Runnable::run; private TestableLooper mTestableLooper; @@ -198,6 +201,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { when(mTelephonyManager.getServiceStateForSubscriber(anyInt())) .thenReturn(new ServiceState()); when(mLockPatternUtils.getLockSettings()).thenReturn(mLockSettings); + when(mAuthController.isUdfpsEnrolled()).thenReturn(false); mSpiedContext.addMockSystemService(TrustManager.class, mTrustManager); mSpiedContext.addMockSystemService(FingerprintManager.class, mFingerprintManager); mSpiedContext.addMockSystemService(BiometricManager.class, mBiometricManager); @@ -796,7 +800,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { TestableLooper.get(KeyguardUpdateMonitorTest.this).getLooper(), mBroadcastDispatcher, mDumpManager, mRingerModeTracker, mBackgroundExecutor, - mStatusBarStateController, mLockPatternUtils); + mStatusBarStateController, mLockPatternUtils, + mAuthController); setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java index 33b1d94df80e..6978ef4fb9ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -92,7 +92,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mInstrumentation = InstrumentationRegistry.getInstrumentation(); - mWaitingAnimationPeriod = ANIMATION_DURATION_MS + 50; + mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS; mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2; mController = new SpyWindowMagnificationController(mContext, mHandler, mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 0186d7394ecb..42bb005b5144 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -494,8 +494,9 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testOnAodInterrupt() { final int pos = 10; - mAuthController.onAodInterrupt(pos, pos); - verify(mUdfpsController).onAodInterrupt(eq(pos), eq(pos)); + final float majorMinor = 5f; + mAuthController.onAodInterrupt(pos, pos, majorMinor, majorMinor); + verify(mUdfpsController).onAodInterrupt(eq(pos), eq(pos), eq(majorMinor), eq(majorMinor)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index a95396cccd66..82ffd46f7164 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -234,10 +234,10 @@ public class UdfpsControllerTest extends SysuiTestCase { mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID); mFgExecutor.runAllReady(); // WHEN fingerprint is requested because of AOD interrupt - mUdfpsController.onAodInterrupt(0, 0); + mUdfpsController.onAodInterrupt(0, 0, 2f, 3f); // THEN the event is passed to the FingerprintManager verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), - eq(0), anyFloat(), anyFloat()); + eq(0), eq(3f) /* minor */, eq(2f) /* major */); // AND the scrim and dot is shown verify(mUdfpsView).showScrimAndDot(); } @@ -247,7 +247,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN AOD interrupt mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID); mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0); + mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); // WHEN it is cancelled mUdfpsController.onCancelAodInterrupt(); // THEN the scrim and dot is hidden @@ -259,7 +259,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN AOD interrupt mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID); mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0); + mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); // WHEN it times out mFgExecutor.advanceClockToNext(); mFgExecutor.runAllReady(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 6d87cc3c3e38..4bbba56395f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -194,10 +194,13 @@ public class DozeTriggersTest extends SysuiTestCase { public void testOnSensor_Fingerprint() { final int screenX = 100; final int screenY = 100; + final float minor = 2f; + final float major = 3f; final int reason = DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; - mTriggers.onSensor(reason, screenX, screenY, null); + float[] rawValues = new float[]{screenX, screenY, minor, major}; + mTriggers.onSensor(reason, screenX, screenY, rawValues); verify(mHost).extendPulse(reason); - verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY)); + verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY), eq(minor), eq(major)); } private void waitForSensorManager() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java index 6ceac131ed4b..0d352c1b42d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java @@ -447,6 +447,25 @@ public class MediaOutputControllerTest extends SysuiTestCase { } @Test + public void getNotificationLargeIcon_withoutLargeIcon_returnsNull() { + final List<NotificationEntry> entryList = new ArrayList<>(); + final NotificationEntry entry = mock(NotificationEntry.class); + final StatusBarNotification sbn = mock(StatusBarNotification.class); + final Notification notification = mock(Notification.class); + entryList.add(entry); + + when(mNotificationEntryManager.getActiveNotificationsForCurrentUser()) + .thenReturn(entryList); + when(entry.getSbn()).thenReturn(sbn); + when(sbn.getNotification()).thenReturn(notification); + when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(notification.hasMediaSession()).thenReturn(true); + when(notification.getLargeIcon()).thenReturn(null); + + assertThat(mMediaOutputController.getNotificationIcon()).isNull(); + } + + @Test public void getNotificationLargeIcon_withPackageNameAndMediaSession_returnsIconCompat() { final List<NotificationEntry> entryList = new ArrayList<>(); final NotificationEntry entry = mock(NotificationEntry.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index e0d268127d90..f9b14ba7cd31 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -494,7 +494,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, 80 /* level */, BatteryManager.BATTERY_PLUGGED_WIRELESS, 100 /* health */, - 0 /* maxChargingWattage */); + 0 /* maxChargingWattage */, true /* present */); mController.getKeyguardCallback().onRefreshBatteryInfo(status); verify(mIBatteryStats).computeChargeTimeRemaining(); @@ -506,7 +506,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, 80 /* level */, 0 /* plugged */, 100 /* health */, - 0 /* maxChargingWattage */); + 0 /* maxChargingWattage */, true /* present */); mController.getKeyguardCallback().onRefreshBatteryInfo(status); verify(mIBatteryStats, never()).computeChargeTimeRemaining(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index 2042faba2b3a..83ef87a1066c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -384,7 +384,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight, mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mPreferredClockY, mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */, - 0 /* unlockedStackScrollerPadding */); + 0 /* unlockedStackScrollerPadding */, false /* udfpsEnrolled */); mClockPositionAlgorithm.run(mClockPosition); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java index 06d0331543da..72a0258e148a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java @@ -32,6 +32,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -80,6 +81,8 @@ public class LockscreenIconControllerTest extends SysuiTestCase { private Resources mResources; @Mock private HeadsUpManagerPhone mHeadsUpManagerPhone; + @Mock + private AuthController mAuthController; private LockscreenLockIconController mLockIconController; private OnAttachStateChangeListener mOnAttachStateChangeListener; @@ -89,13 +92,14 @@ public class LockscreenIconControllerTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); + when(mAuthController.isUdfpsEnrolled()).thenReturn(false); when(mLockIcon.getContext()).thenReturn(mContext); mLockIconController = new LockscreenLockIconController( mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils, mShadeController, mAccessibilityController, mKeyguardIndicationController, mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator, mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources, - mHeadsUpManagerPhone); + mHeadsUpManagerPhone, mAuthController); ArgumentCaptor<OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor = ArgumentCaptor.forClass(OnAttachStateChangeListener.class); 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 cb56f1fbc054..3b123f65d886 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 @@ -58,6 +58,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.doze.DozeLog; import com.android.systemui.media.MediaHierarchyManager; @@ -194,6 +195,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { private KeyguardStatusViewController mKeyguardStatusViewController; @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; + @Mock + private AuthController mAuthController; private NotificationPanelViewController mNotificationPanelViewController; private View.AccessibilityDelegate mAccessibiltyDelegate; @@ -201,6 +204,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Before public void setup() { MockitoAnnotations.initMocks(this); + when(mAuthController.isUdfpsEnrolled()).thenReturn(false); when(mHeadsUpCallback.getContext()).thenReturn(mContext); when(mView.getResources()).thenReturn(mResources); when(mResources.getConfiguration()).thenReturn(mConfiguration); @@ -267,7 +271,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { mNotificationStackScrollLayoutController, mKeyguardStatusViewComponentFactory, mGroupManager, - mNotificationAreaController); + mNotificationAreaController, + mAuthController); mNotificationPanelViewController.initDependencies( mStatusBar, mNotificationShelfController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt new file mode 100644 index 000000000000..dcd57f137d71 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import android.app.NotificationManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper + +import androidx.test.filters.SmallTest + +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock + +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +private fun <T> anyObject(): T { + return Mockito.anyObject<T>() +} + +@RunWith(AndroidTestingRunner::class) +@RunWithLooper() +@SmallTest +class BatteryStateNotifierTest : SysuiTestCase() { + @Mock private lateinit var batteryController: BatteryController + @Mock private lateinit var noMan: NotificationManager + + private val clock = FakeSystemClock() + private val executor = FakeExecutor(clock) + + private lateinit var notifier: BatteryStateNotifier + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + notifier = BatteryStateNotifier(batteryController, noMan, executor, context) + notifier.startListening() + + context.ensureTestableResources() + } + + @Test + fun testNotifyWhenStateUnknown() { + notifier.onBatteryUnknownStateChanged(true) + verify(noMan).notify(anyString(), anyInt(), anyObject()) + } + + @Test + fun testCancelAfterDelay() { + notifier.onBatteryUnknownStateChanged(true) + notifier.onBatteryUnknownStateChanged(false) + + clock.advanceTime(DELAY_MILLIS + 1) + verify(noMan).cancel(anyInt()) + } +} + +// From BatteryStateNotifier.kt +private const val DELAY_MILLIS: Long = 40 * 60 * 60 * 1000 diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index 13cf679cc5d2..138236af70ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -314,7 +314,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } public void setCdmaRoaming(boolean isRoaming) { - when(mMockTm.getCdmaEnhancedRoamingIndicatorIconIndex()).thenReturn( + when(mMockTm.getCdmaEnhancedRoamingIndicatorDisplayNumber()).thenReturn( isRoaming ? TelephonyManager.ERI_ON : TelephonyManager.ERI_OFF); } diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-bn/strings.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-bn/strings.xml new file mode 100644 index 000000000000..3e9c962f8946 --- /dev/null +++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-bn/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="display_cutout_emulation_overlay" msgid="7305489596221077240">"পাঞ্চ হোল কাট-আউট"</string> +</resources> diff --git a/services/backup/OWNERS b/services/backup/OWNERS index 7c7e74285bf5..3c5268c5a2a9 100644 --- a/services/backup/OWNERS +++ b/services/backup/OWNERS @@ -1,7 +1,6 @@ # Bug component: 656484 aabhinav@google.com -alsutton@google.com bryanmawhinney@google.com jstemmer@google.com nathch@google.com diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 21ad6de045bb..22423fe00b6c 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -65,7 +65,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.app.BroadcastOptions; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -1146,8 +1145,7 @@ public class ConnectivityService extends IConnectivityManager.Stub dataConnectionStats.startMonitoring(); mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler); - mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager, - mContext.getSystemService(NotificationManager.class)); + mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager); final int dailyLimit = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 8ea71b3202e5..770e8de66bd7 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -340,7 +340,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES | PhoneStateListener.LISTEN_REGISTRATION_FAILURE - | PhoneStateListener.LISTEN_BARRING_INFO; + | PhoneStateListener.LISTEN_BARRING_INFO + | PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION; static final long READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK = PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java new file mode 100644 index 000000000000..db7e16ca8b25 --- /dev/null +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.content.Context; +import android.net.vcn.IVcnManagementService; + +/** + * VcnManagementService manages Virtual Carrier Network profiles and lifecycles. + * + * <pre>The internal structure of the VCN Management subsystem is as follows: + * + * +------------------------+ 1:1 +--------------------------------+ + * | VcnManagementService | ------------ Creates -------------> | TelephonySubscriptionManager | + * | | | | + * | Manages configs and | | Tracks subscriptions, carrier | + * | VcnInstance lifecycles | <--- Notifies of subscription & --- | privilege changes, caches maps | + * +------------------------+ carrier privilege changes +--------------------------------+ + * | 1:N ^ + * | | + * | +-------------------------------+ + * +---------------+ | + * | | + * Creates when config present, | + * subscription group active, and | + * providing app is carrier privileged Notifies of safe + * | mode state changes + * v | + * +-----------------------------------------------------------------------+ + * | VcnInstance | + * | | + * | Manages tunnel lifecycles based on fulfillable NetworkRequest(s) | + * | and overall safe-mode | + * +-----------------------------------------------------------------------+ + * | 1:N ^ + * Creates to fulfill | + * NetworkRequest(s), tears Notifies of VcnTunnel + * down when no longer needed teardown (e.g. Network reaped) + * | and safe-mode timer changes + * v | + * +-----------------------------------------------------------------------+ + * | VcnTunnel | + * | | + * | Manages a single (IKEv2) tunnel session and NetworkAgent, | + * | handles mobility events, (IPsec) Tunnel setup and safe-mode timers | + * +-----------------------------------------------------------------------+ + * | 1:1 ^ + * | | + * Creates upon instantiation Notifies of changes in + * | selected underlying network + * | or its properties + * v | + * +-----------------------------------------------------------------------+ + * | UnderlyingNetworkTracker | + * | | + * | Manages lifecycle of underlying physical networks, filing requests to | + * | bring them up, and releasing them as they become no longer necessary | + * +-----------------------------------------------------------------------+ + * </pre> + * + * @hide + */ +public class VcnManagementService extends IVcnManagementService.Stub { + @NonNull private static final String TAG = VcnManagementService.class.getSimpleName(); + + public static final boolean VDBG = false; // STOPSHIP: if true + + /* Binder context for this service */ + @NonNull private final Context mContext; + @NonNull private final Dependencies mDeps; + + private VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) { + mContext = requireNonNull(context, "Missing context"); + mDeps = requireNonNull(deps, "Missing dependencies"); + } + + // Package-visibility for SystemServer to create instances. + static VcnManagementService create(@NonNull Context context) { + return new VcnManagementService(context, new Dependencies()); + } + + private static class Dependencies {} + + /** Notifies the VcnManagementService that external dependencies can be set up */ + public void systemReady() {} +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java index f7998ee8caeb..d67ea1687595 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java @@ -582,13 +582,19 @@ class Face10 implements IHwBinder.DeathRecipient { final FaceGenerateChallengeClient previousChallengeOwner = mCurrentChallengeOwner.getInterruptedClient(); mCurrentChallengeOwner = null; + Slog.d(TAG, "Previous challenge owner: " + previousChallengeOwner); if (previousChallengeOwner != null) { - try { - previousChallengeOwner.getListener() - .onChallengeInterruptFinished(mSensorId); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to notify interrupt finished", e); + final ClientMonitorCallbackConverter listener = + previousChallengeOwner.getListener(); + if (listener == null) { + Slog.w(TAG, "Listener is null"); + } else { + try { + listener.onChallengeInterruptFinished(mSensorId); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to notify interrupt finished", e); + } } } } 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 c88247f59871..265ba0545395 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 @@ -469,6 +469,27 @@ public class FingerprintService extends SystemService { } @Override // Binder call + public boolean hasEnrolledTemplatesForAnySensor(int userId, + @NonNull List<FingerprintSensorPropertiesInternal> sensors, + @NonNull String opPackageName) { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + + for (FingerprintSensorPropertiesInternal prop : sensors) { + final ServiceProvider provider = getProviderForSensor(prop.sensorId); + if (provider == null) { + Slog.w(TAG, "Null provider for sensorId: " + prop.sensorId + + ", caller: " + opPackageName); + continue; + } + + if (!provider.getEnrolledFingerprints(prop.sensorId, userId).isEmpty()) { + return true; + } + } + return false; + } + + @Override // Binder call public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index bc3bff1b966f..ff3193182501 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -18,7 +18,7 @@ package com.android.server.compat; import android.annotation.Nullable; import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; +import android.compat.annotation.EnabledSince; import android.content.pm.ApplicationInfo; import com.android.internal.compat.CompatibilityChangeInfo; @@ -43,8 +43,8 @@ public final class CompatChange extends CompatibilityChangeInfo { * A change ID to be used only in the CTS test for this SystemApi */ @ChangeId - @EnabledAfter(targetSdkVersion = 1234) // Needs to be > test APK targetSdkVersion. - private static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id. + @EnabledSince(targetSdkVersion = 1235) // Needs to be > test APK targetSdkVersion. + static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id. /** * Callback listener for when compat changes are updated for a package. diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index e4f52f1fc927..77d5411f5f7f 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -381,6 +381,9 @@ public class PlatformCompat extends IPlatformCompat.Stub { if (change.getLoggingOnly()) { return false; } + if (change.getId() == CompatChange.CTS_SYSTEM_API_CHANGEID) { + return false; + } if (change.getEnableSinceTargetSdk() > 0) { if (change.getEnableSinceTargetSdk() < sMinTargetSdk || change.getEnableSinceTargetSdk() > sMaxTargetSdk) { diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 26356b440d09..338539312278 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -21,6 +21,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import android.annotation.NonNull; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -75,16 +76,21 @@ public class NetworkNotificationManager { private static final boolean DBG = true; private static final boolean VDBG = false; + // The context is for the current user (system server) private final Context mContext; private final TelephonyManager mTelephonyManager; + // The notification manager is created from a context for User.ALL, so notifications + // will be sent to all users. private final NotificationManager mNotificationManager; // Tracks the types of notifications managed by this instance, from creation to cancellation. private final SparseIntArray mNotificationTypeMap; - public NetworkNotificationManager(Context c, TelephonyManager t, NotificationManager n) { + public NetworkNotificationManager(@NonNull final Context c, @NonNull final TelephonyManager t) { mContext = c; mTelephonyManager = t; - mNotificationManager = n; + mNotificationManager = + (NotificationManager) c.createContextAsUser(UserHandle.ALL, 0 /* flags */) + .getSystemService(Context.NOTIFICATION_SERVICE); mNotificationTypeMap = new SparseIntArray(); } @@ -282,7 +288,7 @@ public class NetworkNotificationManager { mNotificationTypeMap.put(id, eventId); try { - mNotificationManager.notifyAsUser(tag, eventId, notification, UserHandle.ALL); + mNotificationManager.notify(tag, eventId, notification); } catch (NullPointerException npe) { Slog.d(TAG, "setNotificationVisible: visible notificationManager error", npe); } @@ -311,7 +317,7 @@ public class NetworkNotificationManager { nameOf(eventId))); } try { - mNotificationManager.cancelAsUser(tag, eventId, UserHandle.ALL); + mNotificationManager.cancel(tag, eventId); } catch (NullPointerException npe) { Slog.d(TAG, String.format( "failed to clear notification tag=%s event=%s", tag, nameOf(eventId)), npe); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index aebcdf19ecf4..4d3998956858 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -199,6 +199,8 @@ public class Vpn { // automated reconnection private final Context mContext; + // The context is for specific user which is created from mUserId + private final Context mUserIdContext; @VisibleForTesting final Dependencies mDeps; private final NetworkInfo mNetworkInfo; private int mLegacyState; @@ -399,6 +401,7 @@ public class Vpn { int userId, @NonNull KeyStore keyStore, SystemServices systemServices, Ikev2SessionCreator ikev2SessionCreator) { mContext = context; + mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */); mDeps = deps; mNetd = netService; mUserId = userId; @@ -1925,9 +1928,10 @@ public class Vpn { final UserHandle user = UserHandle.of(mUserId); final long token = Binder.clearCallingIdentity(); try { - final NotificationManager notificationManager = NotificationManager.from(mContext); + final NotificationManager notificationManager = + mUserIdContext.getSystemService(NotificationManager.class); if (!visible) { - notificationManager.cancelAsUser(TAG, SystemMessage.NOTE_VPN_DISCONNECTED, user); + notificationManager.cancel(TAG, SystemMessage.NOTE_VPN_DISCONNECTED); return; } final Intent intent = new Intent(); @@ -1947,8 +1951,7 @@ public class Vpn { .setVisibility(Notification.VISIBILITY_PUBLIC) .setOngoing(true) .setColor(mContext.getColor(R.color.system_notification_accent_color)); - notificationManager.notifyAsUser(TAG, SystemMessage.NOTE_VPN_DISCONNECTED, - builder.build(), user); + notificationManager.notify(TAG, SystemMessage.NOTE_VPN_DISCONNECTED, builder.build()); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 9437677d8ab8..9245f55bd9fe 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -351,10 +351,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { if (mDefaultModeId == NO_DISPLAY_MODE_ID) { mDefaultModeId = activeRecord.mMode.getModeId(); mDefaultConfigGroup = configs[activeConfigId].configGroup; - } else if (modesAdded && mActiveModeId != activeRecord.mMode.getModeId()) { + } else if (modesAdded && activeModeChanged) { Slog.d(TAG, "New display modes are added and the active mode has changed, " + "use active mode as default mode."); - mActiveModeId = activeRecord.mMode.getModeId(); mDefaultModeId = activeRecord.mMode.getModeId(); mDefaultConfigGroup = configs[activeConfigId].configGroup; } else if (findDisplayConfigIdLocked(mDefaultModeId, mDefaultConfigGroup) < 0) { diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 9ca4d35f4579..9c48d23dade6 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -306,9 +306,8 @@ public class LocationManagerService extends ILocationManager.Stub { private void removeLocationProviderManager(LocationProviderManager manager) { synchronized (mProviderManagers) { - Preconditions.checkState(getLocationProviderManager(manager.getName()) == manager); - - mProviderManagers.remove(manager); + boolean removed = mProviderManagers.remove(manager); + Preconditions.checkArgument(removed); manager.setMockProvider(null); manager.setRealProvider(null); manager.stopManager(); diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java index b4a172393ba6..f25c65192066 100644 --- a/services/core/java/com/android/server/location/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -491,7 +491,7 @@ class LocationProviderManager extends builder.setIntervalMillis(MIN_COARSE_INTERVAL_MS); } if (baseRequest.getMinUpdateIntervalMillis() < MIN_COARSE_INTERVAL_MS) { - builder.clearMinUpdateIntervalMillis(); + builder.setMinUpdateIntervalMillis(MIN_COARSE_INTERVAL_MS); } } @@ -1197,19 +1197,19 @@ class LocationProviderManager extends public void stopManager() { synchronized (mLock) { - mUserHelper.removeListener(mUserChangedListener); - mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener); - - mStarted = false; - final long identity = Binder.clearCallingIdentity(); try { onEnabledChanged(UserHandle.USER_ALL); removeRegistrationIf(key -> true); - mEnabledListeners.clear(); } finally { Binder.restoreCallingIdentity(identity); } + + mUserHelper.removeListener(mUserChangedListener); + mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener); + + Preconditions.checkState(mEnabledListeners.isEmpty()); + mStarted = false; } } @@ -1408,6 +1408,7 @@ class LocationProviderManager extends Location location; synchronized (mLock) { + Preconditions.checkState(mStarted); LastLocation lastLocation = mLastLocations.get(userId); if (lastLocation == null) { location = null; @@ -1429,6 +1430,7 @@ class LocationProviderManager extends public void injectLastLocation(Location location, int userId) { synchronized (mLock) { + Preconditions.checkState(mStarted); if (getLastLocationUnsafe(userId, PERMISSION_FINE, false, Long.MAX_VALUE) == null) { setLastLocation(location, userId); } @@ -1476,6 +1478,7 @@ class LocationProviderManager extends permissionLevel); synchronized (mLock) { + Preconditions.checkState(mStarted); final long ident = Binder.clearCallingIdentity(); try { putRegistration(callback.asBinder(), registration); @@ -1517,6 +1520,7 @@ class LocationProviderManager extends permissionLevel); synchronized (mLock) { + Preconditions.checkState(mStarted); final long ident = Binder.clearCallingIdentity(); try { putRegistration(listener.asBinder(), registration); @@ -1535,6 +1539,7 @@ class LocationProviderManager extends permissionLevel); synchronized (mLock) { + Preconditions.checkState(mStarted); final long identity = Binder.clearCallingIdentity(); try { putRegistration(pendingIntent, registration); @@ -1546,6 +1551,7 @@ class LocationProviderManager extends public void unregisterLocationRequest(ILocationListener listener) { synchronized (mLock) { + Preconditions.checkState(mStarted); final long identity = Binder.clearCallingIdentity(); try { removeRegistration(listener.asBinder()); @@ -1557,6 +1563,7 @@ class LocationProviderManager extends public void unregisterLocationRequest(PendingIntent pendingIntent) { synchronized (mLock) { + Preconditions.checkState(mStarted); final long identity = Binder.clearCallingIdentity(); try { removeRegistration(pendingIntent); diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index e25e605cf7d2..0c1e91d9bf24 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -583,8 +583,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mDownloadPsdsWakeLock.setReferenceCounted(true); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0); - mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0); + mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), + PendingIntent.FLAG_IMMUTABLE); + mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), + PendingIntent.FLAG_IMMUTABLE); // App ops service to keep track of who is accessing the GPS mAppOps = mContext.getSystemService(AppOpsManager.class); diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index 3a262d6dfafd..06cebac501e7 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -65,6 +65,7 @@ public class LockdownVpnTracker { @NonNull private final Context mContext; @NonNull private final ConnectivityService mConnService; + @NonNull private final NotificationManager mNotificationManager; @NonNull private final Handler mHandler; @NonNull private final Vpn mVpn; @NonNull private final VpnProfile mProfile; @@ -93,6 +94,7 @@ public class LockdownVpnTracker { mHandler = Objects.requireNonNull(handler); mVpn = Objects.requireNonNull(vpn); mProfile = Objects.requireNonNull(profile); + mNotificationManager = mContext.getSystemService(NotificationManager.class); final Intent configIntent = new Intent(ACTION_VPN_SETTINGS); mConfigIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */, configIntent, @@ -266,11 +268,11 @@ public class LockdownVpnTracker { .setColor(mContext.getColor( com.android.internal.R.color.system_notification_accent_color)); - NotificationManager.from(mContext).notify(null, SystemMessage.NOTE_VPN_STATUS, + mNotificationManager.notify(null /* tag */, SystemMessage.NOTE_VPN_STATUS, builder.build()); } private void hideNotification() { - NotificationManager.from(mContext).cancel(null, SystemMessage.NOTE_VPN_STATUS); + mNotificationManager.cancel(null, SystemMessage.NOTE_VPN_STATUS); } } diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 4040f4189698..ea7779a4dc8f 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -110,6 +110,7 @@ abstract public class ManagedServices { static final String ATT_IS_PRIMARY = "primary"; static final String ATT_VERSION = "version"; static final String ATT_DEFAULTS = "defaults"; + static final String ATT_USER_SET = "user_set_services"; static final int DB_VERSION = 3; @@ -150,7 +151,12 @@ abstract public class ManagedServices { // List of approved packages or components (by user, then by primary/secondary) that are // allowed to be bound as managed services. A package or component appearing in this list does // not mean that we are currently bound to said package/component. - private ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>(); + protected ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>(); + + // List of packages or components (by user) that are configured to be enabled/disabled + // explicitly by the user + @GuardedBy("mApproved") + protected ArrayMap<Integer, ArraySet<String>> mUserSetServices = new ArrayMap<>(); // True if approved services are stored in xml, not settings. private boolean mUseXml; @@ -333,6 +339,12 @@ abstract public class ManagedServices { } } } + + pw.println(" Has user set:"); + Set<Integer> userIds = mUserSetServices.keySet(); + for (int userId : userIds) { + pw.println(" userId=" + userId + " value=" + mUserSetServices.get(userId)); + } } pw.println(" All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size() @@ -465,12 +477,20 @@ abstract public class ManagedServices { for (int j = 0; j < M; j++) { final boolean isPrimary = approvedByType.keyAt(j); final Set<String> approved = approvedByType.valueAt(j); - if (approved != null) { - String allowedItems = String.join(ENABLED_SERVICES_SEPARATOR, approved); + final Set<String> userSet = mUserSetServices.get(approvedUserId); + if (approved != null || userSet != null) { + String allowedItems = approved == null + ? "" + : String.join(ENABLED_SERVICES_SEPARATOR, approved); out.startTag(null, TAG_MANAGED_SERVICES); out.attribute(null, ATT_APPROVED_LIST, allowedItems); out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId)); out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary)); + if (userSet != null) { + String userSetItems = + String.join(ENABLED_SERVICES_SEPARATOR, userSet); + out.attribute(null, ATT_USER_SET, userSetItems); + } writeExtraAttributes(out, approvedUserId); out.endTag(null, TAG_MANAGED_SERVICES); @@ -559,11 +579,12 @@ abstract public class ManagedServices { ? userId : XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0); final boolean isPrimary = XmlUtils.readBooleanAttribute(parser, ATT_IS_PRIMARY, true); + final String userSet = XmlUtils.readStringAttribute(parser, ATT_USER_SET); readExtraAttributes(tag, parser, resolvedUserId); if (allowedManagedServicePackages == null || allowedManagedServicePackages.test( getPackageName(approved), resolvedUserId, getRequiredPermission())) { if (mUm.getUserInfo(resolvedUserId) != null) { - addApprovedList(approved, resolvedUserId, isPrimary); + addApprovedList(approved, resolvedUserId, isPrimary, userSet); } mUseXml = true; } @@ -632,9 +653,16 @@ abstract public class ManagedServices { } protected void addApprovedList(String approved, int userId, boolean isPrimary) { + addApprovedList(approved, userId, isPrimary, approved); + } + + protected void addApprovedList(String approved, int userId, boolean isPrimary, String userSet) { if (TextUtils.isEmpty(approved)) { approved = ""; } + if (userSet == null) { + userSet = approved; + } synchronized (mApproved) { ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); if (approvedByType == null) { @@ -655,6 +683,19 @@ abstract public class ManagedServices { approvedList.add(approvedItem); } } + + ArraySet<String> userSetList = mUserSetServices.get(userId); + if (userSetList == null) { + userSetList = new ArraySet<>(); + mUserSetServices.put(userId, userSetList); + } + String[] userSetArray = userSet.split(ENABLED_SERVICES_SEPARATOR); + for (String pkgOrComponent : userSetArray) { + String approvedItem = getApprovedValue(pkgOrComponent); + if (approvedItem != null) { + userSetList.add(approvedItem); + } + } } } @@ -664,8 +705,14 @@ abstract public class ManagedServices { protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId, boolean isPrimary, boolean enabled) { + setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled, true); + } + + protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId, + boolean isPrimary, boolean enabled, boolean userSet) { Slog.i(TAG, - (enabled ? " Allowing " : "Disallowing ") + mConfig.caption + " " + pkgOrComponent); + (enabled ? " Allowing " : "Disallowing ") + mConfig.caption + " " + + pkgOrComponent + " (userSet: " + userSet + ")"); synchronized (mApproved) { ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.get(userId); if (allowedByType == null) { @@ -686,6 +733,16 @@ abstract public class ManagedServices { approved.remove(approvedItem); } } + ArraySet<String> userSetServices = mUserSetServices.get(userId); + if (userSetServices == null) { + userSetServices = new ArraySet<>(); + mUserSetServices.put(userId, userSetServices); + } + if (userSet) { + userSetServices.add(pkgOrComponent); + } else { + userSetServices.remove(pkgOrComponent); + } } rebindServices(false, userId); @@ -761,6 +818,13 @@ abstract public class ManagedServices { return false; } + boolean isPackageOrComponentUserSet(String pkgOrComponent, int userId) { + synchronized (mApproved) { + ArraySet<String> services = mUserSetServices.get(userId); + return services != null && services.contains(pkgOrComponent); + } + } + protected boolean isPackageAllowed(String pkg, int userId) { if (pkg == null) { return false; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 8ea432634e61..4f4f3c64b1b4 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -97,6 +97,7 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.internal.util.CollectionUtils.emptyIfNull; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; @@ -304,6 +305,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; @@ -714,7 +716,7 @@ public class NotificationManagerService extends SystemService { try { getBinderService().setNotificationListenerAccessGrantedForUser(cn, - userId, true); + userId, true, true); } catch (RemoteException e) { e.printStackTrace(); } @@ -4822,9 +4824,9 @@ public class NotificationManagerService extends SystemService { @Override public void setNotificationListenerAccessGranted(ComponentName listener, - boolean granted) throws RemoteException { + boolean granted, boolean userSet) throws RemoteException { setNotificationListenerAccessGrantedForUser( - listener, getCallingUserHandle().getIdentifier(), granted); + listener, getCallingUserHandle().getIdentifier(), granted, userSet); } @Override @@ -4836,17 +4838,21 @@ public class NotificationManagerService extends SystemService { @Override public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId, - boolean granted) { + boolean granted, boolean userSet) { Objects.requireNonNull(listener); checkNotificationListenerAccess(); + if (!userSet && isNotificationListenerAccessUserSet(listener)) { + // Don't override user's choice + return; + } final long identity = Binder.clearCallingIdentity(); try { if (mAllowedManagedServicePackages.test( listener.getPackageName(), userId, mListeners.getRequiredPermission())) { mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(), - userId, false, granted); + userId, false, granted, userSet); mListeners.setPackageOrComponentEnabled(listener.flattenToString(), - userId, true, granted); + userId, true, granted, userSet); getContext().sendBroadcastAsUser(new Intent( ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) @@ -4861,6 +4867,11 @@ public class NotificationManagerService extends SystemService { } } + private boolean isNotificationListenerAccessUserSet(ComponentName listener) { + return mListeners.isPackageOrComponentUserSet(listener.flattenToString(), + getCallingUserHandle().getIdentifier()); + } + @Override public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant, int userId, boolean granted) { @@ -8830,14 +8841,12 @@ public class NotificationManagerService extends SystemService { public class NotificationAssistants extends ManagedServices { static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants"; - private static final String ATT_USER_SET = "user_set"; private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "q_allowed_adjustments"; private static final String ATT_TYPES = "types"; private final Object mLock = new Object(); @GuardedBy("mLock") - private ArrayMap<Integer, Boolean> mUserSetMap = new ArrayMap<>(); private Set<String> mAllowedAdjustments = new ArraySet<>(); @Override @@ -9026,22 +9035,33 @@ public class NotificationManagerService extends SystemService { boolean hasUserSet(int userId) { synchronized (mLock) { - return mUserSetMap.getOrDefault(userId, false); + ArraySet<String> userSetServices = mUserSetServices.get(userId); + if (userSetServices == null) { + // Legacy case - no data means user-set, unless no assistant is set + return !mApproved.isEmpty(); + } + Map<Boolean, ArraySet<String>> approvedByType = emptyIfNull(mApproved.get(userId)); + return userSetServices.containsAll(emptyIfNull(approvedByType.get(true))) + && userSetServices.containsAll(emptyIfNull(approvedByType.get(false))); } } void setUserSet(int userId, boolean set) { synchronized (mLock) { - mUserSetMap.put(userId, set); + ArraySet<String> userSetServices = new ArraySet<>(); + if (set) { + ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); + if (approvedByType != null) { + for (int i = 0; i < approvedByType.size(); i++) { + userSetServices.addAll(approvedByType.valueAt(i)); + } + } + } + mUserSetServices.put(userId, userSetServices); } } @Override - protected void writeExtraAttributes(XmlSerializer out, int userId) throws IOException { - out.attribute(null, ATT_USER_SET, Boolean.toString(hasUserSet(userId))); - } - - @Override protected void readExtraAttributes(String tag, XmlPullParser parser, int userId) throws IOException { boolean userSet = XmlUtils.readBooleanAttribute(parser, ATT_USER_SET, false); @@ -9279,18 +9299,6 @@ public class NotificationManagerService extends SystemService { super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled); } - @Override - public void dump(PrintWriter pw, DumpFilter filter) { - super.dump(pw, filter); - pw.println(" Has user set:"); - synchronized (mLock) { - Set<Integer> userIds = mUserSetMap.keySet(); - for (int userId : userIds) { - pw.println(" userId=" + userId + " value=" + mUserSetMap.get(userId)); - } - } - } - private boolean isVerboseLogEnabled() { return Log.isLoggable("notification_assistant", Log.VERBOSE); } @@ -9307,8 +9315,8 @@ public class NotificationManagerService extends SystemService { @Override protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId, - boolean isPrimary, boolean enabled) { - super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled); + boolean isPrimary, boolean enabled, boolean userSet) { + super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled, userSet); getContext().sendBroadcastAsUser( new Intent(ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED) diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java index 73272a012671..78c60d559ea9 100644 --- a/services/core/java/com/android/server/notification/NotificationShellCmd.java +++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java @@ -214,7 +214,8 @@ public class NotificationShellCmd extends ShellCommand { if (peekNextArg() != null) { userId = Integer.parseInt(getNextArgRequired()); } - mBinderService.setNotificationListenerAccessGrantedForUser(cn, userId, true); + mBinderService.setNotificationListenerAccessGrantedForUser( + cn, userId, true, true); } break; case "disallow_listener": { @@ -227,7 +228,8 @@ public class NotificationShellCmd extends ShellCommand { if (peekNextArg() != null) { userId = Integer.parseInt(getNextArgRequired()); } - mBinderService.setNotificationListenerAccessGrantedForUser(cn, userId, false); + mBinderService.setNotificationListenerAccessGrantedForUser( + cn, userId, false, true); } break; case "allow_assistant": { diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index c0329b2b2fe6..efbdfeaaeb93 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -918,7 +918,7 @@ class InstantAppRegistry { try { for (String grantedPermission : appInfo.getGrantedPermissions()) { final boolean propagatePermission = - mService.mSettings.canPropagatePermissionToInstantApp(grantedPermission); + mPermissionManager.canPropagatePermissionToInstantApp(grantedPermission); if (propagatePermission && pkg.getRequestedPermissions().contains( grantedPermission)) { mService.grantRuntimePermission(pkg.getPackageName(), grantedPermission, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e63a05aa6b73..9329271382bc 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -375,7 +375,7 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.permission.BasePermission; +import com.android.server.pm.permission.Permission; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.policy.PermissionPolicyInternal; @@ -2742,7 +2742,6 @@ public class PackageManagerService extends IPackageManager.Stub new UserDataPreparer(installer, installLock, context, onlyCore), lock), (i, pm) -> new Settings(Environment.getDataDirectory(), - i.getPermissionManagerServiceInternal().getPermissionSettings(), RuntimePermissionsPersistence.createInstance(), i.getPermissionManagerServiceInternal(), lock), (i, pm) -> AppsFilter.create(pm.mPmInternal, i), @@ -3173,6 +3172,8 @@ public class PackageManagerService extends IPackageManager.Stub /* excludePreCreated= */ false)); t.traceEnd(); + mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions); + // Clean up orphaned packages for which the code path doesn't exist // and they are an update to a system app - caused by bug/32321269 final int packageSettingCount = mSettings.mPackages.size(); @@ -3597,7 +3598,7 @@ public class PackageManagerService extends IPackageManager.Stub + ((SystemClock.uptimeMillis()-startTime)/1000f) + " seconds"); - mPermissionManager.readStateFromPackageSettingsTEMP(); + mPermissionManager.readLegacyPermissionStateTEMP(); // If the platform SDK has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some @@ -11433,7 +11434,7 @@ public class PackageManagerService extends IPackageManager.Stub if (verifyPackageUpdateLPr(orig, pkg)) { Slog.i(TAG, "Adopting permissions from " + origName + " to " + pkg.getPackageName()); - mSettings.mPermissions.transferPermissions(origName, pkg.getPackageName()); + mPermissionManager.transferPermissions(origName, pkg.getPackageName()); } } } @@ -17943,7 +17944,7 @@ public class PackageManagerService extends IPackageManager.Stub final int N = ArrayUtils.size(parsedPackage.getPermissions()); for (int i = N - 1; i >= 0; i--) { final ParsedPermission perm = parsedPackage.getPermissions().get(i); - final BasePermission bp = mPermissionManager.getPermissionTEMP(perm.getName()); + final Permission bp = mPermissionManager.getPermissionTEMP(perm.getName()); // Don't allow anyone but the system to define ephemeral permissions. if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0 @@ -18712,6 +18713,19 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = versionedPackage.getPackageName(); final long versionCode = versionedPackage.getLongVersionCode(); final String internalPackageName; + + try { + if (mInjector.getLocalService(ActivityTaskManagerInternal.class) + .isBaseOfLockedTask(packageName)) { + observer.onPackageDeleted( + packageName, PackageManager.DELETE_FAILED_APP_PINNED, null); + EventLog.writeEvent(0x534e4554, "127605586", -1, ""); + return; + } + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + synchronized (mLock) { // Normalize package name to handle renamed packages and static libs internalPackageName = resolveInternalPackageNameLPr(packageName, versionCode); @@ -19592,7 +19606,7 @@ public class PackageManagerService extends IPackageManager.Stub ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); } - mSettings.writeRuntimePermissionsForUserLPr(userId, false); + writeRuntimePermissionsForUserLPrTEMP(userId, false); } // Regardless of writeSettings we need to ensure that this restriction // state propagation is persisted @@ -24231,9 +24245,9 @@ public class PackageManagerService extends IPackageManager.Stub boolean readPermissionStateForUser(@UserIdInt int userId) { synchronized (mPackages) { - mPermissionManager.writeStateToPackageSettingsTEMP(); + mPermissionManager.writeLegacyPermissionStateTEMP(); mSettings.readPermissionStateForUserSyncLPr(userId); - mPermissionManager.readStateFromPackageSettingsTEMP(); + mPermissionManager.readLegacyPermissionStateTEMP(); return mPmInternal.isPermissionUpgradeNeeded(userId); } } @@ -25738,7 +25752,7 @@ public class PackageManagerService extends IPackageManager.Stub public void writePermissionSettings(int[] userIds, boolean async) { synchronized (mLock) { for (int userId : userIds) { - mSettings.writeRuntimePermissionsForUserLPr(userId, !async); + writeRuntimePermissionsForUserLPrTEMP(userId, !async); } } } @@ -26376,16 +26390,28 @@ public class PackageManagerService extends IPackageManager.Stub } /** - * Temporary method that wraps mSettings.writeLPr() and calls - * mPermissionManager.writeStateToPackageSettingsTEMP() beforehand. + * Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's + * writeLegacyPermissionsTEMP() and writeLegacyPermissionStateTEMP() beforehand. * * TODO(zhanghai): This should be removed once we finish migration of permission storage. */ private void writeSettingsLPrTEMP() { - mPermissionManager.writeStateToPackageSettingsTEMP(); + mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions); + mPermissionManager.writeLegacyPermissionStateTEMP(); mSettings.writeLPr(); } + /** + * Temporary method that wraps mSettings.writeRuntimePermissionsForUserLPr() and calls + * mPermissionManager.writeLegacyPermissionStateTEMP() beforehand. + * + * TODO(zhanghai): This should be removed once we finish migration of permission storage. + */ + private void writeRuntimePermissionsForUserLPrTEMP(@UserIdInt int userId, boolean async) { + mPermissionManager.writeLegacyPermissionStateTEMP(); + mSettings.writeRuntimePermissionsForUserLPr(userId, async); + } + @Override public IBinder getHoldLockToken() { if (!Build.IS_DEBUGGABLE) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index c7304c2695c9..cd9a4e72672f 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -107,11 +107,10 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.permission.BasePermission; import com.android.server.pm.permission.LegacyPermissionDataProvider; +import com.android.server.pm.permission.LegacyPermissionSettings; import com.android.server.pm.permission.LegacyPermissionState; import com.android.server.pm.permission.LegacyPermissionState.PermissionState; -import com.android.server.pm.permission.PermissionSettings; import com.android.server.utils.TimingsTraceAndSlog; import libcore.io.IoUtils; @@ -420,7 +419,7 @@ public final class Settings { public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages); /** Settings and other information about permissions */ - final PermissionSettings mPermissions; + final LegacyPermissionSettings mPermissions; private final LegacyPermissionDataProvider mPermissionDataProvider; @@ -440,11 +439,10 @@ public final class Settings { mKernelMappingFilename = null; } - Settings(File dataDir, PermissionSettings permissionSettings, - RuntimePermissionsPersistence runtimePermissionsPersistence, + Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence, LegacyPermissionDataProvider permissionDataProvider, Object lock) { mLock = lock; - mPermissions = permissionSettings; + mPermissions = new LegacyPermissionSettings(lock); mRuntimePermissionsPersistence = new RuntimePermissionPersistence( runtimePermissionsPersistence); mPermissionDataProvider = permissionDataProvider; @@ -489,10 +487,6 @@ public final class Settings { mRenamedPackages.remove(pkgName); } - public boolean canPropagatePermissionToInstantApp(String permName) { - return mPermissions.canPropagatePermissionToInstantApp(permName); - } - /** Gets and optionally creates a new shared user id. */ SharedUserSetting getSharedUserLPw(String name, int pkgFlags, int pkgPrivateFlags, boolean create) throws PackageManagerException { @@ -2128,23 +2122,14 @@ public final class Settings { String tagName = parser.getName(); if (tagName.equals(TAG_ITEM)) { String name = parser.getAttributeValue(null, ATTR_NAME); - - BasePermission bp = mPermissions.getPermission(name); - if (bp == null) { - Slog.w(PackageManagerService.TAG, "Unknown permission: " + name); - XmlUtils.skipCurrentTag(parser); - continue; - } - String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED); final boolean granted = grantedStr == null || Boolean.parseBoolean(grantedStr); - String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS); final int flags = (flagsStr != null) ? Integer.parseInt(flagsStr, 16) : 0; - - permissionsState.putInstallPermissionState(new PermissionState(bp, granted, flags)); + permissionsState.putInstallPermissionState(new PermissionState(name, granted, + flags)); } else { Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: " + parser.getName()); @@ -2867,10 +2852,6 @@ public final class Settings { } } - void writePermissionLPr(XmlSerializer serializer, BasePermission bp) throws IOException { - bp.writeLPr(serializer); - } - boolean readLPw(@NonNull List<UserInfo> users) { FileInputStream str = null; if (mBackupSettingsFilename.exists()) { @@ -4813,13 +4794,7 @@ public final class Settings { && !permissionNames.contains(perm)) { continue; } - pw.print(prefix); pw.print(" "); pw.print(perm); - final BasePermission bp = mPermissions.getPermission(perm); - if (bp != null && bp.isHardOrSoftRestricted()) { - pw.println(": restricted=true"); - } else { - pw.println(); - } + pw.print(prefix); pw.print(" "); pw.println(perm); } } @@ -5024,7 +4999,9 @@ public final class Settings { void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames, DumpState dumpState) { - mPermissions.dumpPermissions(pw, packageName, permissionNames, + LegacyPermissionSettings.dumpPermissions(pw, packageName, permissionNames, + mPermissionDataProvider.getLegacyPermissions(), + mPermissionDataProvider.getAllAppOpPermissionPackages(), (mReadExternalStorageEnforced == Boolean.TRUE), dumpState); } @@ -5544,18 +5521,11 @@ public final class Settings { int permissionsSize = permissions.size(); for (int i = 0; i < permissionsSize; i++) { RuntimePermissionsState.PermissionState permission = permissions.get(i); - String name = permission.getName(); - BasePermission basePermission = mPermissions.getPermission(name); - if (basePermission == null) { - Slog.w(PackageManagerService.TAG, "Unknown permission:" + name); - continue; - } boolean granted = permission.isGranted(); int flags = permission.getFlags(); - - permissionsState.putRuntimePermissionState(new PermissionState(basePermission, - granted, flags), userId); + permissionsState.putRuntimePermissionState(new PermissionState(name, granted, + flags), userId); } } @@ -5650,23 +5620,14 @@ public final class Settings { switch (parser.getName()) { case TAG_ITEM: { String name = parser.getAttributeValue(null, ATTR_NAME); - BasePermission bp = mPermissions.getPermission(name); - if (bp == null) { - Slog.w(PackageManagerService.TAG, "Unknown permission:" + name); - XmlUtils.skipCurrentTag(parser); - continue; - } - String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED); final boolean granted = grantedStr == null || Boolean.parseBoolean(grantedStr); - String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS); final int flags = (flagsStr != null) ? Integer.parseInt(flagsStr, 16) : 0; - - permissionsState.putRuntimePermissionState(new PermissionState(bp, granted, - flags), userId); + permissionsState.putRuntimePermissionState(new PermissionState(name, + granted, flags), userId); } break; } diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermission.java b/services/core/java/com/android/server/pm/permission/LegacyPermission.java new file mode 100644 index 000000000000..a19a05ae021c --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/LegacyPermission.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.permission; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.PermissionInfo; +import android.util.Log; + +import com.android.server.pm.DumpState; +import com.android.server.pm.PackageManagerService; + +import libcore.util.EmptyArray; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Legacy permission definition. + */ +//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) +public final class LegacyPermission { + /** + * The permission is defined in a manifest. + */ + public static final int TYPE_MANIFEST = 0; + + /** + * The permission is defined in a system config. + */ + public static final int TYPE_CONFIG = 1; + + /** + * The permission is defined dynamically. + */ + public static final int TYPE_DYNAMIC = 2; + + /** + * @hide + */ + @IntDef({ + TYPE_MANIFEST, + TYPE_CONFIG, + TYPE_DYNAMIC, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionType {} + + private static final String ATTR_NAME = "name"; + private static final String ATTR_PACKAGE = "package"; + private static final String TAG_ITEM = "item"; + + @NonNull + private final PermissionInfo mPermissionInfo; + @PermissionType + private final int mType; + private final int mUid; + @NonNull + private final int[] mGids; + + /** + * Create a new instance of this class. + * + * @param permissionInfo the {@link PermissionInfo} for the permission + * @param type the type of the permission + * @param uid the UID defining the permission + * @param gids the GIDs associated with the permission + */ + public LegacyPermission(@NonNull PermissionInfo permissionInfo, @PermissionType int type, + int uid, @NonNull int[] gids) { + mPermissionInfo = permissionInfo; + mType = type; + mUid = uid; + mGids = gids; + } + + private LegacyPermission(@NonNull String name, @NonNull String packageName, + @PermissionType int type) { + mPermissionInfo = new PermissionInfo(); + mPermissionInfo.name = name; + mPermissionInfo.packageName = packageName; + // Default to most conservative protection level. + mPermissionInfo.protectionLevel = PermissionInfo.PROTECTION_SIGNATURE; + mType = type; + mUid = 0; + mGids = EmptyArray.INT; + } + + /** + * Get the {@link PermissionInfo} for this mission. + * + * @return the {@link PermissionInfo} + */ + @NonNull + public PermissionInfo getPermissionInfo() { + return mPermissionInfo; + } + + /** + * Get the type of this mission. + * + * @return the type + */ + public int getType() { + return mType; + } + + /** + * @hide + */ + public static boolean read(@NonNull Map<String, LegacyPermission> out, + @NonNull XmlPullParser parser) { + final String tagName = parser.getName(); + if (!tagName.equals(TAG_ITEM)) { + return false; + } + final String name = parser.getAttributeValue(null, ATTR_NAME); + final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); + final String ptype = parser.getAttributeValue(null, "type"); + if (name == null || packageName == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: permissions has" + " no name at " + + parser.getPositionDescription()); + return false; + } + final boolean dynamic = "dynamic".equals(ptype); + LegacyPermission bp = out.get(name); + // If the permission is builtin, do not clobber it. + if (bp == null || bp.mType != TYPE_CONFIG) { + bp = new LegacyPermission(name.intern(), packageName, + dynamic ? TYPE_DYNAMIC : TYPE_MANIFEST); + } + bp.mPermissionInfo.protectionLevel = readInt(parser, null, "protection", + PermissionInfo.PROTECTION_NORMAL); + bp.mPermissionInfo.protectionLevel = PermissionInfo.fixProtectionLevel( + bp.mPermissionInfo.protectionLevel); + if (dynamic) { + bp.mPermissionInfo.icon = readInt(parser, null, "icon", 0); + bp.mPermissionInfo.nonLocalizedLabel = parser.getAttributeValue(null, "label"); + } + out.put(bp.mPermissionInfo.name, bp); + return true; + } + + private static int readInt(@NonNull XmlPullParser parser, @Nullable String namespace, + @NonNull String name, int defaultValue) { + final String value = parser.getAttributeValue(namespace, name); + if (value == null) { + return defaultValue; + } + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: attribute " + name + + " has bad integer value " + value + " at " + + parser.getPositionDescription()); + return defaultValue; + } + } + + /** + * @hide + */ + public void write(@NonNull XmlSerializer serializer) throws IOException { + if (mPermissionInfo.packageName == null) { + return; + } + serializer.startTag(null, TAG_ITEM); + serializer.attribute(null, ATTR_NAME, mPermissionInfo.name); + serializer.attribute(null, ATTR_PACKAGE, mPermissionInfo.packageName); + if (mPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_NORMAL) { + serializer.attribute(null, "protection", + Integer.toString(mPermissionInfo.protectionLevel)); + } + if (mType == TYPE_DYNAMIC) { + serializer.attribute(null, "type", "dynamic"); + if (mPermissionInfo.icon != 0) { + serializer.attribute(null, "icon", Integer.toString(mPermissionInfo.icon)); + } + if (mPermissionInfo.nonLocalizedLabel != null) { + serializer.attribute(null, "label", mPermissionInfo.nonLocalizedLabel.toString()); + } + } + serializer.endTag(null, TAG_ITEM); + } + + /** + * @hide + */ + public boolean dump(@NonNull PrintWriter pw, @NonNull String packageName, + @NonNull Set<String> permissionNames, boolean readEnforced, boolean printedSomething, + @NonNull DumpState dumpState) { + if (packageName != null && !packageName.equals(mPermissionInfo.packageName)) { + return false; + } + if (permissionNames != null && !permissionNames.contains(mPermissionInfo.name)) { + return false; + } + if (!printedSomething) { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + pw.println("Permissions:"); + } + pw.print(" Permission ["); pw.print(mPermissionInfo.name); pw.print("] ("); + pw.print(Integer.toHexString(System.identityHashCode(this))); + pw.println("):"); + pw.print(" sourcePackage="); pw.println(mPermissionInfo.packageName); + pw.print(" uid="); pw.print(mUid); + pw.print(" gids="); pw.print(Arrays.toString(mGids)); + pw.print(" type="); pw.print(mType); + pw.print(" prot="); + pw.println(PermissionInfo.protectionToString(mPermissionInfo.protectionLevel)); + if (mPermissionInfo != null) { + pw.print(" perm="); pw.println(mPermissionInfo); + if ((mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0 + || (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) { + pw.print(" flags=0x"); pw.println(Integer.toHexString(mPermissionInfo.flags)); + } + } + if (Objects.equals(mPermissionInfo.name, + android.Manifest.permission.READ_EXTERNAL_STORAGE)) { + pw.print(" enforced="); + pw.println(readEnforced); + } + return true; + } +} diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java index 346a2c527fcb..0e790b1899ed 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java @@ -19,12 +19,32 @@ package com.android.server.pm.permission; import android.annotation.AppIdInt; import android.annotation.NonNull; +import java.util.List; +import java.util.Map; +import java.util.Set; + /** * An interface for legacy code to read permission data in order to maintain compatibility. */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) public interface LegacyPermissionDataProvider { /** + * Get all the legacy permissions currently registered in the system. + * + * @return the legacy permissions + */ + @NonNull + List<LegacyPermission> getLegacyPermissions(); + + /** + * Get all the package names requesting app op permissions. + * + * @return a map of app op permission names to package names requesting them + */ + @NonNull + Map<String, Set<String>> getAllAppOpPermissionPackages(); + + /** * Get the legacy permission state of an app ID, either a package or a shared user. * * @param appId the app ID diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java new file mode 100644 index 000000000000..cc0b79872ba0 --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionSettings.java @@ -0,0 +1,182 @@ +/* + * 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. + */ + +package com.android.server.pm.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.XmlUtils; +import com.android.server.pm.DumpState; +import com.android.server.pm.PackageManagerService; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Legacy permission settings for migration. + */ +public class LegacyPermissionSettings { + /** + * All of the permissions known to the system. The mapping is from permission + * name to permission object. + */ + @GuardedBy("mLock") + private final ArrayMap<String, LegacyPermission> mPermissions = new ArrayMap<>(); + + /** + * All permission trees known to the system. The mapping is from permission tree + * name to permission object. + */ + @GuardedBy("mLock") + private final ArrayMap<String, LegacyPermission> mPermissionTrees = new ArrayMap<>(); + + @NonNull + private final Object mLock; + + public LegacyPermissionSettings(@NonNull Object lock) { + mLock = lock; + } + + @NonNull + public List<LegacyPermission> getPermissions() { + synchronized (mLock) { + return new ArrayList<>(mPermissions.values()); + } + } + + @NonNull + public List<LegacyPermission> getPermissionTrees() { + synchronized (mLock) { + return new ArrayList<>(mPermissionTrees.values()); + } + } + + public void replacePermissions(@NonNull List<LegacyPermission> permissions) { + synchronized (mLock) { + mPermissions.clear(); + final int permissionsSize = permissions.size(); + for (int i = 0; i < permissionsSize; i++) { + final LegacyPermission permission = permissions.get(i); + mPermissions.put(permission.getPermissionInfo().name, permission); + } + } + } + + public void replacePermissionTrees(@NonNull List<LegacyPermission> permissionTrees) { + synchronized (mLock) { + mPermissionTrees.clear(); + final int permissionsSize = permissionTrees.size(); + for (int i = 0; i < permissionsSize; i++) { + final LegacyPermission permissionTree = permissionTrees.get(i); + mPermissionTrees.put(permissionTree.getPermissionInfo().name, permissionTree); + } + } + } + + public void readPermissions(@NonNull XmlPullParser parser) throws IOException, + XmlPullParserException { + synchronized (mLock) { + readPermissions(mPermissions, parser); + } + } + + public void readPermissionTrees(@NonNull XmlPullParser parser) throws IOException, + XmlPullParserException { + synchronized (mLock) { + readPermissions(mPermissionTrees, parser); + } + } + + public static void readPermissions(@NonNull ArrayMap<String, LegacyPermission> out, + @NonNull XmlPullParser parser) throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (!LegacyPermission.read(out, parser)) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element reading permissions: " + parser.getName() + " at " + + parser.getPositionDescription()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + public void writePermissions(@NonNull XmlSerializer serializer) throws IOException { + synchronized (mLock) { + for (LegacyPermission bp : mPermissions.values()) { + bp.write(serializer); + } + } + } + + public void writePermissionTrees(@NonNull XmlSerializer serializer) throws IOException { + synchronized (mLock) { + for (LegacyPermission bp : mPermissionTrees.values()) { + bp.write(serializer); + } + } + } + + public static void dumpPermissions(@NonNull PrintWriter pw, @Nullable String packageName, + @Nullable ArraySet<String> permissionNames, @NonNull List<LegacyPermission> permissions, + @NonNull Map<String, Set<String>> appOpPermissionPackages, + boolean externalStorageEnforced, @NonNull DumpState dumpState) { + boolean printedSomething = false; + final int permissionsSize = permissions.size(); + for (int i = 0; i < permissionsSize; i++) { + final LegacyPermission permission = permissions.get(i); + printedSomething = permission.dump(pw, packageName, permissionNames, + externalStorageEnforced, printedSomething, dumpState); + } + if (packageName == null && permissionNames == null) { + boolean firstEntry = true; + for (final Map.Entry<String, Set<String>> entry : appOpPermissionPackages.entrySet()) { + if (firstEntry) { + firstEntry = false; + if (dumpState.onTitlePrinted()) { + pw.println(); + } + pw.println("AppOp Permissions:"); + } + pw.print(" AppOp Permission "); + pw.print(entry.getKey()); + pw.println(":"); + for (final String appOpPackageName : entry.getValue()) { + pw.print(" "); + pw.println(appOpPackageName); + } + } + } + } +} diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java index 63f69cede59c..0e60367c243b 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java @@ -252,7 +252,7 @@ public final class LegacyPermissionState { */ public static final class PermissionState { @NonNull - private final BasePermission mPermission; + private final String mName; private final boolean mGranted; @@ -261,40 +261,30 @@ public final class LegacyPermissionState { /** * Create a new instance of this class. * - * @param permission the {@link BasePermission} for the permission + * @param name the name of the permission * @param granted whether the permission is granted * @param flags the permission flags */ - public PermissionState(@NonNull BasePermission permission, boolean granted, int flags) { - mPermission = permission; + public PermissionState(@NonNull String name, boolean granted, int flags) { + mName = name; mGranted = granted; mFlags = flags; } private PermissionState(@NonNull PermissionState other) { - mPermission = other.mPermission; + mName = other.mName; mGranted = other.mGranted; mFlags = other.mFlags; } /** - * Get the {@link BasePermission} for the permission. - * - * @return the {@link BasePermission} - */ - @NonNull - public BasePermission getPermission() { - return mPermission; - } - - /** * Get the permission name. * * @return the permission name */ @NonNull public String getName() { - return mPermission.getName(); + return mName; } /** diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/Permission.java index 155d71673e06..4e8ddac88529 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/Permission.java @@ -19,6 +19,7 @@ package com.android.server.pm.permission; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PermissionInfo; @@ -28,31 +29,25 @@ import android.os.UserHandle; import android.util.Log; import android.util.Slog; -import com.android.server.pm.DumpState; import com.android.server.pm.PackageManagerService; import com.android.server.pm.parsing.pkg.AndroidPackage; import libcore.util.EmptyArray; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; -import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; import java.util.Collection; -import java.util.Map; import java.util.Objects; -import java.util.Set; -public final class BasePermission { - private static final String TAG = "PackageManager"; +/** + * Permission definition. + */ +public final class Permission { + private static final String TAG = "Permission"; - public static final int TYPE_MANIFEST = 0; - public static final int TYPE_CONFIG = 1; - public static final int TYPE_DYNAMIC = 2; + public static final int TYPE_MANIFEST = LegacyPermission.TYPE_MANIFEST; + public static final int TYPE_CONFIG = LegacyPermission.TYPE_CONFIG; + public static final int TYPE_DYNAMIC = LegacyPermission.TYPE_DYNAMIC; @IntDef({ TYPE_MANIFEST, TYPE_CONFIG, @@ -70,18 +65,13 @@ public final class BasePermission { @Retention(RetentionPolicy.SOURCE) public @interface ProtectionLevel {} - private static final String ATTR_NAME = "name"; - private static final String ATTR_PACKAGE = "package"; - private static final String TAG_ITEM = "item"; - - private boolean mPermissionDefinitionChanged; - @NonNull private PermissionInfo mPermissionInfo; private boolean mReconciled; - private final @PermissionType int mType; + @PermissionType + private final int mType; /** UID that owns the definition of this permission */ private int mUid; @@ -96,7 +86,10 @@ public final class BasePermission { */ private boolean mGidsPerUser; - public BasePermission(@NonNull String name, String packageName, @PermissionType int type) { + private boolean mDefinitionChanged; + + public Permission(@NonNull String name, @NonNull String packageName, + @PermissionType int type) { mPermissionInfo = new PermissionInfo(); mPermissionInfo.name = name; mPermissionInfo.packageName = packageName; @@ -105,10 +98,27 @@ public final class BasePermission { mType = type; } - @Override - public String toString() { - return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " - + mPermissionInfo.name + "}"; + public Permission(@NonNull PermissionInfo permissionInfo, @PermissionType int type) { + mPermissionInfo = permissionInfo; + mType = type; + } + + @NonNull + public PermissionInfo getPermissionInfo() { + return mPermissionInfo; + } + + public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) { + if (permissionInfo != null) { + mPermissionInfo = permissionInfo; + } else { + final PermissionInfo newPermissionInfo = new PermissionInfo(); + newPermissionInfo.name = mPermissionInfo.name; + newPermissionInfo.packageName = mPermissionInfo.packageName; + newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel; + mPermissionInfo = newPermissionInfo; + } + mReconciled = permissionInfo != null; } @NonNull @@ -120,14 +130,11 @@ public final class BasePermission { return mPermissionInfo.protectionLevel; } + @NonNull public String getPackageName() { return mPermissionInfo.packageName; } - public boolean isPermissionDefinitionChanged() { - return mPermissionDefinitionChanged; - } - public int getType() { return mType; } @@ -136,34 +143,26 @@ public final class BasePermission { return mUid; } - public void setGids(@NonNull int[] gids, boolean gidsPerUser) { - mGids = gids; - mGidsPerUser = gidsPerUser; + public boolean hasGids() { + return mGids.length != 0; } - public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) { - if (permissionInfo != null) { - mPermissionInfo = permissionInfo; - } else { - final PermissionInfo newPermissionInfo = new PermissionInfo(); - newPermissionInfo.name = mPermissionInfo.name; - newPermissionInfo.packageName = mPermissionInfo.packageName; - newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel; - mPermissionInfo = newPermissionInfo; - } - mReconciled = permissionInfo != null; + @NonNull + public int[] getRawGids() { + return mGids; } - public void setPermissionDefinitionChanged(boolean shouldOverride) { - mPermissionDefinitionChanged = shouldOverride; + public boolean areGidsPerUser() { + return mGidsPerUser; } - public boolean hasGids() { - return mGids.length != 0; + public void setGids(@NonNull int[] gids, boolean gidsPerUser) { + mGids = gids; + mGidsPerUser = gidsPerUser; } @NonNull - public int[] computeGids(int userId) { + public int[] computeGids(@UserIdInt int userId) { if (mGidsPerUser) { final int[] userGids = new int[mGids.length]; for (int i = 0; i < mGids.length; i++) { @@ -176,19 +175,28 @@ public final class BasePermission { } } - public int calculateFootprint(BasePermission perm) { - if (mUid == perm.mUid) { - return perm.mPermissionInfo.name.length() + perm.mPermissionInfo.calculateFootprint(); + public boolean isDefinitionChanged() { + return mDefinitionChanged; + } + + public void setDefinitionChanged(boolean definitionChanged) { + mDefinitionChanged = definitionChanged; + } + + public int calculateFootprint(@NonNull Permission permission) { + if (mUid == permission.mUid) { + return permission.mPermissionInfo.name.length() + + permission.mPermissionInfo.calculateFootprint(); } return 0; } - public boolean isPermission(ParsedPermission perm) { + public boolean isPermission(@NonNull ParsedPermission parsedPermission) { if (mPermissionInfo == null) { return false; } - return Objects.equals(mPermissionInfo.packageName, perm.getPackageName()) - && Objects.equals(mPermissionInfo.name, perm.getName()); + return Objects.equals(mPermissionInfo.packageName, parsedPermission.getPackageName()) + && Objects.equals(mPermissionInfo.name, parsedPermission.getName()); } public boolean isDynamic() { @@ -323,8 +331,8 @@ public final class BasePermission { return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0; } - public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) { - if (!origPackageName.equals(mPermissionInfo.packageName)) { + public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) { + if (!oldPackageName.equals(mPermissionInfo.packageName)) { return; } final PermissionInfo newPermissionInfo = new PermissionInfo(); @@ -339,28 +347,29 @@ public final class BasePermission { } public boolean addToTree(@ProtectionLevel int protectionLevel, - @NonNull PermissionInfo permissionInfo, @NonNull BasePermission tree) { + @NonNull PermissionInfo permissionInfo, @NonNull Permission permissionTree) { final boolean changed = (mPermissionInfo.protectionLevel != protectionLevel || !mReconciled - || mUid != tree.mUid + || mUid != permissionTree.mUid || !Objects.equals(mPermissionInfo.packageName, - tree.mPermissionInfo.packageName) + permissionTree.mPermissionInfo.packageName) || !comparePermissionInfos(mPermissionInfo, permissionInfo)); mPermissionInfo = new PermissionInfo(permissionInfo); - mPermissionInfo.packageName = tree.mPermissionInfo.packageName; + mPermissionInfo.packageName = permissionTree.mPermissionInfo.packageName; mPermissionInfo.protectionLevel = protectionLevel; mReconciled = true; - mUid = tree.mUid; + mUid = permissionTree.mUid; return changed; } - public void updateDynamicPermission(Collection<BasePermission> permissionTrees) { - if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" - + getName() + " pkg=" + getPackageName() - + " info=" + mPermissionInfo); + public void updateDynamicPermission(@NonNull Collection<Permission> permissionTrees) { + if (PackageManagerService.DEBUG_SETTINGS) { + Log.v(TAG, "Dynamic permission: name=" + getName() + " pkg=" + getPackageName() + + " info=" + mPermissionInfo); + } if (mType == TYPE_DYNAMIC) { - final BasePermission tree = findPermissionTree(permissionTrees, mPermissionInfo.name); + final Permission tree = findPermissionTree(permissionTrees, mPermissionInfo.name); if (tree != null) { mPermissionInfo.packageName = tree.mPermissionInfo.packageName; mReconciled = true; @@ -369,75 +378,83 @@ public final class BasePermission { } } - static BasePermission createOrUpdate(PackageManagerInternal packageManagerInternal, - @Nullable BasePermission bp, @NonNull PermissionInfo p, - @NonNull AndroidPackage pkg, Collection<BasePermission> permissionTrees, + public static boolean isOverridingSystemPermission(@Nullable Permission permission, + @NonNull PermissionInfo permissionInfo, + @NonNull PackageManagerInternal packageManagerInternal) { + if (permission == null || Objects.equals(permission.mPermissionInfo.packageName, + permissionInfo.packageName)) { + return false; + } + if (!permission.mReconciled) { + return false; + } + final AndroidPackage currentPackage = packageManagerInternal.getPackage( + permission.mPermissionInfo.packageName); + if (currentPackage == null) { + return false; + } + return currentPackage.isSystem(); + } + + @NonNull + public static Permission createOrUpdate(@Nullable Permission permission, + @NonNull PermissionInfo permissionInfo, @NonNull AndroidPackage pkg, + @NonNull Collection<Permission> permissionTrees, boolean isOverridingSystemPermission, boolean chatty) { // Allow system apps to redefine non-system permissions boolean ownerChanged = false; - if (bp != null && !Objects.equals(bp.mPermissionInfo.packageName, p.packageName)) { - final boolean currentOwnerIsSystem; - if (!bp.mReconciled) { - currentOwnerIsSystem = false; - } else { - AndroidPackage currentPackage = packageManagerInternal.getPackage( - bp.mPermissionInfo.packageName); - if (currentPackage == null) { - currentOwnerIsSystem = false; - } else { - currentOwnerIsSystem = currentPackage.isSystem(); - } - } - + if (permission != null && !Objects.equals(permission.mPermissionInfo.packageName, + permissionInfo.packageName)) { if (pkg.isSystem()) { - if (bp.mType == BasePermission.TYPE_CONFIG && !bp.mReconciled) { + if (permission.mType == Permission.TYPE_CONFIG && !permission.mReconciled) { // It's a built-in permission and no owner, take ownership now - p.flags |= PermissionInfo.FLAG_INSTALLED; - bp.mPermissionInfo = p; - bp.mReconciled = true; - bp.mUid = pkg.getUid(); - } else if (!currentOwnerIsSystem) { - String msg = "New decl " + pkg + " of permission " - + p.name + " is system; overriding " + bp.mPermissionInfo.packageName; - PackageManagerService.reportSettingsProblem(Log.WARN, msg); + permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED; + permission.mPermissionInfo = permissionInfo; + permission.mReconciled = true; + permission.mUid = pkg.getUid(); + } else if (!isOverridingSystemPermission) { + Slog.w(TAG, "New decl " + pkg + " of permission " + + permissionInfo.name + " is system; overriding " + + permission.mPermissionInfo.packageName); ownerChanged = true; - bp = null; + permission = null; } } } - if (bp == null) { - bp = new BasePermission(p.name, p.packageName, TYPE_MANIFEST); + if (permission == null) { + permission = new Permission(permissionInfo.name, permissionInfo.packageName, + TYPE_MANIFEST); } - boolean wasNormal = bp.isNormal(); + boolean wasNormal = permission.isNormal(); StringBuilder r = null; - if (!bp.mReconciled) { - if (bp.mPermissionInfo.packageName == null - || bp.mPermissionInfo.packageName.equals(p.packageName)) { - final BasePermission tree = findPermissionTree(permissionTrees, p.name); + if (!permission.mReconciled) { + if (permission.mPermissionInfo.packageName == null + || permission.mPermissionInfo.packageName.equals(permissionInfo.packageName)) { + final Permission tree = findPermissionTree(permissionTrees, permissionInfo.name); if (tree == null - || tree.mPermissionInfo.packageName.equals(p.packageName)) { - p.flags |= PermissionInfo.FLAG_INSTALLED; - bp.mPermissionInfo = p; - bp.mReconciled = true; - bp.mUid = pkg.getUid(); + || tree.mPermissionInfo.packageName.equals(permissionInfo.packageName)) { + permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED; + permission.mPermissionInfo = permissionInfo; + permission.mReconciled = true; + permission.mUid = pkg.getUid(); if (chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } - r.append(p.name); + r.append(permissionInfo.name); } } else { - Slog.w(TAG, "Permission " + p.name + " from package " - + p.packageName + " ignored: base tree " + Slog.w(TAG, "Permission " + permissionInfo.name + " from package " + + permissionInfo.packageName + " ignored: base tree " + tree.mPermissionInfo.name + " is from package " + tree.mPermissionInfo.packageName); } } else { - Slog.w(TAG, "Permission " + p.name + " from package " - + p.packageName + " ignored: original from " - + bp.mPermissionInfo.packageName); + Slog.w(TAG, "Permission " + permissionInfo.name + " from package " + + permissionInfo.packageName + " ignored: original from " + + permission.mPermissionInfo.packageName); } } else if (chatty) { if (r == null) { @@ -446,42 +463,47 @@ public final class BasePermission { r.append(' '); } r.append("DUP:"); - r.append(p.name); + r.append(permissionInfo.name); } - if (bp.isRuntime() && (ownerChanged || wasNormal)) { + if (permission.isRuntime() && (ownerChanged || wasNormal)) { // If this is a runtime permission and the owner has changed, or this was a normal // permission, then permission state should be cleaned up - bp.mPermissionDefinitionChanged = true; + permission.mDefinitionChanged = true; } if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) { Log.d(TAG, " Permissions: " + r); } - return bp; + return permission; } - static BasePermission enforcePermissionTree( - Collection<BasePermission> permissionTrees, String permName, int callingUid) { - if (permName != null) { - BasePermission bp = findPermissionTree(permissionTrees, permName); - if (bp != null) { - if (bp.mUid == UserHandle.getAppId(callingUid)) { - return bp; + @NonNull + public static Permission enforcePermissionTree(@NonNull Collection<Permission> permissionTrees, + @NonNull String permissionName, int callingUid) { + if (permissionName != null) { + final Permission permissionTree = Permission.findPermissionTree(permissionTrees, + permissionName); + if (permissionTree != null) { + if (permissionTree.getUid() == UserHandle.getAppId(callingUid)) { + return permissionTree; } throw new SecurityException("Calling uid " + callingUid + " is not allowed to add to permission tree " - + bp.mPermissionInfo.name + " owned by uid " + bp.mUid); + + permissionTree.getName() + " owned by uid " + + permissionTree.getUid()); } } - throw new SecurityException("No permission tree found for " + permName); + throw new SecurityException("No permission tree found for " + permissionName); } - private static BasePermission findPermissionTree( - Collection<BasePermission> permissionTrees, String permName) { - for (BasePermission bp : permissionTrees) { - if (permName.startsWith(bp.mPermissionInfo.name) - && permName.length() > bp.mPermissionInfo.name.length() - && permName.charAt(bp.mPermissionInfo.name.length()) == '.') { - return bp; + @Nullable + private static Permission findPermissionTree(@NonNull Collection<Permission> permissionTrees, + @NonNull String permissionName) { + for (final Permission permissionTree : permissionTrees) { + final String permissionTreeName = permissionTree.getName(); + if (permissionName.startsWith(permissionTreeName) + && permissionName.length() > permissionTreeName.length() + && permissionName.charAt(permissionTreeName.length()) == '.') { + return permissionTree; } } return null; @@ -512,7 +534,7 @@ public final class BasePermission { @NonNull public PermissionInfo generatePermissionInfo(int flags, int targetSdkVersion) { - PermissionInfo permissionInfo; + final PermissionInfo permissionInfo; if (mPermissionInfo != null) { permissionInfo = new PermissionInfo(mPermissionInfo); if ((flags & PackageManager.GET_META_DATA) != PackageManager.GET_META_DATA) { @@ -539,79 +561,6 @@ public final class BasePermission { return permissionInfo; } - public static boolean readLPw(@NonNull Map<String, BasePermission> out, - @NonNull XmlPullParser parser) { - final String tagName = parser.getName(); - if (!tagName.equals(TAG_ITEM)) { - return false; - } - final String name = parser.getAttributeValue(null, ATTR_NAME); - final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); - final String ptype = parser.getAttributeValue(null, "type"); - if (name == null || packageName == null) { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Error in package manager settings: permissions has" + " no name at " - + parser.getPositionDescription()); - return false; - } - final boolean dynamic = "dynamic".equals(ptype); - BasePermission bp = out.get(name); - // If the permission is builtin, do not clobber it. - if (bp == null || bp.mType != TYPE_CONFIG) { - bp = new BasePermission(name.intern(), packageName, - dynamic ? TYPE_DYNAMIC : TYPE_MANIFEST); - } - bp.mPermissionInfo.protectionLevel = readInt(parser, null, "protection", - PermissionInfo.PROTECTION_NORMAL); - bp.mPermissionInfo.protectionLevel = PermissionInfo.fixProtectionLevel( - bp.mPermissionInfo.protectionLevel); - if (dynamic) { - bp.mPermissionInfo.icon = readInt(parser, null, "icon", 0); - bp.mPermissionInfo.nonLocalizedLabel = parser.getAttributeValue(null, "label"); - } - out.put(bp.mPermissionInfo.name, bp); - return true; - } - - private static int readInt(XmlPullParser parser, String ns, String name, int defValue) { - String v = parser.getAttributeValue(ns, name); - try { - if (v == null) { - return defValue; - } - return Integer.parseInt(v); - } catch (NumberFormatException e) { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Error in package manager settings: attribute " + name - + " has bad integer value " + v + " at " - + parser.getPositionDescription()); - } - return defValue; - } - - public void writeLPr(@NonNull XmlSerializer serializer) throws IOException { - if (mPermissionInfo.packageName == null) { - return; - } - serializer.startTag(null, TAG_ITEM); - serializer.attribute(null, ATTR_NAME, mPermissionInfo.name); - serializer.attribute(null, ATTR_PACKAGE, mPermissionInfo.packageName); - if (mPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_NORMAL) { - serializer.attribute(null, "protection", - Integer.toString(mPermissionInfo.protectionLevel)); - } - if (mType == TYPE_DYNAMIC) { - serializer.attribute(null, "type", "dynamic"); - if (mPermissionInfo.icon != 0) { - serializer.attribute(null, "icon", Integer.toString(mPermissionInfo.icon)); - } - if (mPermissionInfo.nonLocalizedLabel != null) { - serializer.attribute(null, "label", mPermissionInfo.nonLocalizedLabel.toString()); - } - } - serializer.endTag(null, TAG_ITEM); - } - private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) { if (pi1.icon != pi2.icon) return false; if (pi1.logo != pi2.logo) return false; @@ -627,42 +576,4 @@ public final class BasePermission { //if (pi1.descriptionRes != pi2.descriptionRes) return false; return true; } - - public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName, - @NonNull Set<String> permissionNames, boolean readEnforced, - boolean printedSomething, @NonNull DumpState dumpState) { - if (packageName != null && !packageName.equals(mPermissionInfo.packageName)) { - return false; - } - if (permissionNames != null && !permissionNames.contains(mPermissionInfo.name)) { - return false; - } - if (!printedSomething) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Permissions:"); - } - pw.print(" Permission ["); pw.print(mPermissionInfo.name); pw.print("] ("); - pw.print(Integer.toHexString(System.identityHashCode(this))); - pw.println("):"); - pw.print(" sourcePackage="); pw.println(mPermissionInfo.packageName); - pw.print(" uid="); pw.print(mUid); - pw.print(" gids="); pw.print(Arrays.toString(computeGids(UserHandle.USER_SYSTEM))); - pw.print(" type="); pw.print(mType); - pw.print(" prot="); - pw.println(PermissionInfo.protectionToString(mPermissionInfo.protectionLevel)); - if (mPermissionInfo != null) { - pw.print(" perm="); pw.println(mPermissionInfo); - if ((mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0 - || (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) { - pw.print(" flags=0x"); pw.println(Integer.toHexString(mPermissionInfo.flags)); - } - } - if (Objects.equals(mPermissionInfo.name, - android.Manifest.permission.READ_EXTERNAL_STORAGE)) { - pw.print(" enforced="); - pw.println(readEnforced); - } - return true; - } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index da4ef63d6945..81e8c2286755 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -224,6 +224,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { private PermissionControllerManager mPermissionControllerManager; /** Map of OneTimePermissionUserManagers keyed by userId */ + @GuardedBy("mLock") + @NonNull private final SparseArray<OneTimePermissionUserManager> mOneTimePermissionUserManagers = new SparseArray<>(); @@ -252,12 +254,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** Internal storage for permissions and related settings */ @GuardedBy("mLock") - private final PermissionSettings mSettings; + @NonNull + private final PermissionRegistry mRegistry = new PermissionRegistry(); /** Injector that can be used to facilitate testing. */ private final Injector mInjector; @GuardedBy("mLock") + @Nullable private ArraySet<String> mPrivappPermissionsViolations; @GuardedBy("mLock") @@ -267,13 +271,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { private PermissionPolicyInternal mPermissionPolicyInternal; /** - * For each foreground/background permission the mapping: - * Background permission -> foreground permissions - */ - @GuardedBy("mLock") - private ArrayMap<String, List<String>> mBackgroundPermissions; - - /** * A permission backup might contain apps that are not installed. In this case we delay the * restoration until the app is installed. * @@ -291,7 +288,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { @GuardedBy("mLock") private CheckPermissionDelegate mCheckPermissionDelegate; - @GuardedBy("mLock") + @NonNull private final OnPermissionChangeListeners mOnPermissionChangeListeners; @GuardedBy("mLock") @@ -309,7 +306,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // purposes. It may make sense to keep as an abstraction, but, the methods // necessary to be overridden may be different than what was initially needed // for the split. - private PermissionCallback mDefaultPermissionCallback = new PermissionCallback() { + private final PermissionCallback mDefaultPermissionCallback = new PermissionCallback() { @Override public void onGidsChanged(int appId, int userId) { mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED)); @@ -387,7 +384,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { mLock = externalLock; mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); mUserManagerInt = LocalServices.getService(UserManagerInternal.class); - mSettings = new PermissionSettings(mLock); mAppOpsManager = context.getSystemService(AppOpsManager.class); mHandlerThread = new ServiceThread(TAG, @@ -409,10 +405,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { synchronized (mLock) { for (int i=0; i<permConfig.size(); i++) { final SystemConfig.PermissionEntry perm = permConfig.valueAt(i); - BasePermission bp = mSettings.getPermissionLocked(perm.name); + Permission bp = mRegistry.getPermission(perm.name); if (bp == null) { - bp = new BasePermission(perm.name, "android", BasePermission.TYPE_CONFIG); - mSettings.putPermissionLocked(perm.name, bp); + bp = new Permission(perm.name, "android", Permission.TYPE_CONFIG); + mRegistry.addPermission(bp); } if (perm.gids != null) { bp.setGids(perm.gids, perm.perUser); @@ -484,13 +480,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - @Nullable - BasePermission getPermission(String permName) { - synchronized (mLock) { - return mSettings.getPermissionLocked(permName); - } - } - @Override public String[] getAppOpPermissionPackages(String permName) { return getAppOpPermissionPackagesInternal(permName, getCallingUid()); @@ -501,7 +490,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return null; } synchronized (mLock) { - final ArraySet<String> pkgs = mSettings.mAppOpPermissionPackages.get(permName); + final ArraySet<String> pkgs = mRegistry.getAppOpPermissionPackages(permName); if (pkgs == null) { return null; } @@ -518,9 +507,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { return ParceledListSlice.emptyList(); } synchronized (mLock) { - final int n = mSettings.mPermissionGroups.size(); - final ArrayList<PermissionGroupInfo> out = new ArrayList<>(n); - for (ParsedPermissionGroup pg : mSettings.mPermissionGroups.values()) { + final List<PermissionGroupInfo> out = new ArrayList<>(); + for (ParsedPermissionGroup pg : mRegistry.getPermissionGroups()) { out.add(PackageInfoUtils.generatePermissionGroupInfo(pg, flags)); } return new ParceledListSlice<>(out); @@ -538,7 +526,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } synchronized (mLock) { return PackageInfoUtils.generatePermissionGroupInfo( - mSettings.mPermissionGroups.get(groupName), flags); + mRegistry.getPermissionGroup(groupName), flags); } } @@ -555,7 +543,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int targetSdkVersion = getPermissionInfoCallingTargetSdkVersion(opPackage, callingUid); synchronized (mLock) { - final BasePermission bp = mSettings.getPermissionLocked(permName); + final Permission bp = mRegistry.getPermission(permName); if (bp == null) { return null; } @@ -585,11 +573,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { return null; } synchronized (mLock) { - if (groupName != null && !mSettings.mPermissionGroups.containsKey(groupName)) { + if (groupName != null && mRegistry.getPermissionGroup(groupName) == null) { return null; } final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10); - for (BasePermission bp : mSettings.mPermissions.values()) { + for (Permission bp : mRegistry.getPermissions()) { if (Objects.equals(bp.getGroup(), groupName)) { out.add(bp.generatePermissionInfo(flags)); } @@ -607,24 +595,23 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (info.labelRes == 0 && info.nonLocalizedLabel == null) { throw new SecurityException("Label must be specified in permission"); } - final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid); final boolean added; final boolean changed; synchronized (mLock) { - BasePermission bp = mSettings.getPermissionLocked(info.name); + final Permission tree = mRegistry.enforcePermissionTree(info.name, callingUid); + Permission bp = mRegistry.getPermission(info.name); added = bp == null; int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel); if (added) { enforcePermissionCapLocked(info, tree); - bp = new BasePermission(info.name, tree.getPackageName(), - BasePermission.TYPE_DYNAMIC); + bp = new Permission(info.name, tree.getPackageName(), Permission.TYPE_DYNAMIC); } else if (!bp.isDynamic()) { throw new SecurityException("Not allowed to modify non-dynamic permission " + info.name); } changed = bp.addToTree(fixedLevel, info, tree); if (added) { - mSettings.putPermissionLocked(info.name, bp); + mRegistry.addPermission(bp); } } if (changed) { @@ -639,9 +626,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant applications don't have access to this method"); } - final BasePermission tree = mSettings.enforcePermissionTree(permName, callingUid); synchronized (mLock) { - final BasePermission bp = mSettings.getPermissionLocked(permName); + mRegistry.enforcePermissionTree(permName, callingUid); + final Permission bp = mRegistry.getPermission(permName); if (bp == null) { return; } @@ -650,9 +637,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { Slog.wtf(TAG, "Not allowed to modify non-dynamic permission " + permName); } - mSettings.removePermissionLocked(permName); - mPackageManagerInt.writeSettings(false); + mRegistry.removePermission(permName); } + mPackageManagerInt.writeSettings(false); } @Override @@ -683,7 +670,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } synchronized (mLock) { - if (mSettings.getPermissionLocked(permName) == null) { + if (mRegistry.getPermission(permName) == null) { return 0; } @@ -808,14 +795,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - final BasePermission bp; + final boolean isRuntimePermission; final boolean permissionUpdated; synchronized (mLock) { - bp = mSettings.getPermissionLocked(permName); + final Permission bp = mRegistry.getPermission(permName); if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + permName); } + isRuntimePermission = bp.isRuntime(); + if (bp.isInstallerExemptIgnored()) { flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; } @@ -834,14 +823,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { permissionUpdated = uidState.updatePermissionFlags(bp, flagMask, flagValues); } - if (permissionUpdated && bp.isRuntime()) { + if (permissionUpdated && isRuntimePermission) { notifyRuntimePermissionStateChanged(packageName, userId); } if (permissionUpdated && callback != null) { // Install and runtime permissions are stored in different places, // so figure out what permission changed and persist the change. - if (!bp.isRuntime()) { - int userUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid())); + if (!isRuntimePermission) { + int userUid = UserHandle.getUid(userId, pkg.getUid()); callback.onInstallPermissionUpdatedNotifyListener(userUid); } else { callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false, pkg.getUid()); @@ -963,6 +952,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return PackageManager.PERMISSION_DENIED; } + @GuardedBy("mLock") private boolean checkSinglePermissionInternalLocked(@NonNull UidPermissionState uidState, @NonNull String permissionName, boolean isInstantApp) { if (!uidState.isPermissionGranted(permissionName)) { @@ -970,7 +960,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { } if (isInstantApp) { - return mSettings.isPermissionInstant(permissionName); + final Permission permission = mRegistry.getPermission(permissionName); + return permission != null && permission.isInstant(); } return true; @@ -1032,6 +1023,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return PackageManager.PERMISSION_DENIED; } + @GuardedBy("mLock") private boolean checkSingleUidPermissionInternalLocked(int uid, @NonNull String permissionName) { ArraySet<String> permissions = mSystemPermissions.get(uid); @@ -1097,9 +1089,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, "addOnPermissionsChangeListener"); - synchronized (mLock) { - mOnPermissionChangeListeners.addListenerLocked(listener); - } + mOnPermissionChangeListeners.addListener(listener); } @Override @@ -1107,9 +1097,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (mPackageManagerInt.getInstantAppPackageName(Binder.getCallingUid()) != null) { throw new SecurityException("Instant applications don't have access to this method"); } - synchronized (mLock) { - mOnPermissionChangeListeners.removeListenerLocked(listener); - } + mOnPermissionChangeListeners.removeListener(listener); } @Override @@ -1231,21 +1219,23 @@ public class PermissionManagerService extends IPermissionManager.Stub { private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission( @NonNull String permName) { + final boolean isImmutablyRestrictedPermission; synchronized (mLock) { - final BasePermission bp = mSettings.getPermissionLocked(permName); + final Permission bp = mRegistry.getPermission(permName); if (bp == null) { Slog.w(TAG, "No such permissions: " + permName); return false; } - if (bp.isHardOrSoftRestricted() && bp.isImmutablyRestricted() - && mContext.checkCallingOrSelfPermission( - Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Cannot modify whitelisting of an immutably " - + "restricted permission: " + permName); - } - return true; + isImmutablyRestrictedPermission = bp.isHardOrSoftRestricted() + && bp.isImmutablyRestricted(); + } + if (isImmutablyRestrictedPermission && mContext.checkCallingOrSelfPermission( + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Cannot modify whitelisting of an immutably " + + "restricted permission: " + permName); } + return true; } @Override @@ -1473,36 +1463,33 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown package: " + packageName); } - final BasePermission bp; + final boolean isSoftRestrictedPermission; synchronized (mLock) { - bp = mSettings.getPermissionLocked(permName); - } - if (bp == null) { - throw new IllegalArgumentException("Unknown permission: " + permName); - } - - if (!(bp.isRuntime() || bp.isDevelopment())) { - throw new SecurityException("Permission " + permName + " requested by " - + pkg.getPackageName() + " is not a changeable permission type"); + final Permission permission = mRegistry.getPermission(permName); + isSoftRestrictedPermission = permission != null && permission.isSoftRestricted(); } + final boolean mayGrantSoftRestrictedPermission = isSoftRestrictedPermission + && SoftRestrictedPermissionPolicy.forPermission(mContext, + pkg.toAppInfoWithoutState(), pkg, UserHandle.of(userId), permName) + .mayGrantPermission(); - // If a permission review is required for legacy apps we represent - // their permissions as always granted runtime ones since we need - // to keep the review required permission flag per user while an - // install permission's state is shared across all users. - if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M && bp.isRuntime()) { - return; - } + final boolean isRuntimePermission; + final boolean isDevelopmentPermission; + final boolean permissionHasGids; + synchronized (mLock) { + final Permission bp = mRegistry.getPermission(permName); + if (bp == null) { + throw new IllegalArgumentException("Unknown permission: " + permName); + } - if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext, - pkg.toAppInfoWithoutState(), pkg, UserHandle.of(userId), permName) - .mayGrantPermission()) { - Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package " - + packageName); - return; - } + isRuntimePermission = bp.isRuntime(); + isDevelopmentPermission = bp.isDevelopment(); + permissionHasGids = bp.hasGids(); + if (!(isRuntimePermission || isDevelopmentPermission)) { + throw new SecurityException("Permission " + permName + " requested by " + + pkg.getPackageName() + " is not a changeable permission type"); + } - synchronized (mLock) { final UidPermissionState uidState = getUidStateLocked(pkg, userId); if (uidState == null) { Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " @@ -1516,6 +1503,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { + " has not requested permission " + permName); } + // If a permission review is required for legacy apps we represent + // their permissions as always granted runtime ones since we need + // to keep the review required permission flag per user while an + // install permission's state is shared across all users. + if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M && bp.isRuntime()) { + return; + } + final int flags = uidState.getPermissionFlags(permName); if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { Log.e(TAG, "Cannot grant system fixed permission " @@ -1535,6 +1530,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { return; } + if (bp.isSoftRestricted() && !mayGrantSoftRestrictedPermission) { + Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package " + + packageName); + return; + } + if (bp.isDevelopment()) { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. @@ -1560,23 +1561,23 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - if (bp.isRuntime()) { + if (isRuntimePermission) { logPermission(MetricsEvent.ACTION_PERMISSION_GRANTED, permName, packageName); } - final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid())); + final int uid = UserHandle.getUid(userId, pkg.getUid()); if (callback != null) { - if (bp.isDevelopment()) { + if (isDevelopmentPermission) { callback.onInstallPermissionGranted(); } else { callback.onPermissionGranted(uid, userId); } - if (bp.hasGids()) { + if (permissionHasGids) { callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId); } } - if (bp.isRuntime()) { + if (isRuntimePermission) { notifyRuntimePermissionStateChanged(packageName, userId); } } @@ -1627,17 +1628,22 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { throw new IllegalArgumentException("Unknown package: " + packageName); } - final BasePermission bp = mSettings.getPermission(permName); - if (bp == null) { - throw new IllegalArgumentException("Unknown permission: " + permName); - } - - if (!(bp.isRuntime() || bp.isDevelopment())) { - throw new SecurityException("Permission " + permName + " requested by " - + pkg.getPackageName() + " is not a changeable permission type"); - } + final boolean isRuntimePermission; + final boolean isDevelopmentPermission; synchronized (mLock) { + final Permission bp = mRegistry.getPermission(permName); + if (bp == null) { + throw new IllegalArgumentException("Unknown permission: " + permName); + } + + isRuntimePermission = bp.isRuntime(); + isDevelopmentPermission = bp.isDevelopment(); + if (!(isRuntimePermission || isDevelopmentPermission)) { + throw new SecurityException("Permission " + permName + " requested by " + + pkg.getPackageName() + " is not a changeable permission type"); + } + final UidPermissionState uidState = getUidStateLocked(pkg, userId); if (uidState == null) { Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " @@ -1680,20 +1686,20 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - if (bp.isRuntime()) { + if (isRuntimePermission) { logPermission(MetricsEvent.ACTION_PERMISSION_REVOKED, permName, packageName); } if (callback != null) { - if (bp.isDevelopment()) { + if (isDevelopmentPermission) { mDefaultPermissionCallback.onInstallPermissionRevoked(); } else { - callback.onPermissionRevoked(UserHandle.getUid(userId, - UserHandle.getAppId(pkg.getUid())), userId, reason); + callback.onPermissionRevoked(UserHandle.getUid(userId, pkg.getUid()), userId, + reason); } } - if (bp.isRuntime()) { + if (isRuntimePermission) { notifyRuntimePermissionStateChanged(packageName, userId); } } @@ -1808,16 +1814,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = 0; i < permissionCount; i++) { final String permName = pkg.getRequestedPermissions().get(i); - final BasePermission bp; + + final boolean isRuntimePermission; synchronized (mLock) { - bp = mSettings.getPermissionLocked(permName); - } - if (bp == null) { - continue; - } + final Permission permission = mRegistry.getPermission(permName); + if (permission == null) { + continue; + } - if (bp.isRemoved()) { - continue; + if (permission.isRemoved()) { + continue; + } + isRuntimePermission = permission.isRuntime(); } // If shared user we just reset the state to which only this app contributed. @@ -1847,7 +1855,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // permission as requiring a review as this is the initial state. final int uid = mPackageManagerInt.getPackageUid(packageName, 0, userId); final int targetSdk = mPackageManagerInt.getUidTargetSdkVersion(uid); - final int flags = (targetSdk < Build.VERSION_CODES.M && bp.isRuntime()) + final int flags = (targetSdk < Build.VERSION_CODES.M && isRuntimePermission) ? FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKED_COMPAT : 0; @@ -1856,7 +1864,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { false, delayingPermCallback); // Below is only runtime permission handling. - if (!bp.isRuntime()) { + if (!isRuntimePermission) { continue; } @@ -2093,13 +2101,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { return false; } - BasePermission permission = getPermission(permName); - if (permission == null) { - return false; - } - if (permission.isHardRestricted() - && (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) { - return false; + synchronized (mLock) { + final Permission permission = mRegistry.getPermission(permName); + if (permission == null) { + return false; + } + if (permission.isHardRestricted() + && (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) { + return false; + } } final long token = Binder.clearCallingIdentity(); @@ -2178,8 +2188,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) { synchronized (mLock) { mHasNoDelayedPermBackup.delete(user.getIdentifier()); - mPermissionControllerManager.stageAndApplyRuntimePermissionsBackup(backup, user); } + mPermissionControllerManager.stageAndApplyRuntimePermissionsBackup(backup, user); } /** @@ -2198,18 +2208,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (mHasNoDelayedPermBackup.get(user.getIdentifier(), false)) { return; } - - mPermissionControllerManager.applyStagedRuntimePermissionBackup(packageName, user, - mContext.getMainExecutor(), (hasMoreBackup) -> { - if (hasMoreBackup) { - return; - } - - synchronized (mLock) { - mHasNoDelayedPermBackup.put(user.getIdentifier(), true); - } - }); } + mPermissionControllerManager.applyStagedRuntimePermissionBackup(packageName, user, + mContext.getMainExecutor(), (hasMoreBackup) -> { + if (hasMoreBackup) { + return; + } + synchronized (mLock) { + mHasNoDelayedPermBackup.put(user.getIdentifier(), true); + } + }); } private void addOnRuntimePermissionStateChangedListener(@NonNull @@ -2347,10 +2355,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int callingUid = Binder.getCallingUid(); for (int permNum = 0; permNum < numPermissions; permNum++) { - String permName = permissionsToRevoke.get(permNum); - BasePermission bp = mSettings.getPermission(permName); - if (bp == null || !bp.isRuntime()) { - continue; + final String permName = permissionsToRevoke.get(permNum); + synchronized (mLock) { + final Permission bp = mRegistry.getPermission(permName); + if (bp == null || !bp.isRuntime()) { + continue; + } } for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) { final int userId = userIds[userIdNum]; @@ -2388,7 +2398,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } } - bp.setPermissionDefinitionChanged(false); } } @@ -2401,13 +2410,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Assume by default that we did not install this permission into the system. p.setFlags(p.getFlags() & ~PermissionInfo.FLAG_INSTALLED); + final PermissionInfo permissionInfo; + final Permission oldPermission; synchronized (mLock) { // Now that permission groups have a special meaning, we ignore permission // groups for legacy apps to prevent unexpected behavior. In particular, // permissions for one app being granted to someone just because they happen // to be in a group defined by another app (before this had no implications). if (pkg.getTargetSdkVersion() > Build.VERSION_CODES.LOLLIPOP_MR1) { - p.setParsedPermissionGroup(mSettings.mPermissionGroups.get(p.getGroup())); + p.setParsedPermissionGroup(mRegistry.getPermissionGroup(p.getGroup())); // Warn for a permission in an unknown group. if (DEBUG_PERMISSIONS && p.getGroup() != null && p.getParsedPermissionGroup() == null) { @@ -2416,27 +2427,30 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - final PermissionInfo permissionInfo = PackageInfoUtils.generatePermissionInfo(p, + permissionInfo = PackageInfoUtils.generatePermissionInfo(p, PackageManager.GET_META_DATA); - final BasePermission bp; + oldPermission = p.isTree() ? mRegistry.getPermissionTree(p.getName()) + : mRegistry.getPermission(p.getName()); + } + // TODO(zhanghai): Maybe we should store whether a permission is owned by system inside + // itself. + final boolean isOverridingSystemPermission = Permission.isOverridingSystemPermission( + oldPermission, permissionInfo, mPackageManagerInt); + synchronized (mLock) { + final Permission permission = Permission.createOrUpdate(oldPermission, + permissionInfo, pkg, mRegistry.getPermissionTrees(), + isOverridingSystemPermission, chatty); if (p.isTree()) { - bp = BasePermission.createOrUpdate( - mPackageManagerInt, - mSettings.getPermissionTreeLocked(p.getName()), permissionInfo, pkg, - mSettings.getAllPermissionTreesLocked(), chatty); - mSettings.putPermissionTreeLocked(p.getName(), bp); + mRegistry.addPermissionTree(permission); } else { - bp = BasePermission.createOrUpdate( - mPackageManagerInt, - mSettings.getPermissionLocked(p.getName()), - permissionInfo, pkg, mSettings.getAllPermissionTreesLocked(), chatty); - mSettings.putPermissionLocked(p.getName(), bp); + mRegistry.addPermission(permission); } - if (bp.isInstalled()) { + if (permission.isInstalled()) { p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED); } - if (bp.isPermissionDefinitionChanged()) { + if (permission.isDefinitionChanged()) { definitionChangedPermissions.add(p.getName()); + permission.setDefinitionChanged(false); } } } @@ -2444,45 +2458,46 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private void addAllPermissionGroups(AndroidPackage pkg, boolean chatty) { - final int N = ArrayUtils.size(pkg.getPermissionGroups()); - StringBuilder r = null; - for (int i=0; i<N; i++) { - final ParsedPermissionGroup pg = pkg.getPermissionGroups().get(i); - final ParsedPermissionGroup cur = mSettings.mPermissionGroups.get(pg.getName()); - final String curPackageName = (cur == null) ? null : cur.getPackageName(); - final boolean isPackageUpdate = pg.getPackageName().equals(curPackageName); - if (cur == null || isPackageUpdate) { - mSettings.mPermissionGroups.put(pg.getName(), pg); - if (chatty && DEBUG_PACKAGE_SCANNING) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - if (isPackageUpdate) { - r.append("UPD:"); + synchronized (mLock) { + final int N = ArrayUtils.size(pkg.getPermissionGroups()); + StringBuilder r = null; + for (int i = 0; i < N; i++) { + final ParsedPermissionGroup pg = pkg.getPermissionGroups().get(i); + final ParsedPermissionGroup cur = mRegistry.getPermissionGroup(pg.getName()); + final String curPackageName = (cur == null) ? null : cur.getPackageName(); + final boolean isPackageUpdate = pg.getPackageName().equals(curPackageName); + if (cur == null || isPackageUpdate) { + mRegistry.addPermissionGroup(pg); + if (chatty && DEBUG_PACKAGE_SCANNING) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + if (isPackageUpdate) { + r.append("UPD:"); + } + r.append(pg.getName()); } - r.append(pg.getName()); - } - } else { - Slog.w(TAG, "Permission group " + pg.getName() + " from package " - + pg.getPackageName() + " ignored: original from " - + cur.getPackageName()); - if (chatty && DEBUG_PACKAGE_SCANNING) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); + } else { + Slog.w(TAG, "Permission group " + pg.getName() + " from package " + + pg.getPackageName() + " ignored: original from " + + cur.getPackageName()); + if (chatty && DEBUG_PACKAGE_SCANNING) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append("DUP:"); + r.append(pg.getName()); } - r.append("DUP:"); - r.append(pg.getName()); } } + if (r != null && DEBUG_PACKAGE_SCANNING) { + Log.d(TAG, " Permission Groups: " + r); + } } - if (r != null && DEBUG_PACKAGE_SCANNING) { - Log.d(TAG, " Permission Groups: " + r); - } - } private void removeAllPermissions(AndroidPackage pkg, boolean chatty) { @@ -2491,9 +2506,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { StringBuilder r = null; for (int i=0; i<N; i++) { ParsedPermission p = pkg.getPermissions().get(i); - BasePermission bp = mSettings.mPermissions.get(p.getName()); + Permission bp = mRegistry.getPermission(p.getName()); if (bp == null) { - bp = mSettings.mPermissionTrees.get(p.getName()); + bp = mRegistry.getPermissionTree(p.getName()); } if (bp != null && bp.isPermission(p)) { bp.setPermissionInfo(null); @@ -2507,11 +2522,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } if (p.isAppOp()) { - ArraySet<String> appOpPkgs = - mSettings.mAppOpPermissionPackages.get(p.getName()); - if (appOpPkgs != null) { - appOpPkgs.remove(pkg.getPackageName()); - } + // TODO(zhanghai): Should we just remove the entry for this permission directly? + mRegistry.removeAppOpPermissionPackage(p.getName(), pkg.getPackageName()); } } if (r != null) { @@ -2521,15 +2533,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { N = pkg.getRequestedPermissions().size(); r = null; for (int i=0; i<N; i++) { - String perm = pkg.getRequestedPermissions().get(i); - if (mSettings.isPermissionAppOp(perm)) { - ArraySet<String> appOpPkgs = mSettings.mAppOpPermissionPackages.get(perm); - if (appOpPkgs != null) { - appOpPkgs.remove(pkg.getPackageName()); - if (appOpPkgs.isEmpty()) { - mSettings.mAppOpPermissionPackages.remove(perm); - } - } + final String permissionName = pkg.getRequestedPermissions().get(i); + final Permission permission = mRegistry.getPermission(permissionName); + if (permission != null && permission.isAppOp()) { + mRegistry.removeAppOpPermissionPackage(permissionName, + pkg.getPackageName()); } } if (r != null) { @@ -2567,7 +2575,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final Set<String> instantPermissions = new ArraySet<>(uidState.getGrantedPermissions()); instantPermissions.removeIf(permissionName -> { - BasePermission permission = mSettings.getPermission(permissionName); + Permission permission = mRegistry.getPermission(permissionName); if (permission == null) { return true; } @@ -2585,11 +2593,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { @NonNull private int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) { - BasePermission permission = mSettings.getPermission(permissionName); - if (permission == null) { - return EmptyArray.INT; + synchronized (mLock) { + Permission permission = mRegistry.getPermission(permissionName); + if (permission == null) { + return EmptyArray.INT; + } + return permission.computeGids(userId); } - return permission.computeGids(userId); } /** @@ -2638,12 +2648,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = 0; i < requestedPermissionsSize; i++) { final String permissionName = pkg.getRequestedPermissions().get(i); - final BasePermission permission = mSettings.getPermission(permissionName); + final Permission permission; + synchronized (mLock) { + permission = mRegistry.getPermission(permissionName); + } if (permission == null) { continue; } - if (permission.isSignature() && shouldGrantSignaturePermission(pkg, ps, - permission)) { + if (permission.isSignature() && shouldGrantSignaturePermission(pkg, ps, permission)) { shouldGrantSignaturePermission.add(permissionName); } } @@ -2683,7 +2695,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } for (String permissionName : uidRequestedPermissions) { - BasePermission permission = mSettings.getPermission(permissionName); + Permission permission = mRegistry.getPermission(permissionName); if (permission == null) { continue; } @@ -2725,12 +2737,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { // the runtime ones are written only if changed. The only cases of // changed runtime permissions here are promotion of an install to // runtime and revocation of a runtime from a shared user. - synchronized (mLock) { - if (revokeUnusedSharedUserPermissionsLocked( - ps.getSharedUser().getPackages(), uidState)) { - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); - runtimePermissionsRevoked = true; - } + if (revokeUnusedSharedUserPermissionsLocked( + ps.getSharedUser().getPackages(), uidState)) { + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); + runtimePermissionsRevoked = true; } } } @@ -2741,7 +2751,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = 0; i < requestedPermissionsSize; i++) { final String permName = requestedPermissions.get(i); - final BasePermission bp = mSettings.getPermission(permName); + final Permission bp = mRegistry.getPermission(permName); final boolean appSupportsRuntimePermissions = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M; String legacyActivityRecognitionPermission = null; @@ -2834,7 +2844,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Keep track of app op permissions. if (bp.isAppOp()) { - mSettings.addAppOpPackage(perm, pkg.getPackageName()); + mRegistry.addAppOpPermissionPackage(perm, pkg.getPackageName()); } boolean shouldGrantNormalPermission = true; @@ -3049,6 +3059,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { * * @return The updated value of the {@code updatedUserIds} parameter */ + @GuardedBy("mLock") private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull UidPermissionState ps, @NonNull AndroidPackage pkg, int userId, @NonNull int[] updatedUserIds) { String pkgName = pkg.getPackageName(); @@ -3057,7 +3068,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (String permission : ps.getGrantedPermissions()) { if (!pkg.getImplicitPermissions().contains(permission)) { - BasePermission bp = mSettings.getPermissionLocked(permission); + Permission bp = mRegistry.getPermission(permission); if (bp != null && bp.isRuntime()) { int flags = ps.getPermissionFlags(permission); @@ -3101,6 +3112,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @param ps The permission state of the package * @param pkg The package requesting the permissions */ + @GuardedBy("mLock") private void inheritPermissionStateToNewImplicitPermissionLocked( @NonNull ArraySet<String> sourcePerms, @NonNull String newPerm, @NonNull UidPermissionState ps, @NonNull AndroidPackage pkg) { @@ -3131,11 +3143,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { + " for " + pkgName); } - ps.grantPermission(mSettings.getPermissionLocked(newPerm)); + ps.grantPermission(mRegistry.getPermission(newPerm)); } // Add permission flags - ps.updatePermissionFlags(mSettings.getPermission(newPerm), flags, flags); + ps.updatePermissionFlags(mRegistry.getPermission(newPerm), flags, flags); } /** @@ -3172,6 +3184,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { * * @return List of users for which the permission state has been changed */ + @GuardedBy("mLock") private @NonNull int[] setInitialGrantForNewImplicitPermissionsLocked( @NonNull UidPermissionState origPs, @NonNull UidPermissionState ps, @NonNull AndroidPackage pkg, @NonNull ArraySet<String> newImplicitPermissions, @@ -3206,7 +3219,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { ArraySet<String> sourcePerms = newToSplitPerms.get(newPerm); if (sourcePerms != null) { - BasePermission bp = mSettings.getPermissionLocked(newPerm); + Permission bp = mRegistry.getPermission(newPerm); if (bp.isRuntime()) { if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) { @@ -3221,7 +3234,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size(); sourcePermNum++) { final String sourcePerm = sourcePerms.valueAt(sourcePermNum); - BasePermission sourceBp = mSettings.getPermissionLocked(sourcePerm); + Permission sourceBp = mRegistry.getPermission(sourcePerm); if (!sourceBp.isRuntime()) { inheritsFromInstallPerm = true; break; @@ -3344,7 +3357,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private boolean shouldGrantSignaturePermission(@NonNull AndroidPackage pkg, - @NonNull PackageSetting pkgSetting, @NonNull BasePermission bp) { + @NonNull PackageSetting pkgSetting, @NonNull Permission bp) { // expect single system package String systemPackageName = ArrayUtils.firstOrNull(mPackageManagerInt.getKnownPackageNames( PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM)); @@ -3510,7 +3523,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { @NonNull private PackageParser.SigningDetails getSourcePackageSigningDetails( - @NonNull BasePermission bp) { + @NonNull Permission bp) { final PackageSetting ps = getSourcePackageSetting(bp); if (ps == null) { return PackageParser.SigningDetails.UNKNOWN; @@ -3519,13 +3532,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Nullable - private PackageSetting getSourcePackageSetting(@NonNull BasePermission bp) { + private PackageSetting getSourcePackageSetting(@NonNull Permission bp) { final String sourcePackageName = bp.getPackageName(); return mPackageManagerInt.getPackageSetting(sourcePackageName); } private boolean canGrantPrivilegedPermission(@NonNull AndroidPackage pkg, - boolean isUpdatedSystemApp, @NonNull BasePermission permission) { + boolean isUpdatedSystemApp, @NonNull Permission permission) { if (!pkg.isPrivileged()) { return false; } @@ -3563,11 +3576,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { + packageName + " (" + pkg.getPath() + ") not in privapp-permissions whitelist"); if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) { - if (mPrivappPermissionsViolations == null) { - mPrivappPermissionsViolations = new ArraySet<>(); + synchronized (mLock) { + if (mPrivappPermissionsViolations == null) { + mPrivappPermissionsViolations = new ArraySet<>(); + } + mPrivappPermissionsViolations.add(packageName + " (" + pkg.getPath() + "): " + + permissionName); } - mPrivappPermissionsViolations.add(packageName + " (" + pkg.getPath() + "): " - + permissionName); } } } @@ -3671,15 +3686,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.getPackageName(), userId); for (String permission : pkg.getRequestedPermissions()) { - final BasePermission bp; + final boolean shouldGrantPermission; synchronized (mLock) { - bp = mSettings.getPermissionLocked(permission); - } - if (bp != null && (bp.isRuntime() || bp.isDevelopment()) - && (!instantApp || bp.isInstant()) - && (supportsRuntimePermissions || !bp.isRuntimeOnly()) - && (grantedPermissions == null - || ArrayUtils.contains(grantedPermissions, permission))) { + final Permission bp = mRegistry.getPermission(permission); + shouldGrantPermission = bp != null && (bp.isRuntime() || bp.isDevelopment()) + && (!instantApp || bp.isInstant()) + && (supportsRuntimePermissions || !bp.isRuntimeOnly()) + && (grantedPermissions == null + || ArrayUtils.contains(grantedPermissions, permission)); + } + if (shouldGrantPermission) { final int flags = getPermissionFlagsInternal(permission, pkg.getPackageName(), callingUid, userId); if (supportsRuntimePermissions) { @@ -3713,14 +3729,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int j = 0; j < permissionCount; j++) { final String permissionName = pkg.getRequestedPermissions().get(j); - final BasePermission bp = mSettings.getPermissionLocked(permissionName); - - if (bp == null || !bp.isHardOrSoftRestricted()) { - continue; - } - final boolean isGranted; synchronized (mLock) { + final Permission bp = mRegistry.getPermission(permissionName); + if (bp == null || !bp.isHardOrSoftRestricted()) { + continue; + } + final UidPermissionState uidState = getUidStateLocked(pkg, userId); if (uidState == null) { Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() @@ -3874,11 +3889,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { int affectedUserId = UserHandle.USER_NULL; // Update permissions for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) { - BasePermission bp = mSettings.getPermission(eachPerm); - if (bp == null) { - continue; - } - // Check if another package in the shared user needs the permission. boolean used = false; final List<AndroidPackage> pkgs = sus.getPackages(); @@ -3922,6 +3932,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { continue; } + Permission bp = mRegistry.getPermission(eachPerm); + if (bp == null) { + continue; + } + // TODO(zhanghai): Why are we only killing the UID when GIDs changed, instead of any // permission change? if (uidState.removePermissionState(bp.getName()) && bp.hasGids()) { @@ -3948,7 +3963,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int requestedPermCount = pkg.getRequestedPermissions().size(); for (int j = 0; j < requestedPermCount; j++) { String permission = pkg.getRequestedPermissions().get(j); - BasePermission bp = mSettings.getPermissionLocked(permission); + Permission bp = mRegistry.getPermission(permission); if (bp != null) { usedPermissions.add(permission); } @@ -3963,7 +3978,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = permissionStatesSize - 1; i >= 0; i--) { PermissionState permissionState = permissionStates.get(i); if (!usedPermissions.contains(permissionState.getName())) { - BasePermission bp = mSettings.getPermissionLocked(permissionState.getName()); + Permission bp = mRegistry.getPermission(permissionState.getName()); if (bp != null) { if (uidState.removePermissionState(bp.getName()) && permissionState.isRuntime()) { @@ -4026,35 +4041,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } /** - * Cache background->foreground permission mapping. - * - * <p>This is only run once. - */ - private void cacheBackgroundToForegoundPermissionMapping() { - synchronized (mLock) { - if (mBackgroundPermissions == null) { - // Cache background -> foreground permission mapping. - // Only system declares background permissions, hence mapping does never change. - mBackgroundPermissions = new ArrayMap<>(); - for (BasePermission bp : mSettings.getAllPermissionsLocked()) { - if (bp.getBackgroundPermission() != null) { - String fgPerm = bp.getName(); - String bgPerm = bp.getBackgroundPermission(); - - List<String> fgPerms = mBackgroundPermissions.get(bgPerm); - if (fgPerms == null) { - fgPerms = new ArrayList<>(); - mBackgroundPermissions.put(bgPerm, fgPerms); - } - - fgPerms.add(fgPerm); - } - } - } - } - } - - /** * Update all packages on the volume, <u>beside</u> the changing package. If the changing * package is set too, all packages are updated. */ @@ -4127,8 +4113,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { flags |= UPDATE_PERMISSIONS_ALL; } - cacheBackgroundToForegoundPermissionMapping(); - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "restorePermissionState"); // Now update the permissions for all packages. if ((flags & UPDATE_PERMISSIONS_ALL) != 0) { @@ -4180,13 +4164,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } boolean changed = false; - Set<BasePermission> needsUpdate = null; + Set<Permission> needsUpdate = null; synchronized (mLock) { - final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator(); - while (it.hasNext()) { - final BasePermission bp = it.next(); + for (final Permission bp : mRegistry.getPermissions()) { if (bp.isDynamic()) { - bp.updateDynamicPermission(mSettings.mPermissionTrees.values()); + bp.updateDynamicPermission(mRegistry.getPermissionTrees()); } if (!packageName.equals(bp.getPackageName())) { // Not checking sourcePackageSetting because it can be null when @@ -4198,13 +4180,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Set to changed for either install or uninstall changed = true; if (needsUpdate == null) { - needsUpdate = new ArraySet<>(mSettings.mPermissions.size()); + needsUpdate = new ArraySet<>(); } needsUpdate.add(bp); } } if (needsUpdate != null) { - for (final BasePermission bp : needsUpdate) { + for (final Permission bp : needsUpdate) { // If the target package is being uninstalled, we need to revoke this permission // From all other packages if (pkg == null || !hasPermission(pkg, bp.getName())) { @@ -4236,7 +4218,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { } }); } - mSettings.removePermissionLocked(bp.getName()); + synchronized (mLock) { + mRegistry.removePermission(bp.getName()); + } continue; } final AndroidPackage sourcePkg = @@ -4250,7 +4234,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } Slog.w(TAG, "Removing dangling permission: " + bp.getName() + " from package " + bp.getPackageName()); - mSettings.removePermissionLocked(bp.getName()); + mRegistry.removePermission(bp.getName()); } } } @@ -4316,11 +4300,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } boolean changed = false; - Set<BasePermission> needsUpdate = null; + Set<Permission> needsUpdate = null; synchronized (mLock) { - final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator(); + final Iterator<Permission> it = mRegistry.getPermissionTrees().iterator(); while (it.hasNext()) { - final BasePermission bp = it.next(); + final Permission bp = it.next(); if (!packageName.equals(bp.getPackageName())) { // Not checking sourcePackageSetting because it can be null when // the permission source package is the target package and the target package is @@ -4336,13 +4320,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { it.remove(); } if (needsUpdate == null) { - needsUpdate = new ArraySet<>(mSettings.mPermissionTrees.size()); + needsUpdate = new ArraySet<>(); } needsUpdate.add(bp); } } if (needsUpdate != null) { - for (final BasePermission bp : needsUpdate) { + for (final Permission bp : needsUpdate) { final AndroidPackage sourcePkg = mPackageManagerInt.getPackage(bp.getPackageName()); final PackageSetting sourcePs = @@ -4354,7 +4338,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } Slog.w(TAG, "Removing dangling permission tree: " + bp.getName() + " from package " + bp.getPackageName()); - mSettings.removePermissionLocked(bp.getName()); + mRegistry.removePermission(bp.getName()); } } } @@ -4535,17 +4519,17 @@ public class PermissionManagerService extends IPermissionManager.Stub { return builder.toString(); } - @GuardedBy({"mSettings.mLock", "mLock"}) - private int calculateCurrentPermissionFootprintLocked(BasePermission tree) { + @GuardedBy("mLock") + private int calculateCurrentPermissionFootprintLocked(@NonNull Permission permissionTree) { int size = 0; - for (BasePermission perm : mSettings.mPermissions.values()) { - size += tree.calculateFootprint(perm); + for (final Permission permission : mRegistry.getPermissions()) { + size += permissionTree.calculateFootprint(permission); } return size; } - @GuardedBy({"mSettings.mLock", "mLock"}) - private void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) { + @GuardedBy("mLock") + private void enforcePermissionCapLocked(PermissionInfo info, Permission tree) { // We calculate the max size of permissions defined by this uid and throw // if that plus the size of 'info' would exceed our stated maximum. if (tree.getUid() != Process.SYSTEM_UID) { @@ -4558,9 +4542,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void systemReady() { mSystemReady = true; - if (mPrivappPermissionsViolations != null) { - throw new IllegalStateException("Signature|privileged permissions not in " - + "privapp-permissions whitelist: " + mPrivappPermissionsViolations); + + synchronized (mLock) { + if (mPrivappPermissionsViolations != null) { + throw new IllegalStateException("Signature|privileged permissions not in " + + "privapp-permissions whitelist: " + mPrivappPermissionsViolations); + } } mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); @@ -4628,29 +4615,21 @@ public class PermissionManagerService extends IPermissionManager.Stub { mMetricsLogger.write(log); } - /** - * Get the mapping of background permissions to their foreground permissions. - * - * <p>Only initialized in the system server. - * - * @return the map <bg permission -> list<fg perm>> - */ - public @Nullable ArrayMap<String, List<String>> getBackgroundPermissions() { - return mBackgroundPermissions; - } - + @GuardedBy("mLock") @Nullable private UidPermissionState getUidStateLocked(@NonNull PackageSetting ps, @UserIdInt int userId) { return getUidStateLocked(ps.getAppId(), userId); } + @GuardedBy("mLock") @Nullable private UidPermissionState getUidStateLocked(@NonNull AndroidPackage pkg, @UserIdInt int userId) { return getUidStateLocked(pkg.getUid(), userId); } + @GuardedBy("mLock") @Nullable private UidPermissionState getUidStateLocked(@AppIdInt int appId, @UserIdInt int userId) { final UserPermissionState userState = mState.getUserState(userId); @@ -4670,7 +4649,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - private void readStateFromPackageSettings() { + private void readLegacyPermissionState() { final int[] userIds = getAllUserIds(); mPackageManagerInt.forEachPackageSetting(ps -> { final int appId = ps.getAppId(); @@ -4684,24 +4663,31 @@ public class PermissionManagerService extends IPermissionManager.Stub { final UidPermissionState uidState = userState.getOrCreateUidState(appId); uidState.reset(); uidState.setMissing(legacyState.isMissing(userId)); - readStateFromPermissionStates(uidState, + readLegacyPermissionStatesLocked(uidState, legacyState.getInstallPermissionStates()); - readStateFromPermissionStates(uidState, + readLegacyPermissionStatesLocked(uidState, legacyState.getRuntimePermissionStates(userId)); } } }); } - private void readStateFromPermissionStates(@NonNull UidPermissionState uidState, + @GuardedBy("mLock") + private void readLegacyPermissionStatesLocked(@NonNull UidPermissionState uidState, @NonNull Collection<LegacyPermissionState.PermissionState> permissionStates) { for (final LegacyPermissionState.PermissionState permissionState : permissionStates) { - uidState.putPermissionState(permissionState.getPermission(), - permissionState.isGranted(), permissionState.getFlags()); + final String permissionName = permissionState.getName(); + final Permission permission = mRegistry.getPermission(permissionName); + if (permission == null) { + Slog.w(TAG, "Unknown permission: " + permissionName); + continue; + } + uidState.putPermissionState(permission, permissionState.isGranted(), + permissionState.getFlags()); } } - private void writeStateToPackageSettings() { + private void writeLegacyPermissionState() { final int[] userIds; synchronized (mLock) { userIds = mState.getUserIds(); @@ -4738,12 +4724,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { final PermissionState permissionState = permissionStates.get(i); final LegacyPermissionState.PermissionState legacyPermissionState = - new LegacyPermissionState.PermissionState( - permissionState.getPermission(), + new LegacyPermissionState.PermissionState(permissionState.getName(), permissionState.isGranted(), permissionState.getFlags()); if (permissionState.isRuntime()) { - legacyState.putRuntimePermissionState(legacyPermissionState, - userId); + legacyState.putRuntimePermissionState(legacyPermissionState, userId); } else { legacyState.putInstallPermissionState(legacyPermissionState); } @@ -4753,6 +4737,108 @@ public class PermissionManagerService extends IPermissionManager.Stub { }); } + private void readLegacyPermissions(@NonNull LegacyPermissionSettings legacyPermissionSettings) { + for (int readPermissionOrPermissionTree = 0; readPermissionOrPermissionTree < 2; + readPermissionOrPermissionTree++) { + final List<LegacyPermission> legacyPermissions = readPermissionOrPermissionTree == 0 + ? legacyPermissionSettings.getPermissions() + : legacyPermissionSettings.getPermissionTrees(); + synchronized (mLock) { + final int legacyPermissionsSize = legacyPermissions.size(); + for (int i = 0; i < legacyPermissionsSize; i++) { + final LegacyPermission legacyPermission = legacyPermissions.get(i); + final Permission permission = new Permission( + legacyPermission.getPermissionInfo(), legacyPermission.getType()); + if (readPermissionOrPermissionTree == 0) { + // Config permissions are currently read in PermissionManagerService + // constructor. The old behavior was to add other attributes to the config + // permission in LegacyPermission.read(), so equivalently we can add the + // GIDs to the new permissions here, since config permissions created in + // PermissionManagerService constructor get only their names and GIDs there. + final Permission configPermission = mRegistry.getPermission( + permission.getName()); + if (configPermission != null + && configPermission.getType() == Permission.TYPE_CONFIG) { + permission.setGids(configPermission.getRawGids(), + configPermission.areGidsPerUser()); + } + mRegistry.addPermission(permission); + } else { + mRegistry.addPermissionTree(permission); + } + } + } + } + } + + private void writeLegacyPermissions( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + for (int writePermissionOrPermissionTree = 0; writePermissionOrPermissionTree < 2; + writePermissionOrPermissionTree++) { + final List<LegacyPermission> legacyPermissions = new ArrayList<>(); + synchronized (mLock) { + final Collection<Permission> permissions = writePermissionOrPermissionTree == 0 + ? mRegistry.getPermissions() : mRegistry.getPermissionTrees(); + for (final Permission permission : permissions) { + // We don't need to provide UID and GIDs, which are only retrieved when dumping. + final LegacyPermission legacyPermission = new LegacyPermission( + permission.getPermissionInfo(), permission.getType(), 0, + EmptyArray.INT); + legacyPermissions.add(legacyPermission); + } + } + if (writePermissionOrPermissionTree == 0) { + legacyPermissionSettings.replacePermissions(legacyPermissions); + } else { + legacyPermissionSettings.replacePermissionTrees(legacyPermissions); + } + } + } + + private void transferPermissions(@NonNull String oldPackageName, + @NonNull String newPackageName) { + synchronized (mLock) { + mRegistry.transferPermissions(oldPackageName, newPackageName); + } + } + + private boolean canPropagatePermissionToInstantApp(@NonNull String permissionName) { + synchronized (mLock) { + final Permission bp = mRegistry.getPermission(permissionName); + return bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant(); + } + } + + @NonNull + private List<LegacyPermission> getLegacyPermissions() { + synchronized (mLock) { + final List<LegacyPermission> legacyPermissions = new ArrayList<>(); + for (final Permission permission : mRegistry.getPermissions()) { + final LegacyPermission legacyPermission = new LegacyPermission( + permission.getPermissionInfo(), permission.getType(), permission.getUid(), + permission.getRawGids()); + legacyPermissions.add(legacyPermission); + } + return legacyPermissions; + } + } + + @NonNull + private Map<String, Set<String>> getAllAppOpPermissionPackages() { + synchronized (mLock) { + final ArrayMap<String, ArraySet<String>> appOpPermissionPackages = + mRegistry.getAllAppOpPermissionPackages(); + final Map<String, Set<String>> deepClone = new ArrayMap<>(); + final int appOpPermissionPackagesSize = appOpPermissionPackages.size(); + for (int i = 0; i < appOpPermissionPackagesSize; i++) { + final String appOpPermission = appOpPermissionPackages.keyAt(i); + final ArraySet<String> packageNames = appOpPermissionPackages.valueAt(i); + deepClone.put(appOpPermission, new ArraySet<>(packageNames)); + } + return deepClone; + } + } + @NonNull private LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { final LegacyPermissionState legacyState = new LegacyPermissionState(); @@ -4772,9 +4858,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { final PermissionState permissionState = permissionStates.get(i); final LegacyPermissionState.PermissionState legacyPermissionState = - new LegacyPermissionState.PermissionState( - permissionState.getPermission(), permissionState.isGranted(), - permissionState.getFlags()); + new LegacyPermissionState.PermissionState(permissionState.getName(), + permissionState.isGranted(), permissionState.getFlags()); if (permissionState.isRuntime()) { legacyState.putRuntimePermissionState(legacyPermissionState, userId); } else if (userId == UserHandle.USER_SYSTEM) { @@ -4842,12 +4927,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { PermissionManagerService.this.removeAllPermissions(pkg, chatty); } @Override - public void readStateFromPackageSettingsTEMP() { - PermissionManagerService.this.readStateFromPackageSettings(); + public void readLegacyPermissionStateTEMP() { + PermissionManagerService.this.readLegacyPermissionState(); } @Override - public void writeStateToPackageSettingsTEMP() { - PermissionManagerService.this.writeStateToPackageSettings(); + public void writeLegacyPermissionStateTEMP() { + PermissionManagerService.this.writeLegacyPermissionState(); } @Override public void onUserRemoved(@UserIdInt int userId) { @@ -4950,17 +5035,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public void enforceGrantRevokeRuntimePermissionPermissions(String message) { - PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message); - } - @Override - public PermissionSettings getPermissionSettings() { - return mSettings; - } - @Override - public BasePermission getPermissionTEMP(String permName) { - synchronized (PermissionManagerService.this.mLock) { - return mSettings.getPermissionLocked(permName); + public Permission getPermissionTEMP(String permName) { + synchronized (mLock) { + return mRegistry.getPermission(permName); } } @@ -4970,13 +5047,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { ArrayList<PermissionInfo> matchingPermissions = new ArrayList<>(); synchronized (mLock) { - int numTotalPermissions = mSettings.mPermissions.size(); - - for (int i = 0; i < numTotalPermissions; i++) { - BasePermission bp = mSettings.mPermissions.valueAt(i); - - if (bp.getProtection() == protection) { - matchingPermissions.add(bp.generatePermissionInfo(0)); + for (final Permission permission : mRegistry.getPermissions()) { + if (permission.getProtection() == protection) { + matchingPermissions.add(permission.generatePermissionInfo(0)); } } } @@ -4990,13 +5063,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { ArrayList<PermissionInfo> matchingPermissions = new ArrayList<>(); synchronized (mLock) { - int numTotalPermissions = mSettings.mPermissions.size(); - - for (int i = 0; i < numTotalPermissions; i++) { - BasePermission bp = mSettings.mPermissions.valueAt(i); - - if ((bp.getProtectionFlags() & protectionFlags) == protectionFlags) { - matchingPermissions.add(bp.generatePermissionInfo(0)); + for (final Permission permission : mRegistry.getPermissions()) { + if ((permission.getProtectionFlags() & protectionFlags) == protectionFlags) { + matchingPermissions.add(permission.generatePermissionInfo(0)); } } } @@ -5187,25 +5256,62 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissions) { + public void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissionNames) { synchronized (mLock) { - Iterator<String> iterator = permissions.iterator(); + Iterator<String> iterator = permissionNames.iterator(); while (iterator.hasNext()) { - String permission = iterator.next(); - BasePermission basePermission = mSettings.mPermissions.get(permission); - if (basePermission == null || !basePermission.isHardOrSoftRestricted()) { + final String permissionName = iterator.next(); + final Permission permission = mRegistry.getPermission(permissionName); + if (permission == null || !permission.isHardOrSoftRestricted()) { iterator.remove(); } } } } + @Override + public void readLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + PermissionManagerService.this.readLegacyPermissions(legacyPermissionSettings); + } + + @Override + public void writeLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + PermissionManagerService.this.writeLegacyPermissions(legacyPermissionSettings); + } + + @Override + public void transferPermissions(@NonNull String oldPackageName, + @NonNull String newPackageName) { + PermissionManagerService.this.transferPermissions(oldPackageName, newPackageName); + } + + @Override + public boolean canPropagatePermissionToInstantApp(@NonNull String permissionName) { + return PermissionManagerService.this.canPropagatePermissionToInstantApp(permissionName); + } + + @NonNull + @Override + public List<LegacyPermission> getLegacyPermissions() { + return PermissionManagerService.this.getLegacyPermissions(); + } + + @NonNull + @Override + public Map<String, Set<String>> getAllAppOpPermissionPackages() { + return PermissionManagerService.this.getAllAppOpPermissionPackages(); + } + @NonNull + @Override public LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { return PermissionManagerService.this.getLegacyPermissionState(appId); } @NonNull + @Override public int[] getGidsForUid(int uid) { return PermissionManagerService.this.getGidsForUid(uid); } @@ -5231,12 +5337,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - public void addListenerLocked(IOnPermissionsChangeListener listener) { + public void addListener(IOnPermissionsChangeListener listener) { mPermissionListeners.register(listener); - } - public void removeListenerLocked(IOnPermissionsChangeListener listener) { + public void removeListener(IOnPermissionsChangeListener listener) { mPermissionListeners.unregister(listener); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 20e9c5dcb521..df81bac99a21 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -280,21 +280,21 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty); /** - * Read permission state from package settings. + * Read legacy permission state from package settings. * * TODO(zhanghai): This is a temporary method because we should not expose * {@code PackageSetting} which is a implementation detail that permission should not know. * Instead, it should retrieve the legacy state via a defined API. */ - public abstract void readStateFromPackageSettingsTEMP(); + public abstract void readLegacyPermissionStateTEMP(); /** - * Write permission state to package settings. + * Write legacy permission state to package settings. * * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence * for permission. */ - public abstract void writeStateToPackageSettingsTEMP(); + public abstract void writeLegacyPermissionStateTEMP(); /** * Notify that a user has been removed and its permission state should be removed as well. @@ -367,16 +367,14 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission, boolean checkShell, boolean requirePermissionWhenSameUser, @NonNull String message); - public abstract void enforceGrantRevokeRuntimePermissionPermissions(@NonNull String message); - - public abstract @NonNull PermissionSettings getPermissionSettings(); /** Grants default browser permissions to the given package */ public abstract void grantDefaultPermissionsToDefaultBrowser( @NonNull String packageName, @UserIdInt int userId); /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */ - public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName); + @Nullable + public abstract Permission getPermissionTEMP(@NonNull String permName); /** Get all permissions that have a certain protection */ public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionsWithProtection( @@ -536,5 +534,39 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * Removes invalid permissions which are not {@link PermissionInfo#FLAG_HARD_RESTRICTED} or * {@link PermissionInfo#FLAG_SOFT_RESTRICTED} from the input. */ - public abstract void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissions); + public abstract void retainHardAndSoftRestrictedPermissions( + @NonNull List<String> permissionNames); + + /** + * Read legacy permissions from legacy permission settings. + * + * TODO(zhanghai): This is a temporary method because we should not expose + * {@code LegacyPermissionSettings} which is a implementation detail that permission should not + * know. Instead, it should retrieve the legacy permissions via a defined API. + */ + public abstract void readLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings); + + /** + * Write legacy permissions to legacy permission settings. + * + * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence + * for permission. + */ + public abstract void writeLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings); + + /** + * Transfers ownership of permissions from one package to another. + */ + public abstract void transferPermissions(@NonNull String oldPackageName, + @NonNull String newPackageName); + + /** + * Check whether a permission can be propagated to instant app. + * + * @param permissionName the name of the permission + * @return whether the permission can be propagated + */ + public abstract boolean canPropagatePermissionToInstantApp(@NonNull String permissionName); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionRegistry.java b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java new file mode 100644 index 000000000000..0e3fda7b937a --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/PermissionRegistry.java @@ -0,0 +1,156 @@ +/* + * 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. + */ + +package com.android.server.pm.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.parsing.component.ParsedPermissionGroup; +import android.util.ArrayMap; +import android.util.ArraySet; + +import java.util.Collection; + +/** + * Permission registry for permissions, permission trees, permission groups and related things. + */ +public class PermissionRegistry { + /** + * All of the permissions known to the system. The mapping is from permission + * name to permission object. + */ + private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>(); + + /** + * All permission trees known to the system. The mapping is from permission tree + * name to permission object. + */ + private final ArrayMap<String, Permission> mPermissionTrees = new ArrayMap<>(); + + /** + * All permisson groups know to the system. The mapping is from permission group + * name to permission group object. + */ + private final ArrayMap<String, ParsedPermissionGroup> mPermissionGroups = new ArrayMap<>(); + + /** + * Set of packages that request a particular app op. The mapping is from permission + * name to package names. + */ + private final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>(); + + @NonNull + public Collection<Permission> getPermissions() { + return mPermissions.values(); + } + + @Nullable + public Permission getPermission(@NonNull String permissionName) { + return mPermissions.get(permissionName); + } + + public void addPermission(@NonNull Permission permission) { + mPermissions.put(permission.getName(), permission); + } + + public void removePermission(@NonNull String permissionName) { + mPermissions.remove(permissionName); + } + + @NonNull + public Collection<Permission> getPermissionTrees() { + return mPermissionTrees.values(); + } + + @Nullable + public Permission getPermissionTree(@NonNull String permissionTreeName) { + return mPermissionTrees.get(permissionTreeName); + } + + public void addPermissionTree(@NonNull Permission permissionTree) { + mPermissionTrees.put(permissionTree.getName(), permissionTree); + } + + /** + * Transfers ownership of permissions from one package to another. + */ + public void transferPermissions(@NonNull String oldPackageName, + @NonNull String newPackageName) { + for (int i = 0; i < 2; i++) { + ArrayMap<String, Permission> permissions = i == 0 ? mPermissionTrees : mPermissions; + for (final Permission permission : permissions.values()) { + permission.transfer(oldPackageName, newPackageName); + } + } + } + + @NonNull + public Collection<ParsedPermissionGroup> getPermissionGroups() { + return mPermissionGroups.values(); + } + + @Nullable + public ParsedPermissionGroup getPermissionGroup(@NonNull String permissionGroupName) { + return mPermissionGroups.get(permissionGroupName); + } + + public void addPermissionGroup(@NonNull ParsedPermissionGroup permissionGroup) { + mPermissionGroups.put(permissionGroup.getName(), permissionGroup); + } + + @NonNull + public ArrayMap<String, ArraySet<String>> getAllAppOpPermissionPackages() { + return mAppOpPermissionPackages; + } + + @Nullable + public ArraySet<String> getAppOpPermissionPackages(@NonNull String permissionName) { + return mAppOpPermissionPackages.get(permissionName); + } + + public void addAppOpPermissionPackage(@NonNull String permissionName, + @NonNull String packageName) { + ArraySet<String> packageNames = mAppOpPermissionPackages.get(permissionName); + if (packageNames == null) { + packageNames = new ArraySet<>(); + mAppOpPermissionPackages.put(permissionName, packageNames); + } + packageNames.add(packageName); + } + + public void removeAppOpPermissionPackage(@NonNull String permissionName, + @NonNull String packageName) { + final ArraySet<String> packageNames = mAppOpPermissionPackages.get(permissionName); + if (packageNames == null) { + return; + } + final boolean removed = packageNames.remove(packageName); + if (removed && packageNames.isEmpty()) { + mAppOpPermissionPackages.remove(permissionName); + } + } + + /** + * Returns the permission tree for the given permission. + * @throws SecurityException If the calling UID is not allowed to add permissions to the + * found permission tree. + */ + @NonNull + public Permission enforcePermissionTree(@NonNull String permissionName, int callingUid) { + return Permission.enforcePermissionTree(mPermissionTrees.values(), permissionName, + callingUid); + } +} diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java deleted file mode 100644 index eea8ac737b86..000000000000 --- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * 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. - */ - -package com.android.server.pm.permission; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.pm.parsing.component.ParsedPermissionGroup; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.XmlUtils; -import com.android.server.pm.DumpState; -import com.android.server.pm.PackageManagerService; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Collection; - -/** - * Permissions and other related data. This class is not meant for - * direct access outside of the permission package with the sole exception - * of package settings. Instead, it should be reference either from the - * permission manager or package settings. - */ -public class PermissionSettings { - - /** - * All of the permissions known to the system. The mapping is from permission - * name to permission object. - */ - @GuardedBy("mLock") - final ArrayMap<String, BasePermission> mPermissions = - new ArrayMap<String, BasePermission>(); - - /** - * All permission trees known to the system. The mapping is from permission tree - * name to permission object. - */ - @GuardedBy("mLock") - final ArrayMap<String, BasePermission> mPermissionTrees = - new ArrayMap<String, BasePermission>(); - - /** - * All permisson groups know to the system. The mapping is from permission group - * name to permission group object. - */ - @GuardedBy("mLock") - final ArrayMap<String, ParsedPermissionGroup> mPermissionGroups = - new ArrayMap<>(); - - /** - * Set of packages that request a particular app op. The mapping is from permission - * name to package names. - */ - @GuardedBy("mLock") - final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>(); - - private final Object mLock; - - PermissionSettings(@NonNull Object lock) { - mLock = lock; - } - - public @Nullable BasePermission getPermission(@NonNull String permName) { - synchronized (mLock) { - return getPermissionLocked(permName); - } - } - - public void addAppOpPackage(String permName, String packageName) { - synchronized (mLock) { - ArraySet<String> pkgs = mAppOpPermissionPackages.get(permName); - if (pkgs == null) { - pkgs = new ArraySet<>(); - mAppOpPermissionPackages.put(permName, pkgs); - } - pkgs.add(packageName); - } - } - - /** - * Transfers ownership of permissions from one package to another. - */ - public void transferPermissions(String origPackageName, String newPackageName) { - synchronized (mLock) { - for (int i=0; i<2; i++) { - ArrayMap<String, BasePermission> permissions = - i == 0 ? mPermissionTrees : mPermissions; - for (BasePermission bp : permissions.values()) { - bp.transfer(origPackageName, newPackageName); - } - } - } - } - - public boolean canPropagatePermissionToInstantApp(String permName) { - synchronized (mLock) { - final BasePermission bp = mPermissions.get(permName); - return (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant()); - } - } - - public void readPermissions(XmlPullParser parser) throws IOException, XmlPullParserException { - synchronized (mLock) { - readPermissions(mPermissions, parser); - } - } - - public void readPermissionTrees(XmlPullParser parser) - throws IOException, XmlPullParserException { - synchronized (mLock) { - readPermissions(mPermissionTrees, parser); - } - } - - public void writePermissions(XmlSerializer serializer) throws IOException { - synchronized (mLock) { - for (BasePermission bp : mPermissions.values()) { - bp.writeLPr(serializer); - } - } - } - - public void writePermissionTrees(XmlSerializer serializer) throws IOException { - synchronized (mLock) { - for (BasePermission bp : mPermissionTrees.values()) { - bp.writeLPr(serializer); - } - } - } - - public static void readPermissions(ArrayMap<String, BasePermission> out, XmlPullParser parser) - throws IOException, XmlPullParserException { - int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - if (!BasePermission.readLPw(out, parser)) { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Unknown element reading permissions: " + parser.getName() + " at " - + parser.getPositionDescription()); - } - XmlUtils.skipCurrentTag(parser); - } - } - - public void dumpPermissions(PrintWriter pw, String packageName, - ArraySet<String> permissionNames, boolean externalStorageEnforced, - DumpState dumpState) { - synchronized (mLock) { - boolean printedSomething = false; - for (BasePermission bp : mPermissions.values()) { - printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames, - externalStorageEnforced, printedSomething, dumpState); - } - if (packageName == null && permissionNames == null) { - for (int iperm = 0; iperm<mAppOpPermissionPackages.size(); iperm++) { - if (iperm == 0) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("AppOp Permissions:"); - } - pw.print(" AppOp Permission "); - pw.print(mAppOpPermissionPackages.keyAt(iperm)); - pw.println(":"); - ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm); - for (int ipkg=0; ipkg<pkgs.size(); ipkg++) { - pw.print(" "); pw.println(pkgs.valueAt(ipkg)); - } - } - } - } - } - - @GuardedBy("mLock") - @Nullable BasePermission getPermissionLocked(@NonNull String permName) { - return mPermissions.get(permName); - } - - @GuardedBy("mLock") - @Nullable BasePermission getPermissionTreeLocked(@NonNull String permName) { - return mPermissionTrees.get(permName); - } - - @GuardedBy("mLock") - void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) { - mPermissions.put(permName, permission); - } - - @GuardedBy("mLock") - void putPermissionTreeLocked(@NonNull String permName, @NonNull BasePermission permission) { - mPermissionTrees.put(permName, permission); - } - - @GuardedBy("mLock") - void removePermissionLocked(@NonNull String permName) { - mPermissions.remove(permName); - } - - @GuardedBy("mLock") - void removePermissionTreeLocked(@NonNull String permName) { - mPermissionTrees.remove(permName); - } - - @GuardedBy("mLock") - @NonNull Collection<BasePermission> getAllPermissionsLocked() { - return mPermissions.values(); - } - - @GuardedBy("mLock") - @NonNull Collection<BasePermission> getAllPermissionTreesLocked() { - return mPermissionTrees.values(); - } - - /** - * Returns the permission tree for the given permission. - * @throws SecurityException If the calling UID is not allowed to add permissions to the - * found permission tree. - */ - @Nullable BasePermission enforcePermissionTree(@NonNull String permName, int callingUid) { - synchronized (mLock) { - return BasePermission.enforcePermissionTree( - mPermissionTrees.values(), permName, callingUid); - } - } - - /** - * Check whether a permission is runtime. - * - * @see BasePermission#isRuntime() - */ - public boolean isPermissionRuntime(@NonNull String permName) { - synchronized (mLock) { - final BasePermission bp = mPermissions.get(permName); - return (bp != null && bp.isRuntime()); - } - } - - public boolean isPermissionInstant(String permName) { - synchronized (mLock) { - final BasePermission bp = mPermissions.get(permName); - return (bp != null && bp.isInstant()); - } - } - - boolean isPermissionAppOp(String permName) { - synchronized (mLock) { - final BasePermission bp = mPermissions.get(permName); - return (bp != null && bp.isAppOp()); - } - } - -} diff --git a/services/core/java/com/android/server/pm/permission/PermissionState.java b/services/core/java/com/android/server/pm/permission/PermissionState.java index 59b204f7dfff..12f29d091134 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionState.java +++ b/services/core/java/com/android/server/pm/permission/PermissionState.java @@ -27,7 +27,7 @@ import com.android.internal.annotations.GuardedBy; public final class PermissionState { @NonNull - private final BasePermission mPermission; + private final Permission mPermission; private final Object mLock = new Object(); @@ -40,7 +40,7 @@ public final class PermissionState { @GuardedBy("mLock") private int mFlags; - public PermissionState(@NonNull BasePermission permission) { + public PermissionState(@NonNull Permission permission) { mPermission = permission; } @@ -52,7 +52,7 @@ public final class PermissionState { } @NonNull - public BasePermission getPermission() { + public Permission getPermission() { return mPermission; } diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java index 9727a5452440..04d82e872ae6 100644 --- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java +++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java @@ -125,7 +125,7 @@ public final class UidPermissionState { } @NonNull - private PermissionState getOrCreatePermissionState(@NonNull BasePermission permission) { + private PermissionState getOrCreatePermissionState(@NonNull Permission permission) { if (mPermissions == null) { mPermissions = new ArrayMap<>(); } @@ -158,7 +158,7 @@ public final class UidPermissionState { * @param granted whether the permission is granted * @param flags the permission flags */ - public void putPermissionState(@NonNull BasePermission permission, boolean granted, int flags) { + public void putPermissionState(@NonNull Permission permission, boolean granted, int flags) { final String name = permission.getName(); if (mPermissions == null) { mPermissions = new ArrayMap<>(); @@ -230,7 +230,7 @@ public final class UidPermissionState { * @param permission the permission to grant * @return whether the permission grant state changed */ - public boolean grantPermission(@NonNull BasePermission permission) { + public boolean grantPermission(@NonNull Permission permission) { final PermissionState permissionState = getOrCreatePermissionState(permission); return permissionState.grant(); } @@ -241,7 +241,7 @@ public final class UidPermissionState { * @param permission the permission to revoke * @return whether the permission grant state changed */ - public boolean revokePermission(@NonNull BasePermission permission) { + public boolean revokePermission(@NonNull Permission permission) { final String name = permission.getName(); final PermissionState permissionState = getPermissionState(name); if (permissionState == null) { @@ -276,7 +276,7 @@ public final class UidPermissionState { * @param flagValues the new values for the masked flags * @return whether the permission flags changed */ - public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask, + public boolean updatePermissionFlags(@NonNull Permission permission, int flagMask, int flagValues) { if (flagMask == 0) { return false; 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 7433a3a53ad8..d3b1ac6e6096 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -247,6 +247,13 @@ public class StatsPullAtomService extends SystemService { // 20% as a conservative estimate. private static final int MAX_PROCSTATS_RAW_SHARD_SIZE = (int) (MAX_PROCSTATS_SHARD_SIZE * 1.20); + /** + * Threshold to filter out small CPU times at frequency per UID. Those small values appear + * because of more precise accounting in a BPF program. Discarding them reduces the data by at + * least 20% with negligible error. + */ + private static final int MIN_CPU_TIME_PER_UID_FREQ = 10; + private final Object mThermalLock = new Object(); @GuardedBy("mThermalLock") private IThermalService mThermalService; @@ -1509,7 +1516,7 @@ public class StatsPullAtomService extends SystemService { int pullCpuTimePerUidFreqLocked(int atomTag, List<StatsEvent> pulledData) { mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> { for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { - if (cpuFreqTimeMs[freqIndex] != 0) { + if (cpuFreqTimeMs[freqIndex] >= MIN_CPU_TIME_PER_UID_FREQ) { pulledData.add(FrameworkStatsLog.buildStatsEvent( atomTag, uid, freqIndex, cpuFreqTimeMs[freqIndex])); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java index 941be0ede1b9..1357608ef9d4 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java @@ -168,7 +168,7 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat // that support new types of auto detection on the same hardware. if (isAutoDetectionSupported()) { final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled(); - setAutoDetectionEnabled(autoDetectionEnabled); + setAutoDetectionEnabledIfRequired(autoDetectionEnabled); // Avoid writing the geo detection enabled setting for devices that do not support geo // time zone detection: if we wrote it down then we'd set the value explicitly, which @@ -176,7 +176,7 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat // releases that support geo detection on the same hardware. if (isGeoDetectionSupported()) { final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled(); - setGeoDetectionEnabled(userId, geoTzDetectionEnabled); + setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled); } } } @@ -198,8 +198,14 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0; } - private void setAutoDetectionEnabled(boolean enabled) { - Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, enabled ? 1 : 0); + private void setAutoDetectionEnabledIfRequired(boolean enabled) { + // This check is racey, but the whole settings update process is racey. This check prevents + // a ConfigurationChangeListener callback triggering due to ContentObserver's still + // triggering *sometimes* for no-op updates. Because callbacks are async this is necessary + // for stable behavior during tests. + if (isAutoDetectionEnabled() != enabled) { + Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, enabled ? 1 : 0); + } } private boolean isLocationEnabled(@UserIdInt int userId) { @@ -213,9 +219,12 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat (geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0; } - private void setGeoDetectionEnabled(@UserIdInt int userId, boolean enabled) { - Settings.Secure.putIntForUser(mCr, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, - enabled ? 1 : 0, userId); + private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) { + // See comment in setAutoDetectionEnabledIfRequired. http://b/171953500 + if (isGeoDetectionEnabled(userId) != enabled) { + Settings.Secure.putIntForUser(mCr, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, + enabled ? 1 : 0, userId); + } } private boolean deviceHasTelephonyNetwork() { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 25b2523b1a3e..910a1a2c69b2 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1195,12 +1195,7 @@ class ActivityStarter { } } - // Only allow app switching to be resumed if activity is not a restricted background - // activity, otherwise any background activity started in background task can stop - // home button protection mode. - if (!restrictedBgActivity) { - mService.onStartActivitySetDidAppSwitch(); - } + mService.onStartActivitySetDidAppSwitch(); mController.doPendingActivityLaunches(false); mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession, @@ -1265,20 +1260,6 @@ class ActivityStarter { return false; } - // Always allow home application to start activities. - if (mService.mHomeProcess != null && callingUid == mService.mHomeProcess.mUid) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "Activity start allowed for home app callingUid (" + callingUid + ")"); - } - return false; - } - - // App switching will be allowed if BAL app switching flag is not enabled, or if - // its app switching rule allows it. - // This is used to block background activity launch even if the app is still - // visible to user after user clicking home button. - final boolean appSwitchAllowed = mService.getBalAppSwitchesAllowed(); - // don't abort if the callingUid has a visible window or is a persistent system process final int callingUidProcState = mService.getUidState(callingUid); final boolean callingUidHasAnyVisibleWindow = @@ -1288,8 +1269,7 @@ class ActivityStarter { || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP; final boolean isCallingUidPersistentSystemProcess = callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; - if ((appSwitchAllowed && callingUidHasAnyVisibleWindow) - || isCallingUidPersistentSystemProcess) { + if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) { if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid + ", isCallingUidPersistentSystemProcess = " @@ -1315,7 +1295,6 @@ class ActivityStarter { || realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; if (realCallingUid != callingUid) { // don't abort if the realCallingUid has a visible window - // TODO(b/171459802): We should check appSwitchAllowed also if (realCallingUidHasAnyVisibleWindow) { if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid @@ -1397,7 +1376,7 @@ class ActivityStarter { // don't abort if the callerApp or other processes of that uid are allowed in any way if (callerApp != null) { // first check the original calling process - if (callerApp.areBackgroundActivityStartsAllowed(appSwitchAllowed)) { + if (callerApp.areBackgroundActivityStartsAllowed()) { if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "Background activity start allowed: callerApp process (pid = " + callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed"); @@ -1410,8 +1389,7 @@ class ActivityStarter { if (uidProcesses != null) { for (int i = uidProcesses.size() - 1; i >= 0; i--) { final WindowProcessController proc = uidProcesses.valueAt(i); - if (proc != callerApp - && proc.areBackgroundActivityStartsAllowed(appSwitchAllowed)) { + if (proc != callerApp && proc.areBackgroundActivityStartsAllowed()) { if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "Background activity start allowed: process " + proc.getPid() diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index c7d716d8290a..eb86d37225bc 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -565,4 +565,10 @@ public abstract class ActivityTaskManagerInternal { /** Set all associated companion app that belongs to an userId. */ public abstract void setCompanionAppPackages(int userId, Set<String> companionAppPackages); + + /** + * @param packageName The package to check + * @return Whether the package is the base of any locked task + */ + public abstract boolean isBaseOfLockedTask(String packageName); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a02701f8ad00..608d37395b28 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -161,11 +161,9 @@ import android.app.WindowConfiguration; import android.app.admin.DevicePolicyCache; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; -import android.app.compat.CompatChanges; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.EnterPipRequestedItem; import android.app.usage.UsageStatsManagerInternal; -import android.compat.annotation.ChangeId; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.ContentResolver; @@ -344,12 +342,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** This activity is being relaunched due to a free-resize operation. */ public static final int RELAUNCH_REASON_FREE_RESIZE = 2; - /** - * Apps are blocked from starting activities in the foreground after the user presses home. - */ - @ChangeId - public static final long BLOCK_ACTIVITY_STARTS_AFTER_HOME = 159433730L; - Context mContext; /** @@ -2635,35 +2627,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new SecurityException(msg); } - /** - * Return true if app switch protection will be handled by background activity launch logic. - */ - boolean getBalAppSwitchesProtectionEnabled() { - return CompatChanges.isChangeEnabled(BLOCK_ACTIVITY_STARTS_AFTER_HOME); - } - - /** - * Return true if app switching is allowed. - */ - boolean getBalAppSwitchesAllowed() { - if (getBalAppSwitchesProtectionEnabled()) { - // Apps no longer able to start BAL again until app switching is resumed. - return mAppSwitchesAllowedTime == 0; - } else { - // Legacy behavior, BAL logic won't block app switching. - return true; - } - } - boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid, int callingPid, int callingUid, String name) { - - // Background activity launch logic replaces app switching protection, so allow - // apps to start activity here now. - if (getBalAppSwitchesProtectionEnabled()) { - return true; - } - if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) { return true; } @@ -4698,10 +4663,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mAppSwitchesAllowedTime = SystemClock.uptimeMillis() + APP_SWITCH_DELAY_TIME; mLastStopAppSwitchesTime = SystemClock.uptimeMillis(); mDidAppSwitch = false; - // If BAL app switching enabled, app switches are blocked not delayed. - if (!getBalAppSwitchesProtectionEnabled()) { - getActivityStartController().schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME); - } + getActivityStartController().schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME); } } @@ -7470,5 +7432,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mCompanionAppUidsMap.put(userId, result); } } + + + @Override + public boolean isBaseOfLockedTask(String packageName) { + synchronized (mGlobalLock) { + return getLockTaskController().isBaseOfLockedTask(packageName); + } + } } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 26c5d70a2c45..6453ddf80faa 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1084,23 +1084,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return token; } - SurfaceControl addShellRoot(@NonNull IWindow client, int windowType) { - ShellRoot root = mShellRoots.get(windowType); + SurfaceControl addShellRoot(@NonNull IWindow client, + @WindowManager.ShellRootLayer int shellRootLayer) { + ShellRoot root = mShellRoots.get(shellRootLayer); if (root != null) { if (root.getClient() == client) { return root.getSurfaceControl(); } root.clear(); - mShellRoots.remove(windowType); + mShellRoots.remove(shellRootLayer); } - root = new ShellRoot(client, this, windowType); + root = new ShellRoot(client, this, shellRootLayer); SurfaceControl rootLeash = root.getSurfaceControl(); if (rootLeash == null) { // Root didn't finish initializing, so don't add it. root.clear(); return null; } - mShellRoots.put(windowType, root); + mShellRoots.put(shellRootLayer, root); SurfaceControl out = new SurfaceControl(rootLeash, "DisplayContent.addShellRoot"); return out; } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 470c2b1581d0..4a54196b71de 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -45,6 +45,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS; import android.graphics.Rect; import android.os.Handler; @@ -54,6 +55,7 @@ import android.os.Process; import android.os.Trace; import android.os.UserHandle; import android.util.ArrayMap; +import android.util.EventLog; import android.util.Slog; import android.view.InputApplicationHandle; import android.view.InputChannel; @@ -503,13 +505,14 @@ final class InputMonitor { } if (!focus.mWinAnimator.hasSurface()) { - ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, - "Focus not requested for window=%s because it has no surface", - focus); + Slog.v(TAG_WM, "Focus not requested for window=%" + focus + + " because it has no surface."); return; } mInputTransaction.setFocusedWindow(focus.mInputWindowHandle.token, mDisplayId); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, + "Focus request " + focus, "reason=UpdateInputWindows"); mDisplayContent.mLastRequestedFocus = focus; ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus); } diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index e90436ef3b91..b33c2f2823a5 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -871,6 +871,21 @@ public class LockTaskController { } /** + * @param packageName The package to check + * @return Whether the package is the base of any locked task + */ + boolean isBaseOfLockedTask(String packageName) { + for (int i = 0; i < mLockTaskModeTasks.size(); i++) { + final Intent bi = mLockTaskModeTasks.get(i).getBaseIntent(); + if (bi != null && packageName.equals(bi.getComponent() + .getPackageName())) { + return true; + } + } + return false; + } + + /** * Gets the cached value of LockTask feature flags for a specific user. */ private int getLockTaskFeaturesForUser(int userId) { diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java index 759f341c0fe0..62c0527dfe1b 100644 --- a/services/core/java/com/android/server/wm/ShellRoot.java +++ b/services/core/java/com/android/server/wm/ShellRoot.java @@ -16,7 +16,10 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.SHELL_ROOT_LAYER_DIVIDER; +import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION; @@ -31,6 +34,7 @@ import android.view.DisplayInfo; import android.view.IWindow; import android.view.SurfaceControl; import android.view.WindowInfo; +import android.view.WindowManager; import android.view.animation.Animation; /** @@ -39,6 +43,7 @@ import android.view.animation.Animation; public class ShellRoot { private static final String TAG = "ShellRoot"; private final DisplayContent mDisplayContent; + private final int mShellRootLayer; private IWindow mClient; private WindowToken mToken; private final IBinder.DeathRecipient mDeathRecipient; @@ -46,17 +51,31 @@ public class ShellRoot { private IWindow mAccessibilityWindow; private IBinder.DeathRecipient mAccessibilityWindowDeath; - ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc, final int windowType) { + ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc, + @WindowManager.ShellRootLayer final int shellRootLayer) { mDisplayContent = dc; - mDeathRecipient = () -> mDisplayContent.removeShellRoot(windowType); + mShellRootLayer = shellRootLayer; + mDeathRecipient = () -> mDisplayContent.removeShellRoot(shellRootLayer); try { client.asBinder().linkToDeath(mDeathRecipient, 0); } catch (RemoteException e) { - Slog.e(TAG, "Unable to add shell root for layer " + windowType + " on display " + Slog.e(TAG, "Unable to add shell root layer " + shellRootLayer + " on display " + dc.getDisplayId(), e); return; } mClient = client; + int windowType; + switch (shellRootLayer) { + case SHELL_ROOT_LAYER_DIVIDER: + windowType = TYPE_DOCK_DIVIDER; + break; + case SHELL_ROOT_LAYER_PIP: + windowType = TYPE_APPLICATION_OVERLAY; + break; + default: + throw new IllegalArgumentException(shellRootLayer + + " is not an acceptable shell root layer."); + } mToken = new WindowToken( dc.mWmService, client.asBinder(), windowType, true, dc, true, false); mSurfaceControl = mToken.makeChildSurface(null) @@ -111,10 +130,16 @@ public class ShellRoot { } WindowInfo getWindowInfo() { - if (mToken.windowType != TYPE_DOCK_DIVIDER) { + if (mShellRootLayer != SHELL_ROOT_LAYER_DIVIDER + && mShellRootLayer != SHELL_ROOT_LAYER_PIP) { + return null; + } + if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER + && !mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()) { return null; } - if (!mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()) { + if (mShellRootLayer == SHELL_ROOT_LAYER_PIP + && mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask() == null) { return null; } if (mAccessibilityWindow == null) { @@ -125,13 +150,25 @@ public class ShellRoot { windowInfo.type = mToken.windowType; windowInfo.layer = mToken.getWindowLayerFromType(); windowInfo.token = mAccessibilityWindow.asBinder(); - windowInfo.title = "Splitscreen Divider"; windowInfo.focused = false; - windowInfo.inPictureInPicture = false; windowInfo.hasFlagWatchOutsideTouch = false; final Rect regionRect = new Rect(); - mDisplayContent.getDockedDividerController().getTouchRegion(regionRect); - windowInfo.regionInScreen.set(regionRect); + + + // DividerView + if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER) { + windowInfo.inPictureInPicture = false; + mDisplayContent.getDockedDividerController().getTouchRegion(regionRect); + windowInfo.regionInScreen.set(regionRect); + windowInfo.title = "Splitscreen Divider"; + } + // PipMenuView + if (mShellRootLayer == SHELL_ROOT_LAYER_PIP) { + windowInfo.inPictureInPicture = true; + mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask().getBounds(regionRect); + windowInfo.regionInScreen.set(regionRect); + windowInfo.title = "Picture-in-Picture menu"; + } return windowInfo; } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 5bc5b47d5cfc..9273bf71f673 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -125,7 +125,6 @@ import static com.android.server.wm.Task.ActivityState.PAUSED; import static com.android.server.wm.Task.ActivityState.PAUSING; import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.Task.ActivityState.STARTED; -import static com.android.server.wm.Task.ActivityState.STOPPED; import static com.android.server.wm.Task.ActivityState.STOPPING; import static com.android.server.wm.TaskProto.ACTIVITY_TYPE; import static com.android.server.wm.TaskProto.BOUNDS; @@ -2847,13 +2846,18 @@ class Task extends WindowContainer<WindowContainer> { getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode); } - // Do not allow non-resizable non-pinned tasks to be in a multi-window mode - they should - // use their parent's windowing mode, or fullscreen. - if (!isResizeable() && windowingMode != WINDOWING_MODE_PINNED - && WindowConfiguration.inMultiWindowMode(windowingMode)) { - windowingMode = WindowConfiguration.inMultiWindowMode(parentWindowingMode) - ? WINDOWING_MODE_FULLSCREEN : parentWindowingMode; - getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode); + // Do not allow non-resizable tasks to be in a multi-window mode, unless it is in pinned + // windowing mode or is in size compat freeform mode + if (!isResizeable()) { + final int candidateWindowingMode = + windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode; + if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode) + && candidateWindowingMode != WINDOWING_MODE_PINNED + && (candidateWindowingMode != WINDOWING_MODE_FREEFORM + || !mStackSupervisor.mService.mSizeCompatFreeform)) { + getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode( + WINDOWING_MODE_FULLSCREEN); + } } if (isLeafTask()) { @@ -4122,6 +4126,10 @@ class Task extends WindowContainer<WindowContainer> { forAllActivities(r -> { info.addLaunchCookie(r.mLaunchCookie); }); + final Task rootTask = getRootTask(); + info.parentTaskId = rootTask == getParent() && rootTask.mCreatedByOrganizer + ? rootTask.mTaskId + : INVALID_TASK_ID; } @Nullable PictureInPictureParams getPictureInPictureParams() { @@ -4838,6 +4846,17 @@ class Task extends WindowContainer<WindowContainer> { return mTaskOrganizer != null; } + private boolean canBeOrganized() { + // All root tasks can be organized + if (isRootTask()) { + return true; + } + + // Task could be organized if it's the direct child of the root created by organizer. + final Task rootTask = getRootTask(); + return rootTask == getParent() && rootTask.mCreatedByOrganizer; + } + @Override boolean showSurfaceOnCreation() { // Organized tasks handle their own surface visibility @@ -4986,7 +5005,7 @@ class Task extends WindowContainer<WindowContainer> { // is created. return false; } - if (!isRootTask()) { + if (!canBeOrganized()) { return setTaskOrganizer(null); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 70f43635a37c..c806c94358cb 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -206,6 +206,7 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.DisplayMetrics; +import android.util.EventLog; import android.util.MergedConfiguration; import android.util.Slog; import android.util.SparseArray; @@ -407,6 +408,7 @@ public class WindowManagerService extends IWindowManager.Stub */ private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY = "persist.wm.disable_custom_task_animation"; + static final int LOGTAG_INPUT_FOCUS = 62001; /** * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY @@ -4032,7 +4034,8 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public SurfaceControl addShellRoot(int displayId, IWindow client, int windowType) { + public SurfaceControl addShellRoot(int displayId, IWindow client, + @WindowManager.ShellRootLayer int shellRootLayer) { if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); @@ -4044,7 +4047,7 @@ public class WindowManagerService extends IWindowManager.Stub if (dc == null) { return null; } - return dc.addShellRoot(client, windowType); + return dc.addShellRoot(client, shellRootLayer); } } finally { Binder.restoreCallingIdentity(origId); @@ -4052,7 +4055,8 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setShellRootAccessibilityWindow(int displayId, int windowType, IWindow target) { + public void setShellRootAccessibilityWindow(int displayId, + @WindowManager.ShellRootLayer int shellRootLayer, IWindow target) { if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS); @@ -4064,7 +4068,7 @@ public class WindowManagerService extends IWindowManager.Stub if (dc == null) { return; } - ShellRoot root = dc.mShellRoots.get(windowType); + ShellRoot root = dc.mShellRoots.get(shellRootLayer); if (root == null) { return; } @@ -8346,6 +8350,9 @@ public class WindowManagerService extends IWindowManager.Stub final int displayId = embeddedWindow.mDisplayId; if (grantFocus) { t.setFocusedWindow(targetInputToken, displayId).apply(); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, + "Focus request " + embeddedWindow.getName(), + "reason=grantEmbeddedWindowFocus(true)"); } else { // Search for a new focus target DisplayContent displayContent = mRoot.getDisplayContent(displayId); @@ -8359,6 +8366,9 @@ public class WindowManagerService extends IWindowManager.Stub } t.requestFocusTransfer(newFocusTarget.mInputWindowHandle.token, targetInputToken, displayId).apply(); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, + "Transfer focus request " + newFocusTarget, + "reason=grantEmbeddedWindowFocus(false)"); } ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s", embeddedWindow.getName(), grantFocus); @@ -8392,9 +8402,15 @@ public class WindowManagerService extends IWindowManager.Stub if (grantFocus) { t.requestFocusTransfer(targetInputToken, hostWindow.mInputChannel.getToken(), hostWindow.getDisplayId()).apply(); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, + "Transfer focus request " + embeddedWindow.getName(), + "reason=grantEmbeddedWindowFocus(true)"); } else { t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), targetInputToken, hostWindow.getDisplayId()).apply(); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, + "Transfer focus request " + hostWindow, + "reason=grantEmbeddedWindowFocus(false)"); } ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s", embeddedWindow.getName(), grantFocus); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 51857dcd323b..7d54ea95579e 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -104,13 +104,55 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub @Override public void applyTransaction(WindowContainerTransaction t) { - applyTransaction(t, null /*callback*/, null /*transition*/); + enforceTaskPermission("applyTransaction()"); + if (t == null) { + throw new IllegalArgumentException("Null transaction passed to applySyncTransaction"); + } + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + applyTransaction(t, -1 /*syncId*/, null /*transition*/); + } + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override public int applySyncTransaction(WindowContainerTransaction t, IWindowContainerTransactionCallback callback) { - return applyTransaction(t, callback, null /*transition*/); + enforceTaskPermission("applySyncTransaction()"); + if (t == null) { + throw new IllegalArgumentException("Null transaction passed to applySyncTransaction"); + } + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + /** + * If callback is non-null we are looking to synchronize this transaction by + * collecting all the results in to a SurfaceFlinger transaction and then delivering + * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the + * details of the operation. But at a high level we create a sync operation with a + * given ID and an associated callback. Then we notify each WindowContainer in this + * WindowContainer transaction that it is participating in a sync operation with + * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady" + * which means that we have added everything to the set. At any point after this, + * all the WindowContainers will eventually finish applying their changes and notify + * the BLASTSyncEngine which will deliver the Transaction to the callback. + */ + int syncId = -1; + if (callback != null) { + syncId = startSyncWithOrganizer(callback); + } + applyTransaction(t, syncId, null /*transition*/); + if (syncId >= 0) { + setSyncReady(syncId); + } + return syncId; + } + } finally { + Binder.restoreCallingIdentity(ident); + } } @Override @@ -131,7 +173,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (t == null) { t = new WindowContainerTransaction(); } - applyTransaction(t, null /*callback*/, transition); + applyTransaction(t, -1 /*syncId*/, transition); return transition; } } finally { @@ -148,10 +190,16 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub try { synchronized (mGlobalLock) { int syncId = -1; - if (t != null) { - syncId = applyTransaction(t, callback, null /*transition*/); + if (t != null && callback != null) { + syncId = startSyncWithOrganizer(callback); } getTransitionController().finishTransition(transitionToken); + if (t != null) { + applyTransaction(t, syncId, null /*transition*/); + } + if (syncId >= 0) { + setSyncReady(syncId); + } return syncId; } } finally { @@ -160,154 +208,114 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } /** - * @param callback If non-null, this will be a sync-transaction. + * @param syncId If non-null, this will be a sync-transaction. * @param transition A transition to collect changes into. - * @return a BLAST sync-id if this is a non-transition, sync transaction. */ - private int applyTransaction(@NonNull WindowContainerTransaction t, - @Nullable IWindowContainerTransactionCallback callback, + private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId, @Nullable Transition transition) { - enforceTaskPermission("applySyncTransaction()"); - int syncId = -1; - if (t == null) { - throw new IllegalArgumentException( - "Null transaction passed to applySyncTransaction"); - } - final long ident = Binder.clearCallingIdentity(); + int effects = 0; + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId); + mService.deferWindowLayout(); try { - synchronized (mGlobalLock) { - int effects = 0; + ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); + Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries = + t.getChanges().entrySet().iterator(); + while (entries.hasNext()) { + final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next(); + final WindowContainer wc = WindowContainer.fromBinder(entry.getKey()); + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to operate on detached container: " + wc); + continue; + } + // Make sure we add to the syncSet before performing + // operations so we don't end up splitting effects between the WM + // pending transaction and the BLASTSync transaction. + if (syncId >= 0) { + addToSyncSet(syncId, wc); + } - /** - * If callback is non-null we are looking to synchronize this transaction by - * collecting all the results in to a SurfaceFlinger transaction and then delivering - * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the - * details of the operation. But at a high level we create a sync operation with a - * given ID and an associated callback. Then we notify each WindowContainer in this - * WindowContainer transaction that it is participating in a sync operation with - * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady" - * which means that we have added everything to the set. At any point after this, - * all the WindowContainers will eventually finish applying their changes and notify - * the BLASTSyncEngine which will deliver the Transaction to the callback. - */ - if (callback != null) { - syncId = startSyncWithOrganizer(callback); + int containerEffect = applyWindowContainerChange(wc, entry.getValue()); + if (transition != null) transition.collect(wc); + effects |= containerEffect; + + // Lifecycle changes will trigger ensureConfig for everything. + if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 + && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { + haveConfigChanges.add(wc); + } + } + // Hierarchy changes + final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); + for (int i = 0, n = hops.size(); i < n; ++i) { + final WindowContainerTransaction.HierarchyOp hop = hops.get(i); + final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + if (!wc.isAttached()) { + Slog.e(TAG, "Attempt to operate on detached container: " + wc); + continue; + } + if (syncId >= 0) { + addToSyncSet(syncId, wc); } - ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", - syncId); - mService.deferWindowLayout(); - try { - ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); - Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries = - t.getChanges().entrySet().iterator(); - while (entries.hasNext()) { - final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = - entries.next(); - final WindowContainer wc = WindowContainer.fromBinder(entry.getKey()); - if (wc == null || !wc.isAttached()) { - Slog.e(TAG, "Attempt to operate on detached container: " + wc); - continue; - } - // Make sure we add to the syncSet before performing - // operations so we don't end up splitting effects between the WM - // pending transaction and the BLASTSync transaction. - if (syncId >= 0) { - addToSyncSet(syncId, wc); - } - - int containerEffect = applyWindowContainerChange(wc, entry.getValue()); - if (transition != null) transition.collect(wc); - effects |= containerEffect; - - // Lifecycle changes will trigger ensureConfig for everything. - if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 - && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { - haveConfigChanges.add(wc); - } - } - // Hierarchy changes - final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); - for (int i = 0, n = hops.size(); i < n; ++i) { - final WindowContainerTransaction.HierarchyOp hop = hops.get(i); - final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - if (!wc.isAttached()) { - Slog.e(TAG, "Attempt to operate on detached container: " + wc); - continue; - } - if (syncId >= 0) { - addToSyncSet(syncId, wc); - } effects |= sanitizeAndApplyHierarchyOp(wc, hop); - if (transition != null) { - transition.collect(wc); - if (hop.isReparent() && hop.getNewParent() != null) { - transition.collect(WindowContainer.fromBinder(hop.getNewParent())); - } - } - } - // Queue-up bounds-change transactions for tasks which are now organized. Do - // this after hierarchy ops so we have the final organized state. - entries = t.getChanges().entrySet().iterator(); - while (entries.hasNext()) { - final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = - entries.next(); - final Task task = WindowContainer.fromBinder(entry.getKey()).asTask(); - final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds(); - if (task == null || !task.isAttached() || surfaceBounds == null) { - continue; - } - if (!task.isOrganized()) { - final Task parent = - task.getParent() != null ? task.getParent().asTask() : null; - // Also allow direct children of created-by-organizer tasks to be - // controlled. In the future, these will become organized anyways. - if (parent == null || !parent.mCreatedByOrganizer) { - throw new IllegalArgumentException( - "Can't manipulate non-organized task surface " + task); - } - } - final SurfaceControl.Transaction sft = new SurfaceControl.Transaction(); - final SurfaceControl sc = task.getSurfaceControl(); - sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top); - if (surfaceBounds.isEmpty()) { - sft.setWindowCrop(sc, null); - } else { - sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height()); - } - task.setMainWindowSizeChangeTransaction(sft); + if (transition != null) { + transition.collect(wc); + if (hop.isReparent() && hop.getNewParent() != null) { + transition.collect(WindowContainer.fromBinder(hop.getNewParent())); } - if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) { - // Already calls ensureActivityConfig - mService.mRootWindowContainer.ensureActivitiesVisible( - null, 0, PRESERVE_WINDOWS); - } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { - final PooledConsumer f = PooledLambda.obtainConsumer( - ActivityRecord::ensureActivityConfiguration, - PooledLambda.__(ActivityRecord.class), 0, - true /* preserveWindow */); - try { - for (int i = haveConfigChanges.size() - 1; i >= 0; --i) { - haveConfigChanges.valueAt(i).forAllActivities(f); - } - } finally { - f.recycle(); - } + } + } + // Queue-up bounds-change transactions for tasks which are now organized. Do + // this after hierarchy ops so we have the final organized state. + entries = t.getChanges().entrySet().iterator(); + while (entries.hasNext()) { + final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next(); + final Task task = WindowContainer.fromBinder(entry.getKey()).asTask(); + final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds(); + if (task == null || !task.isAttached() || surfaceBounds == null) { + continue; + } + if (!task.isOrganized()) { + final Task parent = task.getParent() != null ? task.getParent().asTask() : null; + // Also allow direct children of created-by-organizer tasks to be + // controlled. In the future, these will become organized anyways. + if (parent == null || !parent.mCreatedByOrganizer) { + throw new IllegalArgumentException( + "Can't manipulate non-organized task surface " + task); } - - if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) == 0) { - mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED); + } + final SurfaceControl.Transaction sft = new SurfaceControl.Transaction(); + final SurfaceControl sc = task.getSurfaceControl(); + sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top); + if (surfaceBounds.isEmpty()) { + sft.setWindowCrop(sc, null); + } else { + sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height()); + } + task.setMainWindowSizeChangeTransaction(sft); + } + if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) { + // Already calls ensureActivityConfig + mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { + final PooledConsumer f = PooledLambda.obtainConsumer( + ActivityRecord::ensureActivityConfiguration, + PooledLambda.__(ActivityRecord.class), 0, + true /* preserveWindow */); + try { + for (int i = haveConfigChanges.size() - 1; i >= 0; --i) { + haveConfigChanges.valueAt(i).forAllActivities(f); } } finally { - mService.continueWindowLayout(); - if (syncId >= 0) { - setSyncReady(syncId); - } + f.recycle(); } } + + if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) == 0) { + mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED); + } } finally { - Binder.restoreCallingIdentity(ident); + mService.continueWindowLayout(); } - return syncId; } private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) { diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 2d69dcbcfbd0..2e7905c64049 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -505,33 +505,29 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } } - boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed) { - // 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) { - // allow if any activity in the caller has either started or finished very recently, and - // it must be started or finished after last stop app switches time. - final long now = SystemClock.uptimeMillis(); - if (now - mLastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS - || now - mLastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) { - // if activity is started and finished before stop app switch time, we should not - // let app to be able to start background activity even it's in grace period. - if (mLastActivityLaunchTime > mAtm.getLastStopAppSwitchesTime() - || mLastActivityFinishTime > mAtm.getLastStopAppSwitchesTime()) { - if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "[WindowProcessController(" + mPid - + ")] Activity start allowed: within " - + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period"); - } - return true; - } + boolean areBackgroundActivityStartsAllowed() { + // allow if any activity in the caller has either started or finished very recently, and + // it must be started or finished after last stop app switches time. + final long now = SystemClock.uptimeMillis(); + if (now - mLastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS + || now - mLastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) { + // if activity is started and finished before stop app switch time, we should not + // let app to be able to start background activity even it's in grace period. + if (mLastActivityLaunchTime > mAtm.getLastStopAppSwitchesTime() + || mLastActivityFinishTime > mAtm.getLastStopAppSwitchesTime()) { if (DEBUG_ACTIVITY_STARTS) { - Slog.d(TAG, "[WindowProcessController(" + mPid + ")] Activity start within " - + ACTIVITY_BG_START_GRACE_PERIOD_MS - + "ms grace period but also within stop app switch window"); + Slog.d(TAG, "[WindowProcessController(" + mPid + + ")] Activity start allowed: within " + + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period"); } - + return true; + } + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "[WindowProcessController(" + mPid + ")] Activity start within " + + ACTIVITY_BG_START_GRACE_PERIOD_MS + + "ms grace period but also within stop app switch window"); } + } // allow if the proc is instrumenting with background activity starts privs if (mInstrumentingWithBackgroundActivityStartPrivileges) { @@ -543,7 +539,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return true; } // allow if the caller has an activity in any foreground task - if (appSwitchAllowed && hasActivityInVisibleTask()) { + if (hasActivityInVisibleTask()) { if (DEBUG_ACTIVITY_STARTS) { Slog.d(TAG, "[WindowProcessController(" + mPid + ")] Activity start allowed: process has activity in foreground task"); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 4d553e2f92aa..3bfcb6def252 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4255,18 +4255,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mInjector.getPackageManager().getPackagesForUid( mInjector.binderGetCallingUid())) .write(); - final int callingUserId = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getCallerIdentity(); - if (parent) { - enforceProfileOwnerOrSystemUser(); - } - enforceUserUnlocked(callingUserId); + Preconditions.checkCallAuthorization(!parent || (isDeviceOwner(caller) + || isProfileOwner(caller) || isSystemUid(caller)), + "Only profile owner, device owner and system may call this method."); + enforceUserUnlocked(caller.getUserId()); mContext.enforceCallingOrSelfPermission( REQUEST_PASSWORD_COMPLEXITY, "Must have " + REQUEST_PASSWORD_COMPLEXITY + " permission."); synchronized (getLockObject()) { - final int credentialOwner = getCredentialOwner(callingUserId, parent); + final int credentialOwner = getCredentialOwner(caller.getUserId(), parent); PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner); return metrics == null ? PASSWORD_COMPLEXITY_NONE : metrics.determineComplexity(); } @@ -7299,7 +7299,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean hasDeviceOwner() { - enforceDeviceOwnerOrManageUsers(); + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller)); return mOwners.hasDeviceOwner(); } @@ -8355,32 +8356,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS); } - private void enforceDeviceOwnerOrManageUsers() { - final CallerIdentity caller = getCallerIdentity(); - if (isDeviceOwner(caller)) { - return; - } - Preconditions.checkCallAuthorization(canManageUsers(caller)); - } - - private void enforceProfileOwnerOrSystemUser() { - final CallerIdentity caller = getCallerIdentity(); - if (isDeviceOwner(caller) || isProfileOwner(caller)) { - return; - } - Preconditions.checkState(isSystemUid(caller), - "Only profile owner, device owner and system may call this method."); - } - - private void enforceProfileOwnerOrFullCrossUsersPermission(CallerIdentity caller, - int userId) { - if ((userId == caller.getUserId()) && (isProfileOwner(caller) || isDeviceOwner(caller))) { - // Device Owner/Profile Owner may access the user it runs on. - return; - } - Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId)); - } - private boolean canUserUseLockTaskLocked(int userId) { if (isUserAffiliatedWithDeviceLocked(userId)) { return true; @@ -12458,7 +12433,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return null; } - enforceDeviceOwnerOrManageUsers(); + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller)); synchronized (getLockObject()) { final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked(); return deviceOwnerAdmin == null ? null : deviceOwnerAdmin.organizationName; @@ -13605,19 +13581,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public long getLastSecurityLogRetrievalTime() { - enforceDeviceOwnerOrManageUsers(); + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller)); return getUserData(UserHandle.USER_SYSTEM).mLastSecurityLogRetrievalTime; } @Override public long getLastBugReportRequestTime() { - enforceDeviceOwnerOrManageUsers(); + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller)); return getUserData(UserHandle.USER_SYSTEM).mLastBugReportRequestTime; } @Override public long getLastNetworkLogRetrievalTime() { - enforceDeviceOwnerOrManageUsers(); + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller)); return getUserData(UserHandle.USER_SYSTEM).mLastNetworkLogsRetrievalTime; } @@ -13721,15 +13700,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isCurrentInputMethodSetByOwner() { - enforceProfileOwnerOrSystemUser(); - return getUserData(mInjector.userHandleGetCallingUserId()).mCurrentInputMethodSet; + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(isDeviceOwner(caller) + || isProfileOwner(caller) || isSystemUid(caller), + "Only profile owner, device owner and system may call this method."); + return getUserData(caller.getUserId()).mCurrentInputMethodSet; } @Override public StringParceledListSlice getOwnerInstalledCaCerts(@NonNull UserHandle user) { final int userId = user.getIdentifier(); final CallerIdentity caller = getCallerIdentity(); - enforceProfileOwnerOrFullCrossUsersPermission(caller, userId); + Preconditions.checkCallAuthorization((userId == caller.getUserId()) + || isProfileOwner(caller) || isDeviceOwner(caller) + || hasFullCrossUsersPermission(caller, userId)); + synchronized (getLockObject()) { return new StringParceledListSlice( new ArrayList<>(getUserData(userId).mOwnerInstalledCaCerts)); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e116a353c723..03edc585b46a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1131,6 +1131,7 @@ public final class SystemServer implements Dumpable { IStorageManager storageManager = null; NetworkManagementService networkManagement = null; IpSecService ipSecService = null; + VcnManagementService vcnManagement = null; NetworkStatsService networkStats = null; NetworkPolicyManagerService networkPolicy = null; IConnectivityManager connectivity = null; @@ -1581,6 +1582,15 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); + t.traceBegin("StartVcnManagementService"); + try { + vcnManagement = VcnManagementService.create(context); + ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement); + } catch (Throwable e) { + reportWtf("starting VCN Management Service", e); + } + t.traceEnd(); + t.traceBegin("StartTextServicesManager"); mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class); t.traceEnd(); @@ -2371,6 +2381,7 @@ public final class SystemServer implements Dumpable { final MediaRouterService mediaRouterF = mediaRouter; final MmsServiceBroker mmsServiceF = mmsService; final IpSecService ipSecServiceF = ipSecService; + final VcnManagementService vcnManagementF = vcnManagement; final WindowManagerService windowManagerF = wm; final ConnectivityManager connectivityF = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); @@ -2463,6 +2474,15 @@ public final class SystemServer implements Dumpable { reportWtf("making IpSec Service ready", e); } t.traceEnd(); + t.traceBegin("MakeVcnManagementServiceReady"); + try { + if (vcnManagementF != null) { + vcnManagementF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making VcnManagementService ready", e); + } + t.traceEnd(); t.traceBegin("MakeNetworkStatsServiceReady"); try { if (networkStatsF != null) { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java index 3aedd3c7d753..d260e4dce79a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java @@ -22,6 +22,7 @@ import static android.app.AppOpsManager.OP_MONITOR_LOCATION; import static android.location.Criteria.ACCURACY_COARSE; import static android.location.Criteria.ACCURACY_FINE; import static android.location.Criteria.POWER_HIGH; +import static android.location.LocationRequest.PASSIVE_INTERVAL; import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; import static androidx.test.ext.truth.location.LocationSubject.assertThat; @@ -598,6 +599,38 @@ public class LocationProviderManagerTest { } @Test + public void testRegisterListener_Coarse() throws Exception { + ILocationListener listener = createMockLocationListener(); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + IDENTITY, + PERMISSION_COARSE, + listener); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + verify(listener, times(1)) + .onLocationChanged(any(Location.class), nullable(IRemoteCallback.class)); + } + + @Test + public void testRegisterListener_Coarse_Passive() throws Exception { + ILocationListener listener = createMockLocationListener(); + mManager.registerLocationRequest( + new LocationRequest.Builder(PASSIVE_INTERVAL) + .setMinUpdateIntervalMillis(0) + .setWorkSource(WORK_SOURCE).build(), + IDENTITY, + PERMISSION_COARSE, + listener); + + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + mProvider.setProviderLocation(createLocation(NAME, mRandom)); + verify(listener, times(1)) + .onLocationChanged(any(Location.class), nullable(IRemoteCallback.class)); + } + + @Test public void testGetCurrentLocation() throws Exception { ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class); diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java index 081bfb5b2724..0d0ac6d2794d 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.appsearch.external.localbackend; +package com.android.server.appsearch.external.localstorage; import static com.google.common.truth.Truth.assertThat; diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java index a95290d89b59..85d4f01f8d41 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverterTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.appsearch.external.localbackend.converter; +package com.android.server.appsearch.external.localstorage.converter; import static com.google.common.truth.Truth.assertThat; @@ -53,13 +53,13 @@ public class GenericDocumentToProtoConverterTest { .setScore(1) .setTtlMillis(1L) .setNamespace("namespace") - .setProperty("longKey1", 1L) - .setProperty("doubleKey1", 1.0) - .setProperty("booleanKey1", true) - .setProperty("stringKey1", "test-value1") - .setProperty("byteKey1", BYTE_ARRAY_1, BYTE_ARRAY_2) - .setProperty("documentKey1", DOCUMENT_PROPERTIES_1) - .setProperty(GenericDocument.PROPERTIES_FIELD, DOCUMENT_PROPERTIES_2) + .setPropertyLong("longKey1", 1L) + .setPropertyDouble("doubleKey1", 1.0) + .setPropertyBoolean("booleanKey1", true) + .setPropertyString("stringKey1", "test-value1") + .setPropertyBytes("byteKey1", BYTE_ARRAY_1, BYTE_ARRAY_2) + .setPropertyDocument("documentKey1", DOCUMENT_PROPERTIES_1) + .setPropertyDocument("documentKey2", DOCUMENT_PROPERTIES_2) .build(); // Create the Document proto. Need to sort the property order by key. @@ -87,8 +87,8 @@ public class GenericDocumentToProtoConverterTest { PropertyProto.newBuilder().setName("documentKey1") .addDocumentValues( GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_1))); - propertyProtoMap.put(GenericDocument.PROPERTIES_FIELD, - PropertyProto.newBuilder().setName(GenericDocument.PROPERTIES_FIELD) + propertyProtoMap.put("documentKey2", + PropertyProto.newBuilder().setName("documentKey2") .addDocumentValues( GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_2))); List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet()); diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java index 8b2fd1ce9fef..7336c3c36417 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SchemaToProtoConverterTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.appsearch.external.localbackend.converter; +package com.android.server.appsearch.external.localstorage.converter; import static com.google.common.truth.Truth.assertThat; @@ -50,7 +50,6 @@ public class SchemaToProtoConverterTest { .addProperties(PropertyConfigProto.newBuilder() .setPropertyName("subject") .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setSchemaType("") .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) .setIndexingConfig( com.android.server.appsearch.proto.IndexingConfig.newBuilder() @@ -60,7 +59,6 @@ public class SchemaToProtoConverterTest { ).addProperties(PropertyConfigProto.newBuilder() .setPropertyName("body") .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setSchemaType("") .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) .setIndexingConfig( com.android.server.appsearch.proto.IndexingConfig.newBuilder() @@ -94,7 +92,6 @@ public class SchemaToProtoConverterTest { .addProperties(PropertyConfigProto.newBuilder() .setPropertyName("artist") .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setSchemaType("") .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED) .setIndexingConfig( com.android.server.appsearch.proto.IndexingConfig.newBuilder() @@ -104,7 +101,6 @@ public class SchemaToProtoConverterTest { ).addProperties(PropertyConfigProto.newBuilder() .setPropertyName("pubDate") .setDataType(PropertyConfigProto.DataType.Code.INT64) - .setSchemaType("") .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) .setIndexingConfig( com.android.server.appsearch.proto.IndexingConfig.newBuilder() diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java index e9357aa60632..d5762a1317b5 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SnippetTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.appsearch.external.localbackend.converter; +package com.android.server.appsearch.external.localstorage.converter; import static com.google.common.truth.Truth.assertThat; @@ -123,7 +123,7 @@ public class SnippetTest { for (SearchResultProto.ResultProto proto : searchResultProto.getResultsList()) { SearchResult result = SearchResultToProtoConverter.convertSearchResult(proto); - assertThat(result.getMatches()).isEqualTo(null); + assertThat(result.getMatches()).isEmpty(); } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java index be05245deea1..9660d6ba2f74 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java @@ -15,33 +15,33 @@ */ package com.android.server.devicepolicy; -import android.test.AndroidTestCase; +import static com.google.common.truth.Truth.assertThat; + import android.test.suitebuilder.annotation.SmallTest; +import org.junit.Test; + /** * Test for {@link DevicePolicyConstants}. * - m FrameworksServicesTests && - adb install \ - -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && - adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyConstantsTest \ - -w com.android.frameworks.servicestests - - - -w com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner + * <p>Run this test with: + * + * {@code atest FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyConstantsTest} */ @SmallTest -public class DevicePolicyConstantsTest extends AndroidTestCase { +public class DevicePolicyConstantsTest { private static final String TAG = "DevicePolicyConstantsTest"; + @Test public void testDefaultValues() throws Exception { final DevicePolicyConstants constants = DevicePolicyConstants.loadFromString(""); - assertEquals(1 * 60 * 60, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC); - assertEquals(24 * 60 * 60, constants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC); - assertEquals(2.0, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE); + assertThat(constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC).isEqualTo(1 * 60 * 60); + assertThat(constants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC).isEqualTo(24 * 60 * 60); + assertThat(constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE).isWithin(1.0e-10).of(2.0); } + @Test public void testCustomValues() throws Exception { final DevicePolicyConstants constants = DevicePolicyConstants.loadFromString( "das_died_service_reconnect_backoff_sec=10," @@ -49,11 +49,13 @@ public class DevicePolicyConstantsTest extends AndroidTestCase { + "das_died_service_reconnect_max_backoff_sec=15" ); - assertEquals(10, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC); - assertEquals(15, constants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC); - assertEquals(1.25, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE); + assertThat(constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC).isEqualTo(10); + assertThat(constants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC).isEqualTo(15); + assertThat(constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE).isWithin(1.0e-10) + .of(1.25); } + @Test public void testMinMax() throws Exception { final DevicePolicyConstants constants = DevicePolicyConstants.loadFromString( "das_died_service_reconnect_backoff_sec=3," @@ -61,8 +63,8 @@ public class DevicePolicyConstantsTest extends AndroidTestCase { + "das_died_service_reconnect_max_backoff_sec=1" ); - assertEquals(5, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC); - assertEquals(5, constants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC); - assertEquals(1.0, constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE); + assertThat(constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC).isEqualTo(5); + assertThat(constants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC).isEqualTo(5); + assertThat(constants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE).isWithin(1.0e-10).of(1.0); } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyEventLoggerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyEventLoggerTest.java index b24bca8fc050..350b390a2130 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyEventLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyEventLoggerTest.java @@ -46,8 +46,8 @@ public class DevicePolicyEventLoggerTest { .setTimePeriod(1234L); assertThat(eventLogger.getEventId()).isEqualTo(5); assertThat(eventLogger.getBoolean()).isTrue(); - assertThat(eventLogger.getStringArray()) - .isEqualTo(new String[] {"string1", "string2", "string3"}); + assertThat(eventLogger.getStringArray()).asList() + .containsExactly("string1", "string2", "string3"); assertThat(eventLogger.getAdminPackageName()).isEqualTo("com.test.package"); assertThat(eventLogger.getInt()).isEqualTo(4321); assertThat(eventLogger.getTimePeriod()).isEqualTo(1234L); @@ -57,23 +57,22 @@ public class DevicePolicyEventLoggerTest { public void testStrings() { assertThat(DevicePolicyEventLogger .createEvent(0) - .setStrings("string1", "string2", "string3").getStringArray()) - .isEqualTo(new String[] {"string1", "string2", "string3"}); + .setStrings("string1", "string2", "string3").getStringArray()).asList() + .containsExactly("string1", "string2", "string3").inOrder(); assertThat(DevicePolicyEventLogger .createEvent(0) .setStrings("string1", new String[] {"string2", "string3"}).getStringArray()) - .isEqualTo(new String[] {"string1", "string2", "string3"}); + .asList().containsExactly("string1", "string2", "string3").inOrder(); assertThat(DevicePolicyEventLogger .createEvent(0) .setStrings("string1", "string2", new String[] {"string3"}).getStringArray()) - .isEqualTo(new String[] {"string1", "string2", "string3"}); - + .asList().containsExactly("string1", "string2", "string3").inOrder(); assertThat(DevicePolicyEventLogger .createEvent(0) - .setStrings((String) null).getStringArray()) - .isEqualTo(new String[] {null}); + .setStrings((String) null).getStringArray()).asList() + .containsExactly((String) null); assertThat(DevicePolicyEventLogger .createEvent(0) @@ -106,8 +105,8 @@ public class DevicePolicyEventLoggerTest { .createEvent(0); assertThat(eventLogger.getEventId()).isEqualTo(0); assertThat(eventLogger.getBoolean()).isFalse(); - assertThat(eventLogger.getStringArray()).isEqualTo(null); - assertThat(eventLogger.getAdminPackageName()).isEqualTo(null); + assertThat(eventLogger.getStringArray()).isNull(); + assertThat(eventLogger.getAdminPackageName()).isNull(); assertThat(eventLogger.getInt()).isEqualTo(0); assertThat(eventLogger.getTimePeriod()).isEqualTo(0L); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java index 3167820f0a48..fa3f45c08202 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -20,7 +20,9 @@ import static android.os.UserHandle.USER_SYSTEM; import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; -import static org.junit.Assert.assertArrayEquals; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; @@ -43,17 +45,23 @@ import android.platform.test.annotations.Presubmit; import android.provider.Settings; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.servicestests.R; import com.android.server.LocalServices; import com.android.server.SystemService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.Set; @Presubmit +@RunWith(AndroidJUnit4.class) public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { private static final String USER_TYPE_EMPTY = ""; @@ -63,9 +71,8 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { private DpmMockContext mContext; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { mContext = getContext(); @@ -77,6 +84,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { .thenReturn(true); } + @Test public void testMigration() throws Exception { final File user10dir = getServices().addUser(10, 0, USER_TYPE_EMPTY); final File user11dir = getServices().addUser(11, 0, @@ -160,19 +168,19 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { mContext.binder.restoreCallingIdentity(ident); } - assertTrue(dpms.mOwners.hasDeviceOwner()); - assertFalse(dpms.mOwners.hasProfileOwner(USER_SYSTEM)); - assertTrue(dpms.mOwners.hasProfileOwner(10)); - assertTrue(dpms.mOwners.hasProfileOwner(11)); - assertFalse(dpms.mOwners.hasProfileOwner(12)); + assertThat(dpms.mOwners.hasDeviceOwner()).isTrue(); + assertThat(dpms.mOwners.hasProfileOwner(USER_SYSTEM)).isFalse(); + assertThat(dpms.mOwners.hasProfileOwner(10)).isTrue(); + assertThat(dpms.mOwners.hasProfileOwner(11)).isTrue(); + assertThat(dpms.mOwners.hasProfileOwner(12)).isFalse(); // Now all information should be migrated. - assertFalse(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration( - USER_SYSTEM)); - assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(12)); + assertThat(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(USER_SYSTEM)) + .isFalse(); + assertThat(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse(); + assertThat(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); + assertThat(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(12)).isFalse(); // Check the new base restrictions. DpmTestUtils.assertRestrictions( @@ -221,6 +229,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { dpms.getProfileOwnerAdminLocked(11).ensureUserRestrictions()); } + @Test public void testMigration2_profileOwnerOnUser0() throws Exception { setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID); @@ -271,13 +280,13 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { } finally { mContext.binder.restoreCallingIdentity(ident); } - assertFalse(dpms.mOwners.hasDeviceOwner()); - assertTrue(dpms.mOwners.hasProfileOwner(USER_SYSTEM)); + assertThat(dpms.mOwners.hasDeviceOwner()).isFalse(); + assertThat(dpms.mOwners.hasProfileOwner(USER_SYSTEM)).isTrue(); // Now all information should be migrated. - assertFalse(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration( - USER_SYSTEM)); + assertThat(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(USER_SYSTEM)) + .isFalse(); // Check the new base restrictions. DpmTestUtils.assertRestrictions( @@ -297,6 +306,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { } // Test setting default restrictions for managed profile. + @Test public void testMigration3_managedProfileOwner() throws Exception { // Create a managed profile user. final File user10dir = getServices().addUser(10, 0, @@ -339,8 +349,8 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { mContext.binder.restoreCallingIdentity(ident); } - assertFalse(dpms.mOwners.hasDeviceOwner()); - assertTrue(dpms.mOwners.hasProfileOwner(10)); + assertThat(dpms.mOwners.hasDeviceOwner()).isFalse(); + assertThat(dpms.mOwners.hasProfileOwner(10)).isTrue(); // Check that default restrictions were applied. DpmTestUtils.assertRestrictions( @@ -352,11 +362,12 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { final Set<String> alreadySet = dpms.getProfileOwnerAdminLocked(10).defaultEnabledRestrictionsAlreadySet; - assertEquals(alreadySet.size(), 1); - assertTrue(alreadySet.contains(UserManager.DISALLOW_BLUETOOTH_SHARING)); + assertThat(alreadySet).hasSize(1); + assertThat(alreadySet.contains(UserManager.DISALLOW_BLUETOOTH_SHARING)).isTrue(); } @SmallTest + @Test public void testCompMigrationUnAffiliated_skipped() throws Exception { prepareAdmin1AsDo(); prepareAdminAnotherPackageAsPo(COPE_PROFILE_USER_ID); @@ -364,10 +375,11 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { final DevicePolicyManagerServiceTestable dpms = bootDpmsUp(); // DO should still be DO since no migration should happen. - assertTrue(dpms.mOwners.hasDeviceOwner()); + assertThat(dpms.mOwners.hasDeviceOwner()).isTrue(); } @SmallTest + @Test public void testCompMigrationAffiliated() throws Exception { prepareAdmin1AsDo(); prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R); @@ -378,48 +390,54 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { final DevicePolicyManagerServiceTestable dpms = bootDpmsUp(); // DO should cease to be DO. - assertFalse(dpms.mOwners.hasDeviceOwner()); + assertThat(dpms.mOwners.hasDeviceOwner()).isFalse(); final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext); poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID); runAsCaller(poContext, dpms, dpm -> { - assertEquals("Password history policy wasn't migrated to PO parent instance", - 33, dpm.getParentProfileInstance(admin1).getPasswordHistoryLength(admin1)); - assertEquals("Password history policy was put into non-parent PO instance", - 0, dpm.getPasswordHistoryLength(admin1)); - assertTrue("Screen capture restriction wasn't migrated to PO parent instance", - dpm.getParentProfileInstance(admin1).getScreenCaptureDisabled(admin1)); - - assertArrayEquals("Accounts with management disabled weren't migrated to PO parent", - new String[] {"com.google-primary"}, - dpm.getParentProfileInstance(admin1).getAccountTypesWithManagementDisabled()); - assertArrayEquals("Accounts with management disabled for profile were lost", - new String[] {"com.google-profile"}, - dpm.getAccountTypesWithManagementDisabled()); - - assertTrue("User restriction wasn't migrated to PO parent instance", - dpm.getParentProfileInstance(admin1).getUserRestrictions(admin1) - .containsKey(UserManager.DISALLOW_BLUETOOTH)); - assertFalse("User restriction was put into non-parent PO instance", - dpm.getUserRestrictions(admin1).containsKey(UserManager.DISALLOW_BLUETOOTH)); - - assertTrue("User restriction wasn't migrated to PO parent instance", - dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID) - .getParentActiveAdmin() - .getEffectiveRestrictions() - .containsKey(UserManager.DISALLOW_CONFIG_DATE_TIME)); - assertFalse("User restriction was put into non-parent PO instance", - dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID) - .getEffectiveRestrictions() - .containsKey(UserManager.DISALLOW_CONFIG_DATE_TIME)); - assertEquals("Personal apps suspension wasn't migrated", - DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED, - dpm.getPersonalAppsSuspendedReasons(admin1)); + assertWithMessage("Password history policy wasn't migrated to PO parent instance") + .that(dpm.getParentProfileInstance(admin1).getPasswordHistoryLength(admin1)) + .isEqualTo(33); + assertWithMessage("Password history policy was put into non-parent PO instance") + .that(dpm.getPasswordHistoryLength(admin1)).isEqualTo(0); + assertWithMessage("Screen capture restriction wasn't migrated to PO parent instance") + .that(dpm.getParentProfileInstance(admin1).getScreenCaptureDisabled(admin1)) + .isTrue(); + + assertWithMessage("Accounts with management disabled weren't migrated to PO parent") + .that(dpm.getParentProfileInstance(admin1) + .getAccountTypesWithManagementDisabled()).asList() + .containsExactly("com.google-primary"); + + assertWithMessage("Accounts with management disabled for profile were lost") + .that(dpm.getAccountTypesWithManagementDisabled()).asList() + .containsExactly("com.google-profile"); + + assertWithMessage("User restriction wasn't migrated to PO parent instance") + .that(dpm.getParentProfileInstance(admin1).getUserRestrictions(admin1).keySet()) + .contains(UserManager.DISALLOW_BLUETOOTH); + + assertWithMessage("User restriction was put into non-parent PO instance").that( + dpm.getUserRestrictions(admin1).keySet()) + .doesNotContain(UserManager.DISALLOW_BLUETOOTH); + + assertWithMessage("User restriction wasn't migrated to PO parent instance") + .that(dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID) + .getParentActiveAdmin().getEffectiveRestrictions().keySet()) + .contains(UserManager.DISALLOW_CONFIG_DATE_TIME); + assertWithMessage("User restriction was put into non-parent PO instance") + .that(dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID) + .getEffectiveRestrictions().keySet()) + .doesNotContain(UserManager.DISALLOW_CONFIG_DATE_TIME); + assertWithMessage("Personal apps suspension wasn't migrated") + .that(dpm.getPersonalAppsSuspendedReasons(admin1)) + .isEqualTo(DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED); }); } @SmallTest + @Test public void testCompMigration_keepSuspendedAppsWhenDpcIsRPlus() throws Exception { prepareAdmin1AsDo(); prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R); @@ -445,13 +463,14 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID); runAsCaller(poContext, dpms, dpm -> { - assertEquals("Personal apps suspension wasn't migrated", - DevicePolicyManager.PERSONAL_APPS_SUSPENDED_EXPLICITLY, - dpm.getPersonalAppsSuspendedReasons(admin1)); + assertWithMessage("Personal apps suspension wasn't migrated") + .that(dpm.getPersonalAppsSuspendedReasons(admin1)) + .isEqualTo(DevicePolicyManager.PERSONAL_APPS_SUSPENDED_EXPLICITLY); }); } @SmallTest + @Test public void testCompMigration_unsuspendAppsWhenDpcNotRPlus() throws Exception { prepareAdmin1AsDo(); prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.Q); @@ -470,9 +489,9 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID); runAsCaller(poContext, dpms, dpm -> { - assertEquals("Personal apps weren't unsuspended", - DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED, - dpm.getPersonalAppsSuspendedReasons(admin1)); + assertWithMessage("Personal apps weren't unsuspended") + .that(dpm.getPersonalAppsSuspendedReasons(admin1)) + .isEqualTo(DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED); }); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index c4f7b9547277..8d7bc16fcf56 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -39,7 +39,9 @@ import static com.android.server.devicepolicy.DpmMockContext.CALLER_USER_HANDLE; import static com.android.server.testutils.TestUtils.assertExpectException; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -89,13 +91,14 @@ import android.os.Bundle; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.annotations.FlakyTest; import android.platform.test.annotations.Presubmit; import android.provider.Settings; import android.security.KeyChain; import android.security.keystore.AttestationUtils; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; -import android.test.MoreAsserts; +import android.test.MoreAsserts; // TODO(b/171932723): replace by Truth import android.util.ArraySet; import android.util.Pair; @@ -111,6 +114,9 @@ import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsLi import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import org.mockito.Mockito; import org.mockito.internal.util.collections.Sets; import org.mockito.stubbing.Answer; @@ -127,17 +133,11 @@ import java.util.concurrent.TimeUnit; /** * Tests for DevicePolicyManager( and DevicePolicyManagerService). - * You can run them via: - m FrameworksServicesTests && - adb install \ - -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && - adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyManagerTest \ - -w com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner - - (mmma frameworks/base/services/tests/servicestests/ for non-ninja build) * - * , or: - * runtest -c com.android.server.devicepolicy.DevicePolicyManagerTest frameworks-services + * <p>Run this test with: + * + * {@code atest FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest} + * */ @SmallTest @Presubmit @@ -205,9 +205,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { private static final String PROFILE_OFF_SUSPENSION_TEXT = "suspension_text"; private static final String PROFILE_OFF_SUSPENSION_SOON_TEXT = "suspension_tomorrow_text"; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { mContext = getContext(); mServiceContext = mContext; @@ -251,11 +250,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { return dpms.mTransferOwnershipMetadataManager; } - @Override - protected void tearDown() throws Exception { + @After + public void tearDown() throws Exception { flushTasks(dpms); getMockTransferMetadataManager().deleteMetadataFile(); - super.tearDown(); } private void initializeDpms() { @@ -336,14 +334,15 @@ public class DevicePolicyManagerTest extends DpmTestBase { // PO needs to be a DA. dpm.setActiveAdmin(admin, /*replace=*/ false); // Fire! - assertTrue(dpm.setProfileOwner(admin, "owner-name", CALLER_USER_HANDLE)); + assertThat(dpm.setProfileOwner(admin, "owner-name", CALLER_USER_HANDLE)).isTrue(); // Check - assertEquals(admin, dpm.getProfileOwnerAsUser(CALLER_USER_HANDLE)); + assertThat(dpm.getProfileOwnerAsUser(CALLER_USER_HANDLE)).isEqualTo(admin); }); mServiceContext.binder.restoreCallingIdentity(ident); } + @Test public void testHasNoFeature() throws Exception { when(getServices().packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN))) .thenReturn(false); @@ -352,9 +351,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { new DevicePolicyManagerServiceTestable(getServices(), mContext); // If the device has no DPMS feature, it shouldn't register the local service. - assertNull(LocalServices.getService(DevicePolicyManagerInternal.class)); + assertThat(LocalServices.getService(DevicePolicyManagerInternal.class)).isNull(); } + @Test public void testLoadAdminData() throws Exception { // Device owner in SYSTEM_USER setDeviceOwner(); @@ -365,7 +365,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { final int ANOTHER_UID = UserHandle.getUid(CALLER_USER_HANDLE, 1306); setUpPackageManagerForFakeAdmin(adminAnotherPackage, ANOTHER_UID, admin2); dpm.setActiveAdmin(adminAnotherPackage, /* replace =*/ false, CALLER_USER_HANDLE); - assertTrue(dpm.isAdminActiveAsUser(adminAnotherPackage, CALLER_USER_HANDLE)); + assertThat(dpm.isAdminActiveAsUser(adminAnotherPackage, CALLER_USER_HANDLE)).isTrue(); initializeDpms(); @@ -381,6 +381,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().networkPolicyManagerInternal).onAdminDataAvailable(); } + @Test public void testLoadAdminData_noAdmins() throws Exception { final int ANOTHER_USER_ID = 15; getServices().addUser(ANOTHER_USER_ID, 0, ""); @@ -399,6 +400,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /** * Caller doesn't have proper permissions. */ + @Test public void testSetActiveAdmin_SecurityException() { // 1. Failure cases. @@ -422,6 +424,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * {@link DevicePolicyManager#getActiveAdmins} * {@link DevicePolicyManager#getActiveAdminsAsUser} */ + @Test public void testSetActiveAdmin() throws Exception { // 1. Make sure the caller has proper permissions. mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); @@ -456,9 +459,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { // TODO Verify other calls too. // Make sure it's active admin1. - assertTrue(dpm.isAdminActive(admin1)); - assertFalse(dpm.isAdminActive(admin2)); - assertFalse(dpm.isAdminActive(admin3)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); + assertThat(dpm.isAdminActive(admin2)).isFalse(); + assertThat(dpm.isAdminActive(admin3)).isFalse(); // But not admin1 for a different user. @@ -466,8 +469,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { // (Because we're checking a different user's status from CALLER_USER_HANDLE.) mContext.callerPermissions.add("android.permission.INTERACT_ACROSS_USERS_FULL"); - assertFalse(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE + 1)); - assertFalse(dpm.isAdminActiveAsUser(admin2, CALLER_USER_HANDLE + 1)); + assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE + 1)).isFalse(); + assertThat(dpm.isAdminActiveAsUser(admin2, CALLER_USER_HANDLE + 1)).isFalse(); mContext.callerPermissions.remove("android.permission.INTERACT_ACROSS_USERS_FULL"); @@ -479,9 +482,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setActiveAdmin(admin2, /* replace =*/ false); // Now we have two admins. - assertTrue(dpm.isAdminActive(admin1)); - assertTrue(dpm.isAdminActive(admin2)); - assertFalse(dpm.isAdminActive(admin3)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); + assertThat(dpm.isAdminActive(admin2)).isTrue(); + assertThat(dpm.isAdminActive(admin3)).isFalse(); // Admin2 was already enabled, so setApplicationEnabledSetting() shouldn't have called // again. (times(1) because it was previously called for admin1) @@ -508,9 +511,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { // 6. Test getActiveAdmins() List<ComponentName> admins = dpm.getActiveAdmins(); - assertEquals(2, admins.size()); - assertEquals(admin1, admins.get(0)); - assertEquals(admin2, admins.get(1)); + assertThat(admins.size()).isEqualTo(2); + assertThat(admins.get(0)).isEqualTo(admin1); + assertThat(admins.get(1)).isEqualTo(admin2); // There shouldn't be any callback to UsageStatsManagerInternal when the admin is being // replaced @@ -519,12 +522,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Another user has no admins. mContext.callerPermissions.add("android.permission.INTERACT_ACROSS_USERS_FULL"); - assertEquals(0, DpmTestUtils.getListSizeAllowingNull( - dpm.getActiveAdminsAsUser(CALLER_USER_HANDLE + 1))); + assertThat(DpmTestUtils.getListSizeAllowingNull( + dpm.getActiveAdminsAsUser(CALLER_USER_HANDLE + 1))).isEqualTo(0); mContext.callerPermissions.remove("android.permission.INTERACT_ACROSS_USERS_FULL"); } + @Test public void testSetActiveAdmin_multiUsers() throws Exception { final int ANOTHER_USER_ID = 100; @@ -544,12 +548,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { mMockContext.binder.callingUid = DpmMockContext.CALLER_UID; - assertTrue(dpm.isAdminActive(admin1)); - assertFalse(dpm.isAdminActive(admin2)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); + assertThat(dpm.isAdminActive(admin2)).isFalse(); mMockContext.binder.callingUid = ANOTHER_ADMIN_UID; - assertFalse(dpm.isAdminActive(admin1)); - assertTrue(dpm.isAdminActive(admin2)); + assertThat(dpm.isAdminActive(admin1)).isFalse(); + assertThat(dpm.isAdminActive(admin2)).isTrue(); } /** @@ -557,12 +561,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { * {@link DevicePolicyManager#setActiveAdmin} * with replace=false */ + @Test public void testSetActiveAdmin_twiceWithoutReplace() throws Exception { // 1. Make sure the caller has proper permissions. mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.isAdminActive(admin1)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); // Add the same admin1 again without replace, which should throw. assertExpectException(IllegalArgumentException.class, /* messageRegex= */ null, @@ -574,6 +579,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * {@link DevicePolicyManager#setActiveAdmin} when the admin isn't protected with * BIND_DEVICE_ADMIN. */ + @Test public void testSetActiveAdmin_permissionCheck() throws Exception { // 1. Make sure the caller has proper permissions. mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); @@ -581,13 +587,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertExpectException(IllegalArgumentException.class, /* messageRegex= */ permission.BIND_DEVICE_ADMIN, () -> dpm.setActiveAdmin(adminNoPerm, /* replace =*/ false)); - assertFalse(dpm.isAdminActive(adminNoPerm)); + assertThat(dpm.isAdminActive(adminNoPerm)).isFalse(); // Change the target API level to MNC. Now it can be set as DA. setUpPackageManagerForAdmin(adminNoPerm, DpmMockContext.CALLER_UID, null, VERSION_CODES.M); dpm.setActiveAdmin(adminNoPerm, /* replace =*/ false); - assertTrue(dpm.isAdminActive(adminNoPerm)); + assertThat(dpm.isAdminActive(adminNoPerm)).isTrue(); // TODO Test the "load from the file" case where DA will still be loaded even without // BIND_DEVICE_ADMIN and target API is N. @@ -597,6 +603,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * Test for: * {@link DevicePolicyManager#removeActiveAdmin} */ + @Test public void testRemoveActiveAdmin_SecurityException() { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); @@ -604,9 +611,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.isAdminActive(admin1)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); - assertFalse(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)).isFalse(); // Directly call the DPMS method with a different userid, which should fail. assertExpectException(SecurityException.class, /* messageRegex =*/ null, @@ -627,6 +634,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * {@link DevicePolicyManager#removeActiveAdmin} should fail with the user is not unlocked * (because we can't send the remove broadcast). */ + @Test public void testRemoveActiveAdmin_userNotRunningOrLocked() { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); @@ -636,9 +644,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.isAdminActive(admin1)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); - assertFalse(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)).isFalse(); // 1. User not unlocked. setUserUnlocked(CALLER_USER_HANDLE, false); @@ -646,7 +654,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /* messageRegex= */ "User must be running and unlocked", () -> dpm.removeActiveAdmin(admin1)); - assertFalse(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)).isFalse(); verify(getServices().usageStatsManagerInternal, times(0)).setActiveAdminApps( null, CALLER_USER_HANDLE); @@ -654,7 +662,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { setUserUnlocked(CALLER_USER_HANDLE, true); dpm.removeActiveAdmin(admin1); - assertFalse(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse(); verify(getServices().usageStatsManagerInternal).setActiveAdminApps( null, CALLER_USER_HANDLE); } @@ -663,6 +671,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * Test for: * {@link DevicePolicyManager#removeActiveAdmin} */ + @Test public void testRemoveActiveAdmin_fromDifferentUserWithINTERACT_ACROSS_USERS_FULL() { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); @@ -670,8 +679,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.isAdminActive(admin1)); - assertFalse(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); + assertThat(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)).isFalse(); // Different user, but should work, because caller has proper permissions. mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL); @@ -680,7 +689,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = 1234567; dpms.removeActiveAdmin(admin1, CALLER_USER_HANDLE); - assertFalse(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse(); verify(getServices().usageStatsManagerInternal).setActiveAdminApps( null, CALLER_USER_HANDLE); @@ -691,6 +700,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * Test for: * {@link DevicePolicyManager#removeActiveAdmin} */ + @Test public void testRemoveActiveAdmin_sameUserNoMANAGE_DEVICE_ADMINS() { // Need MANAGE_DEVICE_ADMINS for setActiveAdmin. We'll remove it later. mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); @@ -699,8 +709,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.isAdminActive(admin1)); - assertFalse(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); + assertThat(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)).isFalse(); // Broadcast from saveSettingsLocked(). verify(mContext.spiedContext, times(1)).sendBroadcastAsUser( @@ -725,7 +735,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { isNull(String.class), isNull(Bundle.class)); - assertFalse(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse(); verify(getServices().usageStatsManagerInternal).setActiveAdminApps( null, CALLER_USER_HANDLE); @@ -738,6 +748,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // TODO Check other internal calls. } + @Test public void testRemoveActiveAdmin_multipleAdminsInUser() { // Need MANAGE_DEVICE_ADMINS for setActiveAdmin. We'll remove it later. mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); @@ -745,14 +756,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Add admin1. dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.isAdminActive(admin1)); - assertFalse(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); + assertThat(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)).isFalse(); // Add admin2. dpm.setActiveAdmin(admin2, /* replace =*/ false); - assertTrue(dpm.isAdminActive(admin2)); - assertFalse(dpm.isRemovingAdmin(admin2, CALLER_USER_HANDLE)); + assertThat(dpm.isAdminActive(admin2)).isTrue(); + assertThat(dpm.isRemovingAdmin(admin2, CALLER_USER_HANDLE)).isFalse(); // Broadcast from saveSettingsLocked(). verify(mContext.spiedContext, times(2)).sendBroadcastAsUser( @@ -777,7 +788,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { isNull(String.class), isNull(Bundle.class)); - assertFalse(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse(); verify(getServices().usageStatsManagerInternal).setActiveAdminApps( MockUtils.checkApps(admin2.getPackageName()), eq(CALLER_USER_HANDLE)); @@ -793,6 +804,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * Test for: * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)} */ + @Test public void testForceRemoveActiveAdmin() throws Exception { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); @@ -802,7 +814,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /* appId= */ 10138, /* flags= */ ApplicationInfo.FLAG_TEST_ONLY); dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.isAdminActive(admin1)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); // Calling from a non-shell uid should fail with a SecurityException mContext.binder.callingUid = 123456; @@ -815,7 +827,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.callerPermissions.add(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); // Verify - assertFalse(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse(); verify(getServices().usageStatsManagerInternal).setActiveAdminApps( null, CALLER_USER_HANDLE); } @@ -825,6 +837,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * * Validates that when the password history length is set, it is persisted after rebooting */ + @Test public void testSaveAndLoadPasswordHistoryLength_persistedAfterReboot() throws Exception { int passwordHistoryLength = 2; @@ -842,13 +855,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Save password history length dpm.setPasswordHistoryLength(admin1, passwordHistoryLength); - assertEquals(dpm.getPasswordHistoryLength(admin1), passwordHistoryLength); + assertThat(passwordHistoryLength).isEqualTo(dpm.getPasswordHistoryLength(admin1)); initializeDpms(); reset(mContext.spiedContext); // Password history length should persist after rebooted - assertEquals(dpm.getPasswordHistoryLength(admin1), passwordHistoryLength); + assertThat(passwordHistoryLength).isEqualTo(dpm.getPasswordHistoryLength(admin1)); } /** @@ -858,6 +871,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * {@link DeviceAdminReceiver#ACTION_PASSWORD_CHANGED} is sent to managed profile owners, in * addition to ones in the original user. */ + @Test public void testSetActivePasswordState_sendToProfiles() throws Exception { mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); @@ -893,6 +907,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * intent {@link DeviceAdminReceiver#ACTION_PASSWORD_CHANGED} is only sent to the profile, not * its parent. */ + @Test public void testSetActivePasswordState_notSentToParent() throws Exception { mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); @@ -932,6 +947,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /** * Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs successfully. */ + @Test public void testSetDeviceOwner() throws Exception { setDeviceOwner(); @@ -944,7 +960,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // DO admin can't be deactivated. dpm.removeActiveAdmin(admin1); - assertTrue(dpm.isAdminActive(admin1)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); // TODO Test getDeviceOwnerName() too. To do so, we need to change // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable. @@ -969,19 +985,19 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setActiveAdmin(admin1, /* replace =*/ false); // Fire! - assertTrue(dpm.setDeviceOwner(admin1, "owner-name")); + assertThat(dpm.setDeviceOwner(admin1, "owner-name")).isTrue(); // getDeviceOwnerComponent should return the admin1 component. - assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser()); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser()); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(admin1); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); // Check various get APIs. checkGetDeviceOwnerInfoApi(dpm, /* hasDeviceOwner =*/ true); // getDeviceOwnerComponent should *NOT* return the admin1 component for other users. mContext.binder.callingUid = DpmMockContext.CALLER_UID; - assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser()); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser()); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(null); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; @@ -997,7 +1013,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED), MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser()); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); } private void checkGetDeviceOwnerInfoApi(DevicePolicyManager dpm, boolean hasDeviceOwner) { @@ -1012,89 +1028,89 @@ public class DevicePolicyManagerTest extends DpmTestBase { // TODO Test getDeviceOwnerName() too. To do so, we need to change // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable. if (hasDeviceOwner) { - assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName())); - assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser()); + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isTrue(); + assertThat(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())).isTrue(); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(admin1); - assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser()); - assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId()); + assertThat(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())).isTrue(); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); + assertThat(dpm.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); } else { - assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName())); - assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())); - assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser()); + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isFalse(); + assertThat(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())).isFalse(); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(null); - assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())); - assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser()); - assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId()); + assertThat(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())).isFalse(); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(null); + assertThat(dpm.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); } mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; if (hasDeviceOwner) { - assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName())); - assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser()); + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isTrue(); + assertThat(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())).isTrue(); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(admin1); - assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser()); - assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId()); + assertThat(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())).isTrue(); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); + assertThat(dpm.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); } else { - assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName())); - assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())); - assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser()); + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isFalse(); + assertThat(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())).isFalse(); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(null); - assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())); - assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser()); - assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId()); + assertThat(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())).isFalse(); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(null); + assertThat(dpm.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); } mContext.binder.callingUid = DpmMockContext.CALLER_UID; // Still with MANAGE_USERS. - assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName())); - assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())); - assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser()); + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isFalse(); + assertThat(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())).isFalse(); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(null); if (hasDeviceOwner) { - assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser()); - assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId()); + assertThat(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())).isTrue(); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); + assertThat(dpm.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); } else { - assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())); - assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser()); - assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId()); + assertThat(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())).isFalse(); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(null); + assertThat(dpm.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); } mContext.binder.callingUid = Process.SYSTEM_UID; mContext.callerPermissions.remove(permission.MANAGE_USERS); // System can still call "OnAnyUser" without MANAGE_USERS. if (hasDeviceOwner) { - assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName())); - assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser()); + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isTrue(); + assertThat(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())).isTrue(); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(admin1); - assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser()); - assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId()); + assertThat(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())).isTrue(); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); + assertThat(dpm.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); } else { - assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName())); - assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())); - assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser()); + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isFalse(); + assertThat(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())).isFalse(); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(null); - assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())); - assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser()); - assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId()); + assertThat(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())).isFalse(); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(null); + assertThat(dpm.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); } mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; // Still no MANAGE_USERS. if (hasDeviceOwner) { - assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName())); - assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser()); + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isTrue(); + assertThat(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())).isTrue(); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(admin1); } else { - assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName())); - assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())); - assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser()); + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isFalse(); + assertThat(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())).isFalse(); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(null); } assertExpectException(SecurityException.class, /* messageRegex =*/ null, @@ -1108,9 +1124,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_UID; // Still no MANAGE_USERS. - assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName())); - assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())); - assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser()); + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isFalse(); + assertThat(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())).isFalse(); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(null); assertExpectException(SecurityException.class, /* messageRegex =*/ null, () -> dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())); @@ -1130,6 +1146,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /** * Test for: {@link DevicePolicyManager#setDeviceOwner} Package doesn't exist. */ + @Test public void testSetDeviceOwner_noSuchPackage() { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_USERS); @@ -1144,10 +1161,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"))); } + @Test public void testSetDeviceOwner_failures() throws Exception { // TODO Test more failure cases. Basically test all chacks in enforceCanSetDeviceOwner(). } + @Test public void testClearDeviceOwner() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_USERS); @@ -1164,20 +1183,20 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.setDeviceOwner(admin1, "owner-name")); + assertThat(dpm.setDeviceOwner(admin1, "owner-name")).isTrue(); // Verify internal calls. verify(getServices().iactivityManager, times(1)).updateDeviceOwner( eq(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser()); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER); when(getServices().userManager.hasUserRestriction(eq(UserManager.DISALLOW_ADD_USER), MockUtils.checkUserHandle(UserHandle.USER_SYSTEM))).thenReturn(true); - assertTrue(dpm.isAdminActive(admin1)); - assertFalse(dpm.isRemovingAdmin(admin1, UserHandle.USER_SYSTEM)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); + assertThat(dpm.isRemovingAdmin(admin1, UserHandle.USER_SYSTEM)).isFalse(); // Set up other mocks. when(getServices().userManager.getUserRestrictions()).thenReturn(new Bundle()); @@ -1196,7 +1215,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.clearDeviceOwnerApp(admin1.getPackageName()); // Now DO shouldn't be set. - assertNull(dpm.getDeviceOwnerComponentOnAnyUser()); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isNull(); verify(getServices().userManager).setUserRestriction(eq(UserManager.DISALLOW_ADD_USER), eq(false), @@ -1209,7 +1228,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().usageStatsManagerInternal).setActiveAdminApps( null, UserHandle.USER_SYSTEM); - assertFalse(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM)); + assertThat(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM)).isFalse(); // ACTION_DEVICE_OWNER_CHANGED should be sent twice, once for setting the device owner // and once for clearing it. @@ -1219,6 +1238,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // TODO Check other calls. } + @Test public void testDeviceOwnerBackupActivateDeactivate() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -1227,7 +1247,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.setDeviceOwner(admin1, "owner-name")); + assertThat(dpm.setDeviceOwner(admin1, "owner-name")).isTrue(); verify(getServices().ibackupManager, times(1)).setBackupServiceActive( eq(UserHandle.USER_SYSTEM), eq(false)); @@ -1238,6 +1258,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(UserHandle.USER_SYSTEM), eq(true)); } + @Test public void testProfileOwnerBackupActivateDeactivate() throws Exception { setAsProfileOwner(admin1); @@ -1250,6 +1271,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(CALLER_USER_HANDLE), eq(true)); } + @Test public void testClearDeviceOwner_fromDifferentUser() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_USERS); @@ -1266,13 +1288,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.setDeviceOwner(admin1, "owner-name")); + assertThat(dpm.setDeviceOwner(admin1, "owner-name")).isTrue(); // Verify internal calls. verify(getServices().iactivityManager, times(1)).updateDeviceOwner( eq(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser()); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); // Now call clear from the secondary user, which should throw. mContext.binder.callingUid = DpmMockContext.CALLER_UID; @@ -1286,7 +1308,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.clearDeviceOwnerApp(admin1.getPackageName())); // DO shouldn't be removed. - assertTrue(dpm.isDeviceManaged()); + assertThat(dpm.isDeviceManaged()).isTrue(); } /** @@ -1294,6 +1316,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * * Validates that when the device owner is removed, the reset password token is cleared */ + @Test public void testClearDeviceOwner_clearResetPasswordToken() throws Exception { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -1312,13 +1335,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().lockPatternUtils.addEscrowToken(eq(token), eq(UserHandle.USER_SYSTEM), nullable(EscrowTokenStateChangeCallback.class))) .thenReturn(handle); - assertTrue(dpm.setResetPasswordToken(admin1, token)); + assertThat(dpm.setResetPasswordToken(admin1, token)).isTrue(); // Assert reset password token is active when(getServices().lockPatternUtils.isEscrowTokenActive(eq(handle), eq(UserHandle.USER_SYSTEM))) .thenReturn(true); - assertTrue(dpm.isResetPasswordTokenActive(admin1)); + assertThat(dpm.isResetPasswordTokenActive(admin1)).isTrue(); // Remove the device owner dpm.clearDeviceOwnerApp(admin1.getPackageName()); @@ -1328,12 +1351,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(UserHandle.USER_SYSTEM)); } + @Test public void testSetProfileOwner() throws Exception { setAsProfileOwner(admin1); // PO admin can't be deactivated. dpm.removeActiveAdmin(admin1); - assertTrue(dpm.isAdminActive(admin1)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); // Try setting DO on the same user, which should fail. setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID); @@ -1347,13 +1371,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { }); } + @Test public void testClearProfileOwner() throws Exception { setAsProfileOwner(admin1); mContext.binder.callingUid = DpmMockContext.CALLER_UID; - assertTrue(dpm.isProfileOwnerApp(admin1.getPackageName())); - assertFalse(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isProfileOwnerApp(admin1.getPackageName())).isTrue(); + assertThat(dpm.isRemovingAdmin(admin1, CALLER_USER_HANDLE)).isFalse(); // First try when the user is locked, which should fail. when(getServices().userManager.isUserUnlocked(anyInt())) @@ -1367,16 +1392,18 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.clearProfileOwner(admin1); // Check - assertFalse(dpm.isProfileOwnerApp(admin1.getPackageName())); - assertFalse(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.isProfileOwnerApp(admin1.getPackageName())).isFalse(); + assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse(); verify(getServices().usageStatsManagerInternal).setActiveAdminApps( null, CALLER_USER_HANDLE); } + @Test public void testSetProfileOwner_failures() throws Exception { // TODO Test more failure cases. Basically test all chacks in enforceCanSetProfileOwner(). } + @Test public void testGetDeviceOwnerAdminLocked() throws Exception { checkDeviceOwnerWithMultipleDeviceAdmins(); } @@ -1421,13 +1448,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set DO on the first non-system user. getServices().setUserRunning(CALLER_USER_HANDLE, true); - assertTrue(dpm.setDeviceOwner(admin2, "owner-name", CALLER_USER_HANDLE)); + assertThat(dpm.setDeviceOwner(admin2, "owner-name", CALLER_USER_HANDLE)).isTrue(); - assertEquals(admin2, dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false)); + assertThat(dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false)).isEqualTo(admin2); // Then check getDeviceOwnerAdminLocked(). - assertEquals(admin2, getDeviceOwner().info.getComponent()); - assertEquals(DpmMockContext.CALLER_UID, getDeviceOwner().getUid()); + assertThat(getDeviceOwner().info.getComponent()).isEqualTo(admin2); + assertThat(getDeviceOwner().getUid()).isEqualTo(DpmMockContext.CALLER_UID); } /** @@ -1438,6 +1465,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * We didn't use to persist the DO component class name, but now we do, and the above method * finds the right component from a package name upon migration. */ + @Test public void testDeviceOwnerMigration() throws Exception { when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true); checkDeviceOwnerWithMultipleDeviceAdmins(); @@ -1449,7 +1477,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpms.mOwners.writeDeviceOwner(); // Make sure the DO component name doesn't have a class name. - assertEquals("", dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false).getClassName()); + assertThat(dpms.getDeviceOwnerComponent(/* callingUserOnly= */ false).getClassName()) + .isEmpty(); // Then create a new DPMS to have it load the settings from files. when(getServices().userManager.getUserRestrictions(any(UserHandle.class))) @@ -1459,9 +1488,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Now the DO component name is a full name. // *BUT* because both admin1 and admin2 belong to the same package, we think admin1 is the // DO. - assertEquals(admin1, dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false)); + assertThat(dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false)).isEqualTo(admin1); } + @Test public void testSetGetApplicationRestriction() { setAsProfileOwner(admin1); mContext.packageName = admin1.getPackageName(); @@ -1480,20 +1510,20 @@ public class DevicePolicyManagerTest extends DpmTestBase { { Bundle returned = dpm.getApplicationRestrictions(admin1, "pkg1"); - assertNotNull(returned); - assertEquals(returned.size(), 1); - assertEquals(returned.get("KEY_STRING"), "Foo1"); + assertThat(returned).isNotNull(); + assertThat(returned.size()).isEqualTo(1); + assertThat("Foo1").isEqualTo(returned.get("KEY_STRING")); } { Bundle returned = dpm.getApplicationRestrictions(admin1, "pkg2"); - assertNotNull(returned); - assertEquals(returned.size(), 1); - assertEquals(returned.get("KEY_STRING"), "Foo2"); + assertThat(returned).isNotNull(); + assertThat(returned.size()).isEqualTo(1); + assertThat("Foo2").isEqualTo(returned.get("KEY_STRING")); } dpm.setApplicationRestrictions(admin1, "pkg2", new Bundle()); - assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size()); + assertThat(dpm.getApplicationRestrictions(admin1, "pkg2").size()).isEqualTo(0); } /** @@ -1546,6 +1576,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { return uid; } + @Test public void testCertificateDisclosure() throws Exception { final int userId = CALLER_USER_HANDLE; final UserHandle user = UserHandle.of(userId); @@ -1571,7 +1602,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { .cancelAsUser(anyString(), anyInt(), eq(user)); // Given that we have four certificates installed, - when(getServices().keyChainConnection.getService().getUserCaAliases()).thenReturn(fourCerts); + when(getServices().keyChainConnection.getService().getUserCaAliases()) + .thenReturn(fourCerts); // when two of them are approved (one of them approved twice hence no action), dpms.approveCaCert(fourCerts.getList().get(0), userId, true); dpms.approveCaCert(fourCerts.getList().get(1), userId, true); @@ -1586,6 +1618,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * Simple test for delegate set/get and general delegation. Tests verifying that delegated * privileges can acually be exercised by a delegate are not covered here. */ + @Test public void testDelegation() throws Exception { setAsProfileOwner(admin1); @@ -1606,17 +1639,18 @@ public class DevicePolicyManagerTest extends DpmTestBase { // DPMS correctly stores and retrieves the delegates DevicePolicyData policy = dpms.mUserData.get(userHandle); - assertEquals(2, policy.mDelegationMap.size()); + assertThat(policy.mDelegationMap.size()).isEqualTo(2); MoreAsserts.assertContentsInAnyOrder(policy.mDelegationMap.get(CERT_DELEGATE), DELEGATION_CERT_INSTALL); MoreAsserts.assertContentsInAnyOrder(dpm.getDelegatedScopes(admin1, CERT_DELEGATE), DELEGATION_CERT_INSTALL); - assertEquals(CERT_DELEGATE, dpm.getCertInstallerPackage(admin1)); + assertThat(dpm.getCertInstallerPackage(admin1)).isEqualTo(CERT_DELEGATE); MoreAsserts.assertContentsInAnyOrder(policy.mDelegationMap.get(RESTRICTIONS_DELEGATE), DELEGATION_APP_RESTRICTIONS); MoreAsserts.assertContentsInAnyOrder(dpm.getDelegatedScopes(admin1, RESTRICTIONS_DELEGATE), DELEGATION_APP_RESTRICTIONS); - assertEquals(RESTRICTIONS_DELEGATE, dpm.getApplicationRestrictionsManagingPackage(admin1)); + assertThat(dpm.getApplicationRestrictionsManagingPackage(admin1)) + .isEqualTo(RESTRICTIONS_DELEGATE); // On calling install certificate APIs from an unauthorized process mContext.binder.callingUid = RESTRICTIONS_DELEGATE_UID; @@ -1658,6 +1692,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } } + @Test public void testApplicationRestrictionsManagingApp() throws Exception { setAsProfileOwner(admin1); @@ -1673,7 +1708,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // delegated that permission yet. mContext.binder.callingUid = DpmMockContext.CALLER_UID; mContext.packageName = admin1.getPackageName(); - assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage()); + assertThat(dpm.isCallerApplicationRestrictionsManagingPackage()).isFalse(); final Bundle rest = new Bundle(); rest.putString("KEY_STRING", "Foo1"); assertExpectException(SecurityException.class, INVALID_CALLING_IDENTITY_MSG, @@ -1682,7 +1717,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Check via the profile owner that no restrictions were set. mContext.binder.callingUid = DpmMockContext.CALLER_UID; mContext.packageName = admin1.getPackageName(); - assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size()); + assertThat(dpm.getApplicationRestrictions(admin1, "pkg1").size()).isEqualTo(0); // Check the API does not allow setting a non-existent package assertExpectException(PackageManager.NameNotFoundException.class, @@ -1692,22 +1727,22 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Let appRestrictionsManagerPackage manage app restrictions dpm.setApplicationRestrictionsManagingPackage(admin1, appRestrictionsManagerPackage); - assertEquals(appRestrictionsManagerPackage, - dpm.getApplicationRestrictionsManagingPackage(admin1)); + assertThat(dpm.getApplicationRestrictionsManagingPackage(admin1)) + .isEqualTo(appRestrictionsManagerPackage); // Now that package should be able to set and retrieve app restrictions. mContext.binder.callingUid = appRestrictionsManagerUid; mContext.packageName = appRestrictionsManagerPackage; - assertTrue(dpm.isCallerApplicationRestrictionsManagingPackage()); + assertThat(dpm.isCallerApplicationRestrictionsManagingPackage()).isTrue(); dpm.setApplicationRestrictions(null, "pkg1", rest); Bundle returned = dpm.getApplicationRestrictions(null, "pkg1"); - assertEquals(1, returned.size(), 1); - assertEquals("Foo1", returned.get("KEY_STRING")); + assertThat(returned.size()).isEqualTo(1); + assertThat(returned.get("KEY_STRING")).isEqualTo("Foo1"); // The same app running on a separate user shouldn't be able to manage app restrictions. mContext.binder.callingUid = UserHandle.getUid( UserHandle.USER_SYSTEM, appRestrictionsManagerAppId); - assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage()); + assertThat(dpm.isCallerApplicationRestrictionsManagingPackage()).isFalse(); assertExpectException(SecurityException.class, nonDelegateExceptionMessageRegex, () -> dpm.setApplicationRestrictions(null, "pkg1", rest)); @@ -1715,20 +1750,21 @@ public class DevicePolicyManagerTest extends DpmTestBase { // too. mContext.binder.callingUid = DpmMockContext.CALLER_UID; mContext.packageName = admin1.getPackageName(); - assertEquals(returned, dpm.getApplicationRestrictions(admin1, "pkg1")); + assertThat(dpm.getApplicationRestrictions(admin1, "pkg1")).isEqualTo(returned); dpm.setApplicationRestrictions(admin1, "pkg1", null); - assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size()); + assertThat(dpm.getApplicationRestrictions(admin1, "pkg1").size()).isEqualTo(0); // Removing the ability for the package to manage app restrictions. dpm.setApplicationRestrictionsManagingPackage(admin1, null); - assertNull(dpm.getApplicationRestrictionsManagingPackage(admin1)); + assertThat(dpm.getApplicationRestrictionsManagingPackage(admin1)).isNull(); mContext.binder.callingUid = appRestrictionsManagerUid; mContext.packageName = appRestrictionsManagerPackage; - assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage()); + assertThat(dpm.isCallerApplicationRestrictionsManagingPackage()).isFalse(); assertExpectException(SecurityException.class, INVALID_CALLING_IDENTITY_MSG, () -> dpm.setApplicationRestrictions(null, "pkg1", null)); } + @Test public void testSetUserRestriction_asDo() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_USERS); @@ -1745,8 +1781,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Call. dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM); - assertTrue(dpm.setDeviceOwner(admin1, "owner-name", - UserHandle.USER_SYSTEM)); + assertThat(dpm.setDeviceOwner(admin1, "owner-name", + UserHandle.USER_SYSTEM)).isTrue(); assertNoDeviceOwnerRestrictions(); reset(getServices().userManagerInternal); @@ -1859,6 +1895,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { return null; } + @Test public void testDaDisallowedPolicies_SecurityException() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL); @@ -1869,25 +1906,28 @@ public class DevicePolicyManagerTest extends DpmTestBase { boolean originalCameraDisabled = dpm.getCameraDisabled(admin1); assertExpectException(SecurityException.class, /* messageRegex= */ null, () -> dpm.setCameraDisabled(admin1, true)); - assertEquals(originalCameraDisabled, dpm.getCameraDisabled(admin1)); + assertThat(dpm.getCameraDisabled(admin1)).isEqualTo(originalCameraDisabled); int originalKeyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(admin1); assertExpectException(SecurityException.class, /* messageRegex= */ null, () -> dpm.setKeyguardDisabledFeatures(admin1, DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)); - assertEquals(originalKeyguardDisabledFeatures, dpm.getKeyguardDisabledFeatures(admin1)); + assertThat(dpm.getKeyguardDisabledFeatures(admin1)) + .isEqualTo(originalKeyguardDisabledFeatures); long originalPasswordExpirationTimeout = dpm.getPasswordExpirationTimeout(admin1); assertExpectException(SecurityException.class, /* messageRegex= */ null, () -> dpm.setPasswordExpirationTimeout(admin1, 1234)); - assertEquals(originalPasswordExpirationTimeout, dpm.getPasswordExpirationTimeout(admin1)); + assertThat(dpm.getPasswordExpirationTimeout(admin1)) + .isEqualTo(originalPasswordExpirationTimeout); int originalPasswordQuality = dpm.getPasswordQuality(admin1); assertExpectException(SecurityException.class, /* messageRegex= */ null, () -> dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC)); - assertEquals(originalPasswordQuality, dpm.getPasswordQuality(admin1)); + assertThat(dpm.getPasswordQuality(admin1)).isEqualTo(originalPasswordQuality); } + @Test public void testSetUserRestriction_asPo() { setAsProfileOwner(admin1); @@ -2023,6 +2063,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { UserManager.DISALLOW_UNMUTE_MICROPHONE ); + @Test public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception { final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID); @@ -2097,6 +2138,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { ); } + @Test public void testNoDefaultEnabledUserRestrictions() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_USERS); @@ -2112,8 +2154,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM); - assertTrue(dpm.setDeviceOwner(admin1, "owner-name", - UserHandle.USER_SYSTEM)); + assertThat(dpm.setDeviceOwner(admin1, "owner-name", + UserHandle.USER_SYSTEM)).isTrue(); assertNoDeviceOwnerRestrictions(); @@ -2132,6 +2174,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { ); } + @Test public void testSetFactoryResetProtectionPolicyWithDO() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -2156,6 +2199,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION)); } + @Test public void testSetFactoryResetProtectionPolicyFailWithPO() throws Exception { setupProfileOwner(); @@ -2167,6 +2211,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.setFactoryResetProtectionPolicy(admin1, policy)); } + @Test public void testSetFactoryResetProtectionPolicyWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); @@ -2204,6 +2249,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION)); } + @Test public void testGetFactoryResetProtectionPolicyWithFrpManagementAgent() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; @@ -2245,6 +2291,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts); } + @Test public void testSetKeyguardDisabledFeaturesWithDO() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -2255,6 +2302,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA); } + @Test public void testSetKeyguardDisabledFeaturesWithPO() throws Exception { setupProfileOwner(); @@ -2264,6 +2312,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT); } + @Test public void testSetKeyguardDisabledFeaturesWithPOOfOrganizationOwnedDevice() throws Exception { final int MANAGED_PROFILE_USER_ID = CALLER_USER_HANDLE; @@ -2281,6 +2330,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA); } + @Test public void testSetApplicationHiddenWithDO() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -2304,6 +2354,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { UserHandle.USER_SYSTEM); } + @Test public void testSetApplicationHiddenWithPOOfOrganizationOwnedDevice() throws Exception { final int MANAGED_PROFILE_USER_ID = CALLER_USER_HANDLE; final int MANAGED_PROFILE_ADMIN_UID = @@ -2334,6 +2385,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { false, UserHandle.USER_SYSTEM); } + @Test public void testGetMacAddress() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -2351,7 +2403,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // DO needs to be an DA. dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.isAdminActive(admin1)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); // Test 2. Caller has DA, but not DO. assertExpectException(SecurityException.class, @@ -2359,7 +2411,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.getWifiMacAddress(admin1)); // Test 3. Caller has PO, but not DO. - assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM)); + assertThat(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue(); assertExpectException(SecurityException.class, /* messageRegex= */ INVALID_CALLING_IDENTITY_MSG, () -> dpm.getWifiMacAddress(admin1)); @@ -2368,30 +2420,32 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.clearProfileOwner(admin1); dpm.setActiveAdmin(admin1, false); // Test 4, Caller is DO now. - assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)); + assertThat(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue(); // 4-1. But WifiManager is not ready. - assertNull(dpm.getWifiMacAddress(admin1)); + assertThat(dpm.getWifiMacAddress(admin1)).isNull(); // 4-2. When WifiManager returns an empty array, dpm should also output null. when(getServices().wifiManager.getFactoryMacAddresses()).thenReturn(new String[0]); - assertNull(dpm.getWifiMacAddress(admin1)); + assertThat(dpm.getWifiMacAddress(admin1)).isNull(); // 4-3. With a real MAC address. final String[] macAddresses = new String[]{"11:22:33:44:55:66"}; when(getServices().wifiManager.getFactoryMacAddresses()).thenReturn(macAddresses); - assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress(admin1)); + assertThat(dpm.getWifiMacAddress(admin1)).isEqualTo("11:22:33:44:55:66"); } + @Test public void testGetMacAddressByOrgOwnedPO() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); final String[] macAddresses = new String[]{"11:22:33:44:55:66"}; when(getServices().wifiManager.getFactoryMacAddresses()).thenReturn(macAddresses); - assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress(admin1)); + assertThat(dpm.getWifiMacAddress(admin1)).isEqualTo("11:22:33:44:55:66"); } + @Test public void testReboot() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -2404,19 +2458,19 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set admin1 as DA. dpm.setActiveAdmin(admin1, false); - assertTrue(dpm.isAdminActive(admin1)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); assertExpectException(SecurityException.class, /* messageRegex= */ INVALID_CALLING_IDENTITY_MSG, () -> dpm.reboot(admin1)); // Set admin1 as PO. - assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM)); + assertThat(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue(); assertExpectException(SecurityException.class, /* messageRegex= */ INVALID_CALLING_IDENTITY_MSG, () -> dpm.reboot(admin1)); // Remove PO and add DO. dpm.clearProfileOwner(admin1); dpm.setActiveAdmin(admin1, false); - assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)); + assertThat(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue(); // admin1 is DO. // Set current call state of device to ringing. @@ -2432,10 +2486,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.reboot(admin1)); // Set current call state of device to idle. - when(getServices().telephonyManager.getCallState()).thenReturn(TelephonyManager.CALL_STATE_IDLE); + when(getServices().telephonyManager.getCallState()) + .thenReturn(TelephonyManager.CALL_STATE_IDLE); dpm.reboot(admin1); } + @Test public void testSetGetSupportText() { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); dpm.setActiveAdmin(admin1, true); @@ -2444,11 +2500,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Null default support messages. { - assertNull(dpm.getLongSupportMessage(admin1)); - assertNull(dpm.getShortSupportMessage(admin1)); + assertThat(dpm.getLongSupportMessage(admin1)).isNull(); + assertThat(dpm.getShortSupportMessage(admin1)).isNull(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertNull(dpm.getShortSupportMessageForUser(admin1, CALLER_USER_HANDLE)); - assertNull(dpm.getLongSupportMessageForUser(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.getShortSupportMessageForUser(admin1, CALLER_USER_HANDLE)).isNull(); + assertThat(dpm.getLongSupportMessageForUser(admin1, CALLER_USER_HANDLE)).isNull(); mMockContext.binder.callingUid = DpmMockContext.CALLER_UID; } @@ -2473,46 +2529,46 @@ public class DevicePolicyManagerTest extends DpmTestBase { { final String supportText = "Some text to test with."; dpm.setShortSupportMessage(admin1, supportText); - assertEquals(supportText, dpm.getShortSupportMessage(admin1)); - assertNull(dpm.getLongSupportMessage(admin1)); - assertNull(dpm.getShortSupportMessage(admin2)); + assertThat(dpm.getShortSupportMessage(admin1)).isEqualTo(supportText); + assertThat(dpm.getLongSupportMessage(admin1)).isNull(); + assertThat(dpm.getShortSupportMessage(admin2)).isNull(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertEquals(supportText, dpm.getShortSupportMessageForUser(admin1, - CALLER_USER_HANDLE)); - assertNull(dpm.getShortSupportMessageForUser(admin2, CALLER_USER_HANDLE)); - assertNull(dpm.getLongSupportMessageForUser(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.getShortSupportMessageForUser(admin1, + CALLER_USER_HANDLE)).isEqualTo(supportText); + assertThat(dpm.getShortSupportMessageForUser(admin2, CALLER_USER_HANDLE)).isNull(); + assertThat(dpm.getLongSupportMessageForUser(admin1, CALLER_USER_HANDLE)).isNull(); mMockContext.binder.callingUid = DpmMockContext.CALLER_UID; dpm.setShortSupportMessage(admin1, null); - assertNull(dpm.getShortSupportMessage(admin1)); + assertThat(dpm.getShortSupportMessage(admin1)).isNull(); } // Set/Get long returns what it sets and other admins text isn't changed. { final String supportText = "Some text to test with.\nWith more text."; dpm.setLongSupportMessage(admin1, supportText); - assertEquals(supportText, dpm.getLongSupportMessage(admin1)); - assertNull(dpm.getShortSupportMessage(admin1)); - assertNull(dpm.getLongSupportMessage(admin2)); + assertThat(dpm.getLongSupportMessage(admin1)).isEqualTo(supportText); + assertThat(dpm.getShortSupportMessage(admin1)).isNull(); + assertThat(dpm.getLongSupportMessage(admin2)).isNull(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertEquals(supportText, dpm.getLongSupportMessageForUser(admin1, - CALLER_USER_HANDLE)); - assertNull(dpm.getLongSupportMessageForUser(admin2, CALLER_USER_HANDLE)); - assertNull(dpm.getShortSupportMessageForUser(admin1, CALLER_USER_HANDLE)); + assertThat(dpm.getLongSupportMessageForUser(admin1, + CALLER_USER_HANDLE)).isEqualTo(supportText); + assertThat(dpm.getLongSupportMessageForUser(admin2, CALLER_USER_HANDLE)).isNull(); + assertThat(dpm.getShortSupportMessageForUser(admin1, CALLER_USER_HANDLE)).isNull(); mMockContext.binder.callingUid = DpmMockContext.CALLER_UID; dpm.setLongSupportMessage(admin1, null); - assertNull(dpm.getLongSupportMessage(admin1)); + assertThat(dpm.getLongSupportMessage(admin1)).isNull(); } } + @Test public void testSetGetMeteredDataDisabledPackages() throws Exception { setAsProfileOwner(admin1); - final ArrayList<String> emptyList = new ArrayList<>(); - assertEquals(emptyList, dpm.getMeteredDataDisabledPackages(admin1)); + assertThat(dpm.getMeteredDataDisabledPackages(admin1)).isEmpty(); // Setup final ArrayList<String> pkgsToRestrict = new ArrayList<>(); @@ -2525,8 +2581,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { List<String> excludedPkgs = dpm.setMeteredDataDisabledPackages(admin1, pkgsToRestrict); // Verify - assertEquals(emptyList, excludedPkgs); - assertEquals(pkgsToRestrict, dpm.getMeteredDataDisabledPackages(admin1)); + assertThat(excludedPkgs).isEmpty(); + assertThat(dpm.getMeteredDataDisabledPackages(admin1)).isEqualTo(pkgsToRestrict); verify(getServices().networkPolicyManagerInternal).setMeteredRestrictedPackages( MockUtils.checkApps(pkgsToRestrict.toArray(new String[0])), eq(CALLER_USER_HANDLE)); @@ -2536,17 +2592,18 @@ public class DevicePolicyManagerTest extends DpmTestBase { excludedPkgs = dpm.setMeteredDataDisabledPackages(admin1, pkgsToRestrict); // Verify - assertEquals(emptyList, excludedPkgs); - assertEquals(pkgsToRestrict, dpm.getMeteredDataDisabledPackages(admin1)); + assertThat(excludedPkgs).isEmpty(); + assertThat(dpm.getMeteredDataDisabledPackages(admin1)).isEqualTo(pkgsToRestrict); verify(getServices().networkPolicyManagerInternal).setMeteredRestrictedPackages( MockUtils.checkApps(pkgsToRestrict.toArray(new String[0])), eq(CALLER_USER_HANDLE)); } + @Test public void testSetGetMeteredDataDisabledPackages_deviceAdmin() { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); dpm.setActiveAdmin(admin1, true); - assertTrue(dpm.isAdminActive(admin1)); + assertThat(dpm.isAdminActive(admin1)).isTrue(); mContext.callerPermissions.remove(permission.MANAGE_DEVICE_ADMINS); assertExpectException(SecurityException.class, /* messageRegex= */ NOT_PROFILE_OWNER_MSG, @@ -2555,11 +2612,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.getMeteredDataDisabledPackages(admin1)); } + @Test public void testIsMeteredDataDisabledForUserPackage() throws Exception { setAsProfileOwner(admin1); // Setup - final ArrayList<String> emptyList = new ArrayList<>(); final ArrayList<String> pkgsToRestrict = new ArrayList<>(); final String package1 = "com.example.one"; final String package2 = "com.example.two"; @@ -2571,16 +2628,20 @@ public class DevicePolicyManagerTest extends DpmTestBase { List<String> excludedPkgs = dpm.setMeteredDataDisabledPackages(admin1, pkgsToRestrict); // Verify - assertEquals(emptyList, excludedPkgs); + assertThat(excludedPkgs).isEmpty(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertTrue(package1 + "should be restricted", - dpm.isMeteredDataDisabledPackageForUser(admin1, package1, CALLER_USER_HANDLE)); - assertTrue(package2 + "should be restricted", - dpm.isMeteredDataDisabledPackageForUser(admin1, package2, CALLER_USER_HANDLE)); - assertFalse(package3 + "should not be restricted", - dpm.isMeteredDataDisabledPackageForUser(admin1, package3, CALLER_USER_HANDLE)); - } - + assertWithMessage("%s should be restricted", package1) + .that(dpm.isMeteredDataDisabledPackageForUser(admin1, package1, CALLER_USER_HANDLE)) + .isTrue(); + assertWithMessage("%s should be restricted", package2) + .that(dpm.isMeteredDataDisabledPackageForUser(admin1, package2, CALLER_USER_HANDLE)) + .isTrue(); + assertWithMessage("%s should not be restricted", package3) + .that(dpm.isMeteredDataDisabledPackageForUser(admin1, package3, CALLER_USER_HANDLE)) + .isFalse(); + } + + @Test public void testIsMeteredDataDisabledForUserPackage_nonSystemUidCaller() throws Exception { setAsProfileOwner(admin1); assertExpectException(SecurityException.class, @@ -2597,6 +2658,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { clearDeviceOwner(); } + @Test public void testCreateAdminSupportIntent() throws Exception { // Setup device owner. mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; @@ -2604,11 +2666,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Nonexisting permission returns null Intent intent = dpm.createAdminSupportIntent("disallow_nothing"); - assertNull(intent); + assertThat(intent).isNull(); // Existing permission that is not set returns null intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME); - assertNull(intent); + assertThat(intent).isNull(); // Existing permission that is not set by device/profile owner returns null when(getServices().userManager.hasUserRestriction( @@ -2616,7 +2678,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid)))) .thenReturn(true); intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME); - assertNull(intent); + assertThat(intent).isNull(); // UM.getUserRestrictionSources() will return a list of size 1 with the caller resource. doAnswer((Answer<List<UserManager.EnforcingUser>>) invocation -> Collections.singletonList( @@ -2626,51 +2688,53 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(UserManager.DISALLOW_ADJUST_VOLUME), eq(UserHandle.of(UserHandle.myUserId()))); intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME); - assertNotNull(intent); - assertEquals(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS, intent.getAction()); - assertEquals(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID), - intent.getIntExtra(Intent.EXTRA_USER_ID, -1)); - assertEquals(admin1, intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN)); - assertEquals(UserManager.DISALLOW_ADJUST_VOLUME, - intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION)); + assertThat(intent).isNotNull(); + assertThat(intent.getAction()).isEqualTo(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS); + assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, -1)) + .isEqualTo(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID)); + assertThat( + (ComponentName) intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN)) + .isEqualTo(admin1); + assertThat(intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION)) + .isEqualTo(UserManager.DISALLOW_ADJUST_VOLUME); // Try with POLICY_DISABLE_CAMERA and POLICY_DISABLE_SCREEN_CAPTURE, which are not // user restrictions // Camera is not disabled intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA); - assertNull(intent); + assertThat(intent).isNull(); // Camera is disabled dpm.setCameraDisabled(admin1, true); intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA); - assertNotNull(intent); - assertEquals(DevicePolicyManager.POLICY_DISABLE_CAMERA, - intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION)); + assertThat(intent).isNotNull(); + assertThat(intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION)) + .isEqualTo(DevicePolicyManager.POLICY_DISABLE_CAMERA); // Screen capture is not disabled intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE); - assertNull(intent); + assertThat(intent).isNull(); // Screen capture is disabled dpm.setScreenCaptureDisabled(admin1, true); intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE); - assertNotNull(intent); - assertEquals(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE, - intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION)); + assertThat(intent).isNotNull(); + assertThat(intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION)) + .isEqualTo(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE); // Same checks for different user mContext.binder.callingUid = DpmMockContext.CALLER_UID; // Camera should be disabled by device owner intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA); - assertNotNull(intent); - assertEquals(DevicePolicyManager.POLICY_DISABLE_CAMERA, - intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION)); - assertEquals(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID), - intent.getIntExtra(Intent.EXTRA_USER_ID, -1)); + assertThat(intent).isNotNull(); + assertThat(intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION)) + .isEqualTo(DevicePolicyManager.POLICY_DISABLE_CAMERA); + assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, -1)) + .isEqualTo(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID)); // ScreenCapture should not be disabled by device owner intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE); - assertNull(intent); + assertThat(intent).isNull(); } /** @@ -2679,6 +2743,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * {@link DevicePolicyManager#getAffiliationIds} * {@link DevicePolicyManager#isAffiliatedUser} */ + @Test public void testUserAffiliation() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -2686,20 +2751,20 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Check that the system user is unaffiliated. mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; - assertFalse(dpm.isAffiliatedUser()); + assertThat(dpm.isAffiliatedUser()).isFalse(); // Set a device owner on the system user. Check that the system user becomes affiliated. setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); dpm.setActiveAdmin(admin1, /* replace =*/ false); - assertTrue(dpm.setDeviceOwner(admin1, "owner-name")); - assertTrue(dpm.isAffiliatedUser()); - assertTrue(dpm.getAffiliationIds(admin1).isEmpty()); + assertThat(dpm.setDeviceOwner(admin1, "owner-name")).isTrue(); + assertThat(dpm.isAffiliatedUser()).isTrue(); + assertThat(dpm.getAffiliationIds(admin1).isEmpty()).isTrue(); // Install a profile owner. Check that the test user is unaffiliated. mContext.binder.callingUid = DpmMockContext.CALLER_UID; setAsProfileOwner(admin2); - assertFalse(dpm.isAffiliatedUser()); - assertTrue(dpm.getAffiliationIds(admin2).isEmpty()); + assertThat(dpm.isAffiliatedUser()).isFalse(); + assertThat(dpm.getAffiliationIds(admin2).isEmpty()).isTrue(); // Have the profile owner specify a set of affiliation ids. Check that the test user remains // unaffiliated. @@ -2709,7 +2774,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { userAffiliationIds.add("blue"); dpm.setAffiliationIds(admin2, userAffiliationIds); MoreAsserts.assertContentsInAnyOrder(dpm.getAffiliationIds(admin2), "red", "green", "blue"); - assertFalse(dpm.isAffiliatedUser()); + assertThat(dpm.isAffiliatedUser()).isFalse(); // Have the device owner specify a set of affiliation ids that do not intersect with those // specified by the profile owner. Check that the test user remains unaffiliated. @@ -2722,7 +2787,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { MoreAsserts.assertContentsInAnyOrder( dpm.getAffiliationIds(admin1), "cyan", "yellow", "magenta"); mContext.binder.callingUid = DpmMockContext.CALLER_UID; - assertFalse(dpm.isAffiliatedUser()); + assertThat(dpm.isAffiliatedUser()).isFalse(); // Have the profile owner specify a set of affiliation ids that intersect with those // specified by the device owner. Check that the test user becomes affiliated. @@ -2730,33 +2795,36 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setAffiliationIds(admin2, userAffiliationIds); MoreAsserts.assertContentsInAnyOrder( dpm.getAffiliationIds(admin2), "red", "green", "blue", "yellow"); - assertTrue(dpm.isAffiliatedUser()); + assertThat(dpm.isAffiliatedUser()).isTrue(); // Clear affiliation ids for the profile owner. The user becomes unaffiliated. dpm.setAffiliationIds(admin2, Collections.emptySet()); - assertTrue(dpm.getAffiliationIds(admin2).isEmpty()); - assertFalse(dpm.isAffiliatedUser()); + assertThat(dpm.getAffiliationIds(admin2).isEmpty()).isTrue(); + assertThat(dpm.isAffiliatedUser()).isFalse(); // Set affiliation ids again, then clear PO to check that the user becomes unaffiliated dpm.setAffiliationIds(admin2, userAffiliationIds); - assertTrue(dpm.isAffiliatedUser()); + assertThat(dpm.isAffiliatedUser()).isTrue(); dpm.clearProfileOwner(admin2); - assertFalse(dpm.isAffiliatedUser()); + assertThat(dpm.isAffiliatedUser()).isFalse(); // Check that the system user remains affiliated. mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; - assertTrue(dpm.isAffiliatedUser()); + assertThat(dpm.isAffiliatedUser()).isTrue(); // Clear the device owner - the user becomes unaffiliated. clearDeviceOwner(); - assertFalse(dpm.isAffiliatedUser()); + assertThat(dpm.isAffiliatedUser()).isFalse(); } + @Test public void testGetUserProvisioningState_defaultResult() { mContext.callerPermissions.add(permission.MANAGE_USERS); - assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState()); + assertThat(dpm.getUserProvisioningState()) + .isEqualTo(DevicePolicyManager.STATE_USER_UNMANAGED); } + @Test public void testSetUserProvisioningState_permission() throws Exception { setupProfileOwner(); @@ -2764,6 +2832,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.STATE_USER_SETUP_FINALIZED); } + @Test public void testSetUserProvisioningState_unprivileged() throws Exception { setupProfileOwner(); assertExpectException(SecurityException.class, /* messageRegex =*/ null, @@ -2771,6 +2840,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { CALLER_USER_HANDLE)); } + @Test public void testSetUserProvisioningState_noManagement() { mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); mContext.callerPermissions.add(permission.MANAGE_USERS); @@ -2778,9 +2848,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { /* messageRegex= */ "change provisioning state unless a .* owner is set", () -> dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED, CALLER_USER_HANDLE)); - assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState()); + assertThat(dpm.getUserProvisioningState()) + .isEqualTo(DevicePolicyManager.STATE_USER_UNMANAGED); } + @Test public void testSetUserProvisioningState_deviceOwnerFromSetupWizard() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -2790,6 +2862,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.STATE_USER_SETUP_FINALIZED); } + @Test public void testSetUserProvisioningState_deviceOwnerFromSetupWizardAlternative() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; @@ -2800,6 +2873,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.STATE_USER_SETUP_FINALIZED); } + @Test public void testSetUserProvisioningState_deviceOwnerWithoutSetupWizard() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -2808,6 +2882,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.STATE_USER_SETUP_FINALIZED); } + @Test public void testSetUserProvisioningState_managedProfileFromSetupWizard_primaryUser() throws Exception { setupProfileOwner(); @@ -2817,6 +2892,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.STATE_USER_PROFILE_FINALIZED); } + @Test public void testSetUserProvisioningState_managedProfileFromSetupWizard_managedProfile() throws Exception { setupProfileOwner(); @@ -2826,6 +2902,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.STATE_USER_SETUP_FINALIZED); } + @Test public void testSetUserProvisioningState_managedProfileWithoutSetupWizard() throws Exception { setupProfileOwner(); @@ -2833,6 +2910,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.STATE_USER_SETUP_FINALIZED); } + @Test public void testSetUserProvisioningState_illegalTransitionOutOfFinalized1() throws Exception { setupProfileOwner(); @@ -2843,6 +2921,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.STATE_USER_UNMANAGED)); } + @Test public void testSetUserProvisioningState_illegalTransitionToAnotherInProgressState() throws Exception { setupProfileOwner(); @@ -2858,10 +2937,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); mContext.callerPermissions.add(permission.MANAGE_USERS); - assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState()); + assertThat(dpm.getUserProvisioningState()) + .isEqualTo(DevicePolicyManager.STATE_USER_UNMANAGED); for (int state : states) { dpm.setUserProvisioningState(state, userId); - assertEquals(state, dpm.getUserProvisioningState()); + assertThat(dpm.getUserProvisioningState()).isEqualTo(state); } } @@ -2870,7 +2950,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID); dpm.setActiveAdmin(admin1, false); - assertTrue(dpm.setProfileOwner(admin1, null, CALLER_USER_HANDLE)); + assertThat(dpm.setProfileOwner(admin1, null, CALLER_USER_HANDLE)).isTrue(); mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS); } @@ -2880,7 +2960,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { setUpPackageManagerForAdmin(admin1, DpmMockContext.SYSTEM_UID); dpm.setActiveAdmin(admin1, false); - assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM)); + assertThat(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue(); mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS); } @@ -2890,11 +2970,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); dpm.setActiveAdmin(admin1, false); - assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)); + assertThat(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM)).isTrue(); mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS); } + @Test public void testSetMaximumTimeToLock() { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); @@ -2956,6 +3037,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verifyStayOnWhilePluggedCleared(false); } + @Test public void testIsActiveSupervisionApp() throws Exception { when(mServiceContext.resources .getString(R.string.config_defaultSupervisionProfileOwnerComponent)) @@ -2968,11 +3050,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { final DevicePolicyManagerInternal dpmi = LocalServices.getService(DevicePolicyManagerInternal.class); - assertTrue(dpmi.isActiveSupervisionApp(PROFILE_ADMIN)); + assertThat(dpmi.isActiveSupervisionApp(PROFILE_ADMIN)).isTrue(); } // Test if lock timeout on managed profile is handled correctly depending on whether profile // uses separate challenge. + @Test public void testSetMaximumTimeToLockProfile() throws Exception { final int PROFILE_USER = 15; final int PROFILE_ADMIN = UserHandle.getUid(PROFILE_USER, 19436); @@ -3039,6 +3122,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verifyScreenTimeoutCall(Long.MAX_VALUE, UserHandle.USER_SYSTEM); } + @Test public void testSetRequiredStrongAuthTimeout_DeviceOwner() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -3055,8 +3139,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { getServices().buildMock.isDebuggable = false; dpm.setRequiredStrongAuthTimeout(admin1, MAX_MINUS_ONE_MINUTE); - assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MAX_MINUS_ONE_MINUTE); - assertEquals(dpm.getRequiredStrongAuthTimeout(null), MAX_MINUS_ONE_MINUTE); + assertThat(MAX_MINUS_ONE_MINUTE).isEqualTo(dpm.getRequiredStrongAuthTimeout(admin1)); + assertThat(MAX_MINUS_ONE_MINUTE).isEqualTo(dpm.getRequiredStrongAuthTimeout(null)); verify(getServices().systemProperties, never()).getLong(anyString(), anyLong()); @@ -3067,45 +3151,47 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setRequiredStrongAuthTimeout(admin1, 0); // aggregation should be the default if unset by any admin - assertEquals(dpm.getRequiredStrongAuthTimeout(null), - DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS); + assertThat(DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) + .isEqualTo(dpm.getRequiredStrongAuthTimeout(null)); // admin not participating by default - assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), 0); + assertThat(dpm.getRequiredStrongAuthTimeout(admin1)).isEqualTo(0); //clamping from the top dpm.setRequiredStrongAuthTimeout(admin1, DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS + ONE_MINUTE); - assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), - DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS); - assertEquals(dpm.getRequiredStrongAuthTimeout(null), - DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS); + assertThat(DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) + .isEqualTo(dpm.getRequiredStrongAuthTimeout(admin1)); + assertThat(DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) + .isEqualTo(dpm.getRequiredStrongAuthTimeout(null)); // 0 means the admin is not participating, so default should be returned dpm.setRequiredStrongAuthTimeout(admin1, 0); - assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), 0); - assertEquals(dpm.getRequiredStrongAuthTimeout(null), - DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS); + assertThat(dpm.getRequiredStrongAuthTimeout(admin1)).isEqualTo(0); + assertThat(DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) + .isEqualTo(dpm.getRequiredStrongAuthTimeout(null)); // clamping from the bottom dpm.setRequiredStrongAuthTimeout(admin1, MINIMUM_STRONG_AUTH_TIMEOUT_MS - ONE_MINUTE); - assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MINIMUM_STRONG_AUTH_TIMEOUT_MS); - assertEquals(dpm.getRequiredStrongAuthTimeout(null), MINIMUM_STRONG_AUTH_TIMEOUT_MS); + assertThat(dpm.getRequiredStrongAuthTimeout(admin1)) + .isEqualTo(MINIMUM_STRONG_AUTH_TIMEOUT_MS); + assertThat(dpm.getRequiredStrongAuthTimeout(null)) + .isEqualTo(MINIMUM_STRONG_AUTH_TIMEOUT_MS); // values within range dpm.setRequiredStrongAuthTimeout(admin1, MIN_PLUS_ONE_MINUTE); - assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MIN_PLUS_ONE_MINUTE); - assertEquals(dpm.getRequiredStrongAuthTimeout(null), MIN_PLUS_ONE_MINUTE); + assertThat(dpm.getRequiredStrongAuthTimeout(admin1)).isEqualTo(MIN_PLUS_ONE_MINUTE); + assertThat(dpm.getRequiredStrongAuthTimeout(null)).isEqualTo(MIN_PLUS_ONE_MINUTE); dpm.setRequiredStrongAuthTimeout(admin1, MAX_MINUS_ONE_MINUTE); - assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MAX_MINUS_ONE_MINUTE); - assertEquals(dpm.getRequiredStrongAuthTimeout(null), MAX_MINUS_ONE_MINUTE); + assertThat(dpm.getRequiredStrongAuthTimeout(admin1)).isEqualTo(MAX_MINUS_ONE_MINUTE); + assertThat(dpm.getRequiredStrongAuthTimeout(null)).isEqualTo(MAX_MINUS_ONE_MINUTE); // reset to default dpm.setRequiredStrongAuthTimeout(admin1, 0); - assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), 0); - assertEquals(dpm.getRequiredStrongAuthTimeout(null), - DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS); + assertThat(dpm.getRequiredStrongAuthTimeout(admin1)).isEqualTo(0); + assertThat(DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) + .isEqualTo(dpm.getRequiredStrongAuthTimeout(null)); // negative value assertExpectException(IllegalArgumentException.class, /* messageRegex= */ null, @@ -3130,8 +3216,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { private void setup_DeviceAdminFeatureOff() throws Exception { when(getServices().packageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) .thenReturn(false); - when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) - .thenReturn(false); + when(getServices().ipackageManager + .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(false); initializeDpms(); when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false); when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) @@ -3141,6 +3227,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; } + @Test public void testIsProvisioningAllowed_DeviceAdminFeatureOff() throws Exception { setup_DeviceAdminFeatureOff(); mContext.packageName = admin1.getPackageName(); @@ -3153,6 +3240,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, false); } + @Test public void testCheckProvisioningPreCondition_DeviceAdminFeatureOff() throws Exception { setup_DeviceAdminFeatureOff(); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -3170,8 +3258,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void setup_ManagedProfileFeatureOff() throws Exception { - when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) - .thenReturn(false); + when(getServices().ipackageManager + .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(false); initializeDpms(); when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false); when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) @@ -3181,6 +3269,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; } + @Test public void testIsProvisioningAllowed_ManagedProfileFeatureOff() throws Exception { setup_ManagedProfileFeatureOff(); mContext.packageName = admin1.getPackageName(); @@ -3202,6 +3291,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, false); } + @Test public void testCheckProvisioningPreCondition_ManagedProfileFeatureOff() throws Exception { setup_ManagedProfileFeatureOff(); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -3233,8 +3323,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void setup_nonSplitUser_firstBoot_primaryUser() throws Exception { - when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) - .thenReturn(true); + when(getServices().ipackageManager + .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true); when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false); when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) .thenReturn(true); @@ -3244,6 +3334,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; } + @Test public void testIsProvisioningAllowed_nonSplitUser_firstBoot_primaryUser() throws Exception { setup_nonSplitUser_firstBoot_primaryUser(); mContext.packageName = admin1.getPackageName(); @@ -3257,6 +3348,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { false /* because of non-split user */); } + @Test public void testCheckProvisioningPreCondition_nonSplitUser_firstBoot_primaryUser() throws Exception { setup_nonSplitUser_firstBoot_primaryUser(); @@ -3275,8 +3367,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void setup_nonSplitUser_afterDeviceSetup_primaryUser() throws Exception { - when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) - .thenReturn(true); + when(getServices().ipackageManager + .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true); when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false); when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) .thenReturn(true); @@ -3302,6 +3394,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { true)).thenReturn(true); } + @Test public void testIsProvisioningAllowed_nonSplitUser_afterDeviceSetup_primaryUser() throws Exception { setup_nonSplitUser_afterDeviceSetup_primaryUser(); @@ -3318,6 +3411,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { false/* because of non-split user */); } + @Test public void testCheckProvisioningPreCondition_nonSplitUser_afterDeviceSetup_primaryUser() throws Exception { setup_nonSplitUser_afterDeviceSetup_primaryUser(); @@ -3335,6 +3429,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.CODE_NOT_SYSTEM_USER_SPLIT); } + @Test public void testProvisioning_nonSplitUser_withDo_primaryUser() throws Exception { setup_nonSplitUser_withDo_primaryUser(); mContext.packageName = admin1.getPackageName(); @@ -3361,6 +3456,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID); } + @Test public void testProvisioning_nonSplitUser_withDo_primaryUser_restrictedBySystem() throws Exception { setup_nonSplitUser_withDo_primaryUser(); @@ -3388,6 +3484,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID); } + @Test public void testCheckCannotSetProfileOwnerWithDeviceOwner() throws Exception { setup_nonSplitUser_withDo_primaryUser(); final int managedProfileUserId = 18; @@ -3399,10 +3496,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS); setUpPackageManagerForFakeAdmin(admin1, managedProfileAdminUid, admin1); dpm.setActiveAdmin(admin1, false, userId); - assertFalse(dpm.setProfileOwner(admin1, null, userId)); + assertThat(dpm.setProfileOwner(admin1, null, userId)).isFalse(); mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS); } + @Test public void testCheckProvisioningPreCondition_nonSplitUser_attemptingComp() throws Exception { setup_nonSplitUser_withDo_primaryUser_ManagedProfile(); mContext.packageName = admin1.getPackageName(); @@ -3420,6 +3518,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID); } + @Test public void testCheckProvisioningPreCondition_nonSplitUser_comp_cannot_remove_profile() throws Exception { setup_nonSplitUser_withDo_primaryUser_ManagedProfile(); @@ -3449,8 +3548,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void setup_splitUser_firstBoot_systemUser() throws Exception { - when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) - .thenReturn(true); + when(getServices().ipackageManager + .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true); when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true); when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) .thenReturn(false); @@ -3459,6 +3558,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; } + @Test public void testIsProvisioningAllowed_splitUser_firstBoot_systemUser() throws Exception { setup_splitUser_firstBoot_systemUser(); mContext.packageName = admin1.getPackageName(); @@ -3473,6 +3573,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { false/* because calling uid is system user */); } + @Test public void testCheckProvisioningPreCondition_splitUser_firstBoot_systemUser() throws Exception { setup_splitUser_firstBoot_systemUser(); @@ -3491,8 +3592,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void setup_splitUser_afterDeviceSetup_systemUser() throws Exception { - when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) - .thenReturn(true); + when(getServices().ipackageManager + .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true); when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true); when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) .thenReturn(false); @@ -3501,6 +3602,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; } + @Test public void testIsProvisioningAllowed_splitUser_afterDeviceSetup_systemUser() throws Exception { setup_splitUser_afterDeviceSetup_systemUser(); mContext.packageName = admin1.getPackageName(); @@ -3517,6 +3619,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { false/* because calling uid is system user */); } + @Test public void testCheckProvisioningPreCondition_splitUser_afterDeviceSetup_systemUser() throws Exception { setup_splitUser_afterDeviceSetup_systemUser(); @@ -3535,8 +3638,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void setup_splitUser_firstBoot_primaryUser() throws Exception { - when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) - .thenReturn(true); + when(getServices().ipackageManager + .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true); when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true); when(getServices().userManager.canAddMoreManagedProfiles(CALLER_USER_HANDLE, true)).thenReturn(true); @@ -3545,6 +3648,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_UID; } + @Test public void testIsProvisioningAllowed_splitUser_firstBoot_primaryUser() throws Exception { setup_splitUser_firstBoot_primaryUser(); mContext.packageName = admin1.getPackageName(); @@ -3557,6 +3661,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, true); } + @Test public void testCheckProvisioningPreCondition_splitUser_firstBoot_primaryUser() throws Exception { setup_splitUser_firstBoot_primaryUser(); @@ -3575,8 +3680,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void setup_splitUser_afterDeviceSetup_primaryUser() throws Exception { - when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) - .thenReturn(true); + when(getServices().ipackageManager + .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true); when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true); when(getServices().userManager.canAddMoreManagedProfiles(CALLER_USER_HANDLE, true)).thenReturn(true); @@ -3585,6 +3690,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_UID; } + @Test public void testIsProvisioningAllowed_splitUser_afterDeviceSetup_primaryUser() throws Exception { setup_splitUser_afterDeviceSetup_primaryUser(); @@ -3601,6 +3707,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { false/* because user setup completed */); } + @Test public void testCheckProvisioningPreCondition_splitUser_afterDeviceSetup_primaryUser() throws Exception { setup_splitUser_afterDeviceSetup_primaryUser(); @@ -3621,8 +3728,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { private void setup_provisionManagedProfileWithDeviceOwner_systemUser() throws Exception { setDeviceOwner(); - when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) - .thenReturn(true); + when(getServices().ipackageManager + .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true); when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true); when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) .thenReturn(false); @@ -3631,6 +3738,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; } + @Test public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_systemUser() throws Exception { setup_provisionManagedProfileWithDeviceOwner_systemUser(); @@ -3640,6 +3748,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { false /* can't provision managed profile on system user */); } + @Test public void testCheckProvisioningPreCondition_provisionManagedProfileWithDeviceOwner_systemUser() throws Exception { setup_provisionManagedProfileWithDeviceOwner_systemUser(); @@ -3651,8 +3760,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { private void setup_provisionManagedProfileWithDeviceOwner_primaryUser() throws Exception { setDeviceOwner(); - when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) - .thenReturn(true); + when(getServices().ipackageManager + .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true); when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false); when(getServices().userManager.getProfileParent(CALLER_USER_HANDLE)) .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); @@ -3663,6 +3772,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.ANOTHER_UID; } + @Test public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser() throws Exception { setup_provisionManagedProfileWithDeviceOwner_primaryUser(); @@ -3671,6 +3781,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false); } + @Test public void testCheckProvisioningPreCondition_provisionManagedProfileWithDeviceOwner_primaryUser() throws Exception { setup_provisionManagedProfileWithDeviceOwner_primaryUser(); @@ -3684,8 +3795,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { private void setup_provisionManagedProfileCantRemoveUser_primaryUser() throws Exception { setDeviceOwner(); - when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) - .thenReturn(true); + when(getServices().ipackageManager + .hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)).thenReturn(true); when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true); when(getServices().userManager.hasUserRestriction( eq(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE), @@ -3700,6 +3811,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_UID; } + @Test public void testIsProvisioningAllowed_provisionManagedProfileCantRemoveUser_primaryUser() throws Exception { setup_provisionManagedProfileCantRemoveUser_primaryUser(); @@ -3708,6 +3820,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false); } + @Test public void testCheckProvisioningPreCondition_provisionManagedProfileCantRemoveUser_primaryUser() throws Exception { setup_provisionManagedProfileCantRemoveUser_primaryUser(); @@ -3716,6 +3829,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE); } + @Test public void testCheckProvisioningPreCondition_permission() { // GIVEN the permission MANAGE_PROFILE_AND_DEVICE_OWNERS is not granted assertExpectException(SecurityException.class, /* messageRegex =*/ null, @@ -3723,12 +3837,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, "some.package")); } + @Test public void testForceUpdateUserSetupComplete_permission() { // GIVEN the permission MANAGE_PROFILE_AND_DEVICE_OWNERS is not granted assertExpectException(SecurityException.class, /* messageRegex =*/ null, () -> dpm.forceUpdateUserSetupComplete()); } + @Test public void testForceUpdateUserSetupComplete_systemUser() { mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); // GIVEN calling from user 20 @@ -3737,6 +3853,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.forceUpdateUserSetupComplete()); } + @Test public void testForceUpdateUserSetupComplete_userbuild() { mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; @@ -3753,14 +3870,15 @@ public class DevicePolicyManagerTest extends DpmTestBase { // GIVEN it's user build getServices().buildMock.isDebuggable = false; - assertTrue(dpms.hasUserSetupCompleted()); + assertThat(dpms.hasUserSetupCompleted()).isTrue(); dpm.forceUpdateUserSetupComplete(); // THEN the state in dpms is not changed - assertTrue(dpms.hasUserSetupCompleted()); + assertThat(dpms.hasUserSetupCompleted()).isTrue(); } + @Test public void testForceUpdateUserSetupComplete_userDebugbuild() { mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; @@ -3777,12 +3895,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { // GIVEN it's userdebug build getServices().buildMock.isDebuggable = true; - assertTrue(dpms.hasUserSetupCompleted()); + assertThat(dpms.hasUserSetupCompleted()).isTrue(); dpm.forceUpdateUserSetupComplete(); // THEN the state in dpms is not changed - assertFalse(dpms.hasUserSetupCompleted()); + assertThat(dpms.hasUserSetupCompleted()).isFalse(); } private void clearDeviceOwner() throws Exception { @@ -3795,6 +3913,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { }); } + @Test public void testGetLastSecurityLogRetrievalTime() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -3806,7 +3925,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(true); // No logs were retrieved so far. - assertEquals(-1, dpm.getLastSecurityLogRetrievalTime()); + assertThat(dpm.getLastSecurityLogRetrievalTime()).isEqualTo(-1); // Enabling logging should not change the timestamp. dpm.setSecurityLoggingEnabled(admin1, true); @@ -3814,55 +3933,56 @@ public class DevicePolicyManagerTest extends DpmTestBase { .securityLogSetLoggingEnabledProperty(true); when(getServices().settings.securityLogGetLoggingEnabledProperty()) .thenReturn(true); - assertEquals(-1, dpm.getLastSecurityLogRetrievalTime()); + assertThat(dpm.getLastSecurityLogRetrievalTime()).isEqualTo(-1); // Retrieving the logs should update the timestamp. final long beforeRetrieval = System.currentTimeMillis(); dpm.retrieveSecurityLogs(admin1); final long firstSecurityLogRetrievalTime = dpm.getLastSecurityLogRetrievalTime(); final long afterRetrieval = System.currentTimeMillis(); - assertTrue(firstSecurityLogRetrievalTime >= beforeRetrieval); - assertTrue(firstSecurityLogRetrievalTime <= afterRetrieval); + assertThat(firstSecurityLogRetrievalTime >= beforeRetrieval).isTrue(); + assertThat(firstSecurityLogRetrievalTime <= afterRetrieval).isTrue(); // Retrieving the pre-boot logs should update the timestamp. Thread.sleep(2); dpm.retrievePreRebootSecurityLogs(admin1); final long secondSecurityLogRetrievalTime = dpm.getLastSecurityLogRetrievalTime(); - assertTrue(secondSecurityLogRetrievalTime > firstSecurityLogRetrievalTime); + assertThat(secondSecurityLogRetrievalTime > firstSecurityLogRetrievalTime).isTrue(); // Checking the timestamp again should not change it. Thread.sleep(2); - assertEquals(secondSecurityLogRetrievalTime, dpm.getLastSecurityLogRetrievalTime()); + assertThat(dpm.getLastSecurityLogRetrievalTime()).isEqualTo(secondSecurityLogRetrievalTime); // Retrieving the logs again should update the timestamp. dpm.retrieveSecurityLogs(admin1); final long thirdSecurityLogRetrievalTime = dpm.getLastSecurityLogRetrievalTime(); - assertTrue(thirdSecurityLogRetrievalTime > secondSecurityLogRetrievalTime); + assertThat(thirdSecurityLogRetrievalTime > secondSecurityLogRetrievalTime).isTrue(); // Disabling logging should not change the timestamp. Thread.sleep(2); dpm.setSecurityLoggingEnabled(admin1, false); - assertEquals(thirdSecurityLogRetrievalTime, dpm.getLastSecurityLogRetrievalTime()); + assertThat(dpm.getLastSecurityLogRetrievalTime()).isEqualTo(thirdSecurityLogRetrievalTime); // Restarting the DPMS should not lose the timestamp. initializeDpms(); - assertEquals(thirdSecurityLogRetrievalTime, dpm.getLastSecurityLogRetrievalTime()); + assertThat(dpm.getLastSecurityLogRetrievalTime()).isEqualTo(thirdSecurityLogRetrievalTime); // Any uid holding MANAGE_USERS permission can retrieve the timestamp. mContext.binder.callingUid = 1234567; mContext.callerPermissions.add(permission.MANAGE_USERS); - assertEquals(thirdSecurityLogRetrievalTime, dpm.getLastSecurityLogRetrievalTime()); + assertThat(dpm.getLastSecurityLogRetrievalTime()).isEqualTo(thirdSecurityLogRetrievalTime); mContext.callerPermissions.remove(permission.MANAGE_USERS); // System can retrieve the timestamp. mContext.binder.clearCallingIdentity(); - assertEquals(thirdSecurityLogRetrievalTime, dpm.getLastSecurityLogRetrievalTime()); + assertThat(dpm.getLastSecurityLogRetrievalTime()).isEqualTo(thirdSecurityLogRetrievalTime); // Removing the device owner should clear the timestamp. clearDeviceOwner(); - assertEquals(-1, dpm.getLastSecurityLogRetrievalTime()); + assertThat(dpm.getLastSecurityLogRetrievalTime()).isEqualTo(-1); } + @Test public void testSetConfiguredNetworksLockdownStateWithDO() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -3875,6 +3995,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0); } + @Test public void testSetConfiguredNetworksLockdownStateWithPO() throws Exception { setupProfileOwner(); assertExpectException(SecurityException.class, null, @@ -3883,6 +4004,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0); } + @Test public void testSetConfiguredNetworksLockdownStateWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); @@ -3896,6 +4018,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0); } + @Test public void testSetSystemSettingFailWithNonWhitelistedSettings() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -3903,6 +4026,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS_FOR_VR, "0")); } + @Test public void testSetSystemSettingWithDO() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -3911,6 +4035,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { Settings.System.SCREEN_BRIGHTNESS, "0", UserHandle.USER_SYSTEM); } + @Test public void testSetSystemSettingWithPO() throws Exception { setupProfileOwner(); dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS, "0"); @@ -3918,6 +4043,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { Settings.System.SCREEN_BRIGHTNESS, "0", CALLER_USER_HANDLE); } + @Test public void testSetAutoTimeEnabledModifiesSetting() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -3928,6 +4054,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0); } + @Test public void testSetAutoTimeEnabledWithPOOnUser0() throws Exception { mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; setupProfileOwnerOnUser0(); @@ -3938,6 +4065,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0); } + @Test public void testSetAutoTimeEnabledFailWithPONotOnUser0() throws Exception { setupProfileOwner(); assertExpectException(SecurityException.class, null, @@ -3945,6 +4073,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().settings, never()).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0); } + @Test public void testSetAutoTimeEnabledWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); @@ -3956,6 +4085,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME, 0); } + @Test public void testSetAutoTimeZoneEnabledModifiesSetting() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -3966,6 +4096,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0); } + @Test public void testSetAutoTimeZoneEnabledWithPOOnUser0() throws Exception { mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; setupProfileOwnerOnUser0(); @@ -3976,6 +4107,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0); } + @Test public void testSetAutoTimeZoneEnabledFailWithPONotOnUser0() throws Exception { setupProfileOwner(); assertExpectException(SecurityException.class, null, @@ -3984,6 +4116,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { 0); } + @Test public void testSetAutoTimeZoneEnabledWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); @@ -3995,12 +4128,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().settings).settingsGlobalPutInt(Settings.Global.AUTO_TIME_ZONE, 0); } + @Test public void testIsOrganizationOwnedDevice() throws Exception { // Set up the user manager to return correct user info addManagedProfile(admin1, DpmMockContext.CALLER_UID, admin1); // Any caller should be able to call this method. - assertFalse(dpm.isOrganizationOwnedDeviceWithManagedProfile()); + assertThat(dpm.isOrganizationOwnedDeviceWithManagedProfile()).isFalse(); configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); verify(getServices().userManager).setUserRestriction( @@ -4008,13 +4142,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(true), eq(UserHandle.of(UserHandle.USER_SYSTEM))); - assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile()); + assertThat(dpm.isOrganizationOwnedDeviceWithManagedProfile()).isTrue(); // A random caller from another user should also be able to get the right result. mContext.binder.callingUid = DpmMockContext.ANOTHER_UID; - assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile()); + assertThat(dpm.isOrganizationOwnedDeviceWithManagedProfile()).isTrue(); } + @Test public void testMarkOrganizationOwnedDevice_baseRestrictionsAdded() throws Exception { addManagedProfile(admin1, DpmMockContext.CALLER_UID, admin1); @@ -4044,6 +4179,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { parentDpm.clearUserRestriction(admin1, UserManager.DISALLOW_ADD_USER)); } + @Test public void testSetTime() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -4051,11 +4187,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().alarmManager).setTime(0); } + @Test public void testSetTimeFailWithPO() throws Exception { setupProfileOwner(); assertExpectException(SecurityException.class, null, () -> dpm.setTime(admin1, 0)); } + @Test public void testSetTimeWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); @@ -4063,14 +4201,16 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().alarmManager).setTime(0); } + @Test public void testSetTimeWithAutoTimeOn() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); when(getServices().settings.settingsGlobalGetInt(Settings.Global.AUTO_TIME, 0)) .thenReturn(1); - assertFalse(dpm.setTime(admin1, 0)); + assertThat(dpm.setTime(admin1, 0)).isFalse(); } + @Test public void testSetTimeZone() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -4078,12 +4218,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().alarmManager).setTimeZone("Asia/Shanghai"); } + @Test public void testSetTimeZoneFailWithPO() throws Exception { setupProfileOwner(); assertExpectException(SecurityException.class, null, () -> dpm.setTimeZone(admin1, "Asia/Shanghai")); } + @Test public void testSetTimeZoneWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); @@ -4091,14 +4233,16 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().alarmManager).setTimeZone("Asia/Shanghai"); } + @Test public void testSetTimeZoneWithAutoTimeZoneOn() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); when(getServices().settings.settingsGlobalGetInt(Settings.Global.AUTO_TIME_ZONE, 0)) .thenReturn(1); - assertFalse(dpm.setTimeZone(admin1, "Asia/Shanghai")); + assertThat(dpm.setTimeZone(admin1, "Asia/Shanghai")).isFalse(); } + @Test public void testGetLastBugReportRequestTime() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -4115,39 +4259,40 @@ public class DevicePolicyManagerTest extends DpmTestBase { getServices().removeUser(CALLER_USER_HANDLE); // No bug reports were requested so far. - assertEquals(-1, dpm.getLastBugReportRequestTime()); + assertThat(dpm.getLastBugReportRequestTime()).isEqualTo(-1); // Requesting a bug report should update the timestamp. final long beforeRequest = System.currentTimeMillis(); dpm.requestBugreport(admin1); final long bugReportRequestTime = dpm.getLastBugReportRequestTime(); final long afterRequest = System.currentTimeMillis(); - assertTrue(bugReportRequestTime >= beforeRequest); - assertTrue(bugReportRequestTime <= afterRequest); + assertThat(bugReportRequestTime).isAtLeast(beforeRequest); + assertThat(bugReportRequestTime).isAtMost(afterRequest); // Checking the timestamp again should not change it. Thread.sleep(2); - assertEquals(bugReportRequestTime, dpm.getLastBugReportRequestTime()); + assertThat(dpm.getLastBugReportRequestTime()).isEqualTo(bugReportRequestTime); // Restarting the DPMS should not lose the timestamp. initializeDpms(); - assertEquals(bugReportRequestTime, dpm.getLastBugReportRequestTime()); + assertThat(dpm.getLastBugReportRequestTime()).isEqualTo(bugReportRequestTime); // Any uid holding MANAGE_USERS permission can retrieve the timestamp. mContext.binder.callingUid = 1234567; mContext.callerPermissions.add(permission.MANAGE_USERS); - assertEquals(bugReportRequestTime, dpm.getLastBugReportRequestTime()); + assertThat(dpm.getLastBugReportRequestTime()).isEqualTo(bugReportRequestTime); mContext.callerPermissions.remove(permission.MANAGE_USERS); // System can retrieve the timestamp. mContext.binder.clearCallingIdentity(); - assertEquals(bugReportRequestTime, dpm.getLastBugReportRequestTime()); + assertThat(dpm.getLastBugReportRequestTime()).isEqualTo(bugReportRequestTime); // Removing the device owner should clear the timestamp. clearDeviceOwner(); - assertEquals(-1, dpm.getLastBugReportRequestTime()); + assertThat(dpm.getLastBugReportRequestTime()).isEqualTo(-1); } + @Test public void testGetLastNetworkLogRetrievalTime() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -4165,57 +4310,58 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(true); // No logs were retrieved so far. - assertEquals(-1, dpm.getLastNetworkLogRetrievalTime()); + assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(-1); // Attempting to retrieve logs without enabling logging should not change the timestamp. dpm.retrieveNetworkLogs(admin1, 0 /* batchToken */); - assertEquals(-1, dpm.getLastNetworkLogRetrievalTime()); + assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(-1); // Enabling logging should not change the timestamp. dpm.setNetworkLoggingEnabled(admin1, true); - assertEquals(-1, dpm.getLastNetworkLogRetrievalTime()); + assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(-1); // Retrieving the logs should update the timestamp. final long beforeRetrieval = System.currentTimeMillis(); dpm.retrieveNetworkLogs(admin1, 0 /* batchToken */); final long firstNetworkLogRetrievalTime = dpm.getLastNetworkLogRetrievalTime(); final long afterRetrieval = System.currentTimeMillis(); - assertTrue(firstNetworkLogRetrievalTime >= beforeRetrieval); - assertTrue(firstNetworkLogRetrievalTime <= afterRetrieval); + assertThat(firstNetworkLogRetrievalTime >= beforeRetrieval).isTrue(); + assertThat(firstNetworkLogRetrievalTime <= afterRetrieval).isTrue(); // Checking the timestamp again should not change it. Thread.sleep(2); - assertEquals(firstNetworkLogRetrievalTime, dpm.getLastNetworkLogRetrievalTime()); + assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(firstNetworkLogRetrievalTime); // Retrieving the logs again should update the timestamp. dpm.retrieveNetworkLogs(admin1, 0 /* batchToken */); final long secondNetworkLogRetrievalTime = dpm.getLastNetworkLogRetrievalTime(); - assertTrue(secondNetworkLogRetrievalTime > firstNetworkLogRetrievalTime); + assertThat(secondNetworkLogRetrievalTime > firstNetworkLogRetrievalTime).isTrue(); // Disabling logging should not change the timestamp. Thread.sleep(2); dpm.setNetworkLoggingEnabled(admin1, false); - assertEquals(secondNetworkLogRetrievalTime, dpm.getLastNetworkLogRetrievalTime()); + assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(secondNetworkLogRetrievalTime); // Restarting the DPMS should not lose the timestamp. initializeDpms(); - assertEquals(secondNetworkLogRetrievalTime, dpm.getLastNetworkLogRetrievalTime()); + assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(secondNetworkLogRetrievalTime); // Any uid holding MANAGE_USERS permission can retrieve the timestamp. mContext.binder.callingUid = 1234567; mContext.callerPermissions.add(permission.MANAGE_USERS); - assertEquals(secondNetworkLogRetrievalTime, dpm.getLastNetworkLogRetrievalTime()); + assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(secondNetworkLogRetrievalTime); mContext.callerPermissions.remove(permission.MANAGE_USERS); // System can retrieve the timestamp. mContext.binder.clearCallingIdentity(); - assertEquals(secondNetworkLogRetrievalTime, dpm.getLastNetworkLogRetrievalTime()); + assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(secondNetworkLogRetrievalTime); // Removing the device owner should clear the timestamp. clearDeviceOwner(); - assertEquals(-1, dpm.getLastNetworkLogRetrievalTime()); + assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(-1); } + @Test public void testGetBindDeviceAdminTargetUsers() throws Exception { // Setup device owner. mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; @@ -4268,9 +4414,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setLockTaskPackages(who, packages); MoreAsserts.assertEquals(packages, dpm.getLockTaskPackages(who)); for (String p : packages) { - assertTrue(dpm.isLockTaskPermitted(p)); + assertThat(dpm.isLockTaskPermitted(p)).isTrue(); } - assertFalse(dpm.isLockTaskPermitted("anotherPackage")); + assertThat(dpm.isLockTaskPermitted("anotherPackage")).isFalse(); // Test to see if set lock task features can be set dpm.setLockTaskFeatures(who, flags); verifyLockTaskState(userId, packages, flags); @@ -4283,11 +4429,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.setLockTaskPackages(who, packages)); assertExpectException(SecurityException.class, /* messageRegex =*/ null, () -> dpm.getLockTaskPackages(who)); - assertFalse(dpm.isLockTaskPermitted("doPackage1")); + assertThat(dpm.isLockTaskPermitted("doPackage1")).isFalse(); assertExpectException(SecurityException.class, /* messageRegex =*/ null, () -> dpm.setLockTaskFeatures(who, flags)); } + @Test public void testLockTaskPolicyForProfileOwner() throws Exception { // Setup a PO mContext.binder.callingUid = DpmMockContext.CALLER_UID; @@ -4315,9 +4462,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { final int mpoFlags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS | DevicePolicyManager.LOCK_TASK_FEATURE_HOME | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW; - verifyCanNotSetLockTask(MANAGED_PROFILE_ADMIN_UID, adminDifferentPackage, mpoPackages, mpoFlags); + verifyCanNotSetLockTask(MANAGED_PROFILE_ADMIN_UID, adminDifferentPackage, mpoPackages, + mpoFlags); } + @Test public void testLockTaskFeatures_IllegalArgumentException() throws Exception { // Setup a device owner. mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; @@ -4332,12 +4481,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.setLockTaskFeatures(admin1, flags)); } + @Test public void testSecondaryLockscreen_profileOwner() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_UID; // Initial state is disabled. - assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.of( - CALLER_USER_HANDLE))); + assertThat(dpm.isSecondaryLockscreenEnabled(UserHandle.of( + CALLER_USER_HANDLE))).isFalse(); // Profile owner can set enabled state. setAsProfileOwner(admin1); @@ -4345,8 +4495,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { .getString(R.string.config_defaultSupervisionProfileOwnerComponent)) .thenReturn(admin1.flattenToString()); dpm.setSecondaryLockscreenEnabled(admin1, true); - assertTrue(dpm.isSecondaryLockscreenEnabled(UserHandle.of( - CALLER_USER_HANDLE))); + assertThat(dpm.isSecondaryLockscreenEnabled(UserHandle.of( + CALLER_USER_HANDLE))).isTrue(); // Managed profile managed by different package is unaffiliated - cannot set enabled. final int managedProfileUserId = 15; @@ -4359,11 +4509,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.setSecondaryLockscreenEnabled(adminDifferentPackage, false)); } + @Test public void testSecondaryLockscreen_deviceOwner() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; // Initial state is disabled. - assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.of(UserHandle.USER_SYSTEM))); + assertThat(dpm.isSecondaryLockscreenEnabled(UserHandle.of(UserHandle.USER_SYSTEM))) + .isFalse(); // Device owners can set enabled state. setupDeviceOwner(); @@ -4371,14 +4523,16 @@ public class DevicePolicyManagerTest extends DpmTestBase { .getString(R.string.config_defaultSupervisionProfileOwnerComponent)) .thenReturn(admin1.flattenToString()); dpm.setSecondaryLockscreenEnabled(admin1, true); - assertTrue(dpm.isSecondaryLockscreenEnabled(UserHandle.of(UserHandle.USER_SYSTEM))); + assertThat(dpm.isSecondaryLockscreenEnabled(UserHandle.of(UserHandle.USER_SYSTEM))) + .isTrue(); } + @Test public void testSecondaryLockscreen_nonOwner() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_UID; // Initial state is disabled. - assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE))); + assertThat(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE))).isFalse(); // Non-DO/PO cannot set enabled state. when(mServiceContext.resources @@ -4386,9 +4540,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(admin1.flattenToString()); assertExpectException(SecurityException.class, /* messageRegex= */ null, () -> dpm.setSecondaryLockscreenEnabled(admin1, true)); - assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE))); + assertThat(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE))).isFalse(); } + @Test public void testSecondaryLockscreen_nonSupervisionApp() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_UID; @@ -4403,13 +4558,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(CALLER_USER_HANDLE)); // Initial state is disabled. - assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE))); + assertThat(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE))).isFalse(); // Caller is Profile Owner, but no supervision app is configured. setAsProfileOwner(admin1); assertExpectException(SecurityException.class, "is not the default supervision component", () -> dpm.setSecondaryLockscreenEnabled(admin1, true)); - assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE))); + assertThat(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE))).isFalse(); // Caller is Profile Owner, but is not the default configured supervision app. when(mServiceContext.resources @@ -4417,22 +4572,23 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(admin2.flattenToString()); assertExpectException(SecurityException.class, "is not the default supervision component", () -> dpm.setSecondaryLockscreenEnabled(admin1, true)); - assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE))); + assertThat(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE))).isFalse(); } + @Test public void testIsDeviceManaged() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); // The device owner itself, any uid holding MANAGE_USERS permission and the system can // find out that the device has a device owner. - assertTrue(dpm.isDeviceManaged()); + assertThat(dpm.isDeviceManaged()).isTrue(); mContext.binder.callingUid = 1234567; mContext.callerPermissions.add(permission.MANAGE_USERS); - assertTrue(dpm.isDeviceManaged()); + assertThat(dpm.isDeviceManaged()).isTrue(); mContext.callerPermissions.remove(permission.MANAGE_USERS); mContext.binder.clearCallingIdentity(); - assertTrue(dpm.isDeviceManaged()); + assertThat(dpm.isDeviceManaged()).isTrue(); clearDeviceOwner(); @@ -4440,12 +4596,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { // not have a device owner. mContext.binder.callingUid = 1234567; mContext.callerPermissions.add(permission.MANAGE_USERS); - assertFalse(dpm.isDeviceManaged()); + assertThat(dpm.isDeviceManaged()).isFalse(); mContext.callerPermissions.remove(permission.MANAGE_USERS); mContext.binder.clearCallingIdentity(); - assertFalse(dpm.isDeviceManaged()); + assertThat(dpm.isDeviceManaged()).isFalse(); } + @Test public void testDeviceOwnerOrganizationName() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -4453,23 +4610,24 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setOrganizationName(admin1, "organization"); // Device owner can retrieve organization managing the device. - assertEquals("organization", dpm.getDeviceOwnerOrganizationName()); + assertThat(dpm.getDeviceOwnerOrganizationName()).isEqualTo("organization"); // Any uid holding MANAGE_USERS permission can retrieve organization managing the device. mContext.binder.callingUid = 1234567; mContext.callerPermissions.add(permission.MANAGE_USERS); - assertEquals("organization", dpm.getDeviceOwnerOrganizationName()); + assertThat(dpm.getDeviceOwnerOrganizationName()).isEqualTo("organization"); mContext.callerPermissions.remove(permission.MANAGE_USERS); // System can retrieve organization managing the device. mContext.binder.clearCallingIdentity(); - assertEquals("organization", dpm.getDeviceOwnerOrganizationName()); + assertThat(dpm.getDeviceOwnerOrganizationName()).isEqualTo("organization"); // Removing the device owner clears the organization managing the device. clearDeviceOwner(); - assertNull(dpm.getDeviceOwnerOrganizationName()); + assertThat(dpm.getDeviceOwnerOrganizationName()).isNull(); } + @Test public void testWipeDataManagedProfile() throws Exception { final int MANAGED_PROFILE_USER_ID = 15; final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436); @@ -4489,6 +4647,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { MANAGED_PROFILE_USER_ID); } + @Test public void testWipeDataManagedProfileDisallowed() throws Exception { final int MANAGED_PROFILE_USER_ID = 15; final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436); @@ -4512,6 +4671,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.wipeData(0)); } + @Test public void testWipeDataDeviceOwner() throws Exception { setDeviceOwner(); when(getServices().userManager.getUserRestrictionSource( @@ -4527,6 +4687,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /*wipeEuicc=*/ eq(false)); } + @Test public void testWipeEuiccDataEnabled() throws Exception { setDeviceOwner(); when(getServices().userManager.getUserRestrictionSource( @@ -4542,6 +4703,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /*wipeEuicc=*/ eq(true)); } + @Test public void testWipeDataDeviceOwnerDisallowed() throws Exception { setDeviceOwner(); when(getServices().userManager.getUserRestrictionSource( @@ -4556,6 +4718,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.wipeData(0)); } + @Test public void testMaximumFailedPasswordAttemptsReachedManagedProfile() throws Exception { final int MANAGED_PROFILE_USER_ID = 15; final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436); @@ -4588,6 +4751,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verifyZeroInteractions(getServices().recoverySystem); } + @Test public void testMaximumFailedPasswordAttemptsReachedManagedProfileDisallowed() throws Exception { final int MANAGED_PROFILE_USER_ID = 15; @@ -4621,6 +4785,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verifyZeroInteractions(getServices().recoverySystem); } + @Test public void testMaximumFailedPasswordAttemptsReachedDeviceOwner() throws Exception { setDeviceOwner(); when(getServices().userManager.getUserRestrictionSource( @@ -4643,6 +4808,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /*wipeEuicc=*/ eq(false)); } + @Test public void testMaximumFailedPasswordAttemptsReachedDeviceOwnerDisallowed() throws Exception { setDeviceOwner(); when(getServices().userManager.getUserRestrictionSource( @@ -4664,6 +4830,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .removeUserEvenWhenDisallowed(anyInt()); } + @Test public void testMaximumFailedDevicePasswordAttemptsReachedOrgOwnedManagedProfile() throws Exception { final int MANAGED_PROFILE_USER_ID = 15; @@ -4679,16 +4846,16 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; dpm.setMaximumFailedPasswordsForWipe(admin1, 3); - assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(admin1)); - assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(null)); + assertThat(dpm.getMaximumFailedPasswordsForWipe(admin1)).isEqualTo(3); + assertThat(dpm.getMaximumFailedPasswordsForWipe(null)).isEqualTo(3); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); - assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(null, UserHandle.USER_SYSTEM)); + assertThat(dpm.getMaximumFailedPasswordsForWipe(null, UserHandle.USER_SYSTEM)).isEqualTo(3); // Check that primary will be wiped as a result of failed primary user unlock attempts. - assertEquals(UserHandle.USER_SYSTEM, - dpm.getProfileWithMinimumFailedPasswordsForWipe(UserHandle.USER_SYSTEM)); + assertThat(dpm.getProfileWithMinimumFailedPasswordsForWipe(UserHandle.USER_SYSTEM)) + .isEqualTo(UserHandle.USER_SYSTEM); // Failed password attempts on the parent user are taken into account, as there isn't a // separate work challenge. @@ -4702,6 +4869,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /*wipeEuicc=*/ eq(false)); } + @Test public void testMaximumFailedProfilePasswordAttemptsReachedOrgOwnedManagedProfile() throws Exception { final int MANAGED_PROFILE_USER_ID = 15; @@ -4724,14 +4892,15 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN); - assertEquals(0, dpm.getMaximumFailedPasswordsForWipe(null, UserHandle.USER_SYSTEM)); - assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(null, MANAGED_PROFILE_USER_ID)); + assertThat(dpm.getMaximumFailedPasswordsForWipe(null, UserHandle.USER_SYSTEM)).isEqualTo(0); + assertThat(dpm.getMaximumFailedPasswordsForWipe(null, MANAGED_PROFILE_USER_ID)) + .isEqualTo(3); // Check that the policy is not affecting primary profile challenge. - assertEquals(UserHandle.USER_NULL, - dpm.getProfileWithMinimumFailedPasswordsForWipe(UserHandle.USER_SYSTEM)); + assertThat(dpm.getProfileWithMinimumFailedPasswordsForWipe(UserHandle.USER_SYSTEM)) + .isEqualTo(UserHandle.USER_NULL); // Check that primary will be wiped as a result of failed profile unlock attempts. - assertEquals(UserHandle.USER_SYSTEM, - dpm.getProfileWithMinimumFailedPasswordsForWipe(MANAGED_PROFILE_USER_ID)); + assertThat(dpm.getProfileWithMinimumFailedPasswordsForWipe(MANAGED_PROFILE_USER_ID)) + .isEqualTo(UserHandle.USER_SYSTEM); // Simulate three failed attempts at solving the separate challenge. dpm.reportFailedPasswordAttempt(MANAGED_PROFILE_USER_ID); @@ -4744,6 +4913,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /*wipeEuicc=*/ eq(false)); } + @Test public void testGetPermissionGrantState() throws Exception { final String permission = "some.permission"; final String app1 = "com.example.app1"; @@ -4766,10 +4936,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { // System can retrieve permission grant state. mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; mContext.packageName = "android"; - assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED, - dpm.getPermissionGrantState(null, app1, permission)); - assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT, - dpm.getPermissionGrantState(null, app2, permission)); + assertThat(dpm.getPermissionGrantState(null, app1, permission)) + .isEqualTo(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED); + assertThat(dpm.getPermissionGrantState(null, app2, permission)) + .isEqualTo(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT); // A regular app cannot retrieve permission grant state. mContext.binder.callingUid = setupPackageInPackageManager(app1, 1); @@ -4781,12 +4951,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_UID; mContext.packageName = admin1.getPackageName(); setAsProfileOwner(admin1); - assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED, - dpm.getPermissionGrantState(admin1, app1, permission)); - assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT, - dpm.getPermissionGrantState(admin1, app2, permission)); + assertThat(dpm.getPermissionGrantState(admin1, app1, permission)) + .isEqualTo(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED); + assertThat(dpm.getPermissionGrantState(admin1, app2, permission)) + .isEqualTo(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT); } + @Test public void testResetPasswordWithToken() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -4801,27 +4972,26 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().lockPatternUtils.addEscrowToken(eq(token), eq(UserHandle.USER_SYSTEM), nullable(EscrowTokenStateChangeCallback.class))) .thenReturn(handle); - assertTrue(dpm.setResetPasswordToken(admin1, token)); + assertThat(dpm.setResetPasswordToken(admin1, token)).isTrue(); // test password activation - when(getServices().lockPatternUtils.isEscrowTokenActive(eq(handle), eq(UserHandle.USER_SYSTEM))) - .thenReturn(true); - assertTrue(dpm.isResetPasswordTokenActive(admin1)); + when(getServices().lockPatternUtils.isEscrowTokenActive(handle, UserHandle.USER_SYSTEM)) + .thenReturn(true); + assertThat(dpm.isResetPasswordTokenActive(admin1)).isTrue(); // test reset password with token when(getServices().lockPatternUtils.setLockCredentialWithToken( - eq(LockscreenCredential.createPassword(password)), - eq(handle), eq(token), - eq(UserHandle.USER_SYSTEM))) - .thenReturn(true); - assertTrue(dpm.resetPasswordWithToken(admin1, password, token, 0)); + LockscreenCredential.createPassword(password), handle, token, + UserHandle.USER_SYSTEM)).thenReturn(true); + assertThat(dpm.resetPasswordWithToken(admin1, password, token, 0)).isTrue(); // test removing a token - when(getServices().lockPatternUtils.removeEscrowToken(eq(handle), eq(UserHandle.USER_SYSTEM))) + when(getServices().lockPatternUtils.removeEscrowToken(handle, UserHandle.USER_SYSTEM)) .thenReturn(true); - assertTrue(dpm.clearResetPasswordToken(admin1)); + assertThat(dpm.clearResetPasswordToken(admin1)).isTrue(); } + @Test public void testIsActivePasswordSufficient() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; mContext.packageName = admin1.getPackageName(); @@ -4841,11 +5011,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { PasswordMetrics passwordMetricsNoSymbols = computeForPassword("abcdXYZ5".getBytes()); setActivePasswordState(passwordMetricsNoSymbols); - assertTrue(dpm.isActivePasswordSufficient()); + assertThat(dpm.isActivePasswordSufficient()).isTrue(); initializeDpms(); reset(mContext.spiedContext); - assertTrue(dpm.isActivePasswordSufficient()); + assertThat(dpm.isActivePasswordSufficient()).isTrue(); // This call simulates the user entering the password for the first time after a reboot. // This causes password metrics to be reloaded into memory. Until this happens, @@ -4854,23 +5024,24 @@ public class DevicePolicyManagerTest extends DpmTestBase { // requirements. This is a known limitation of the current implementation of // isActivePasswordSufficient() - see b/34218769. setActivePasswordState(passwordMetricsNoSymbols); - assertTrue(dpm.isActivePasswordSufficient()); + assertThat(dpm.isActivePasswordSufficient()).isTrue(); dpm.setPasswordMinimumSymbols(admin1, 1); // This assertion would fail if we had not called setActivePasswordState() again after // initializeDpms() - see previous comment. - assertFalse(dpm.isActivePasswordSufficient()); + assertThat(dpm.isActivePasswordSufficient()).isFalse(); initializeDpms(); reset(mContext.spiedContext); - assertFalse(dpm.isActivePasswordSufficient()); + assertThat(dpm.isActivePasswordSufficient()).isFalse(); PasswordMetrics passwordMetricsWithSymbols = computeForPassword("abcd.XY5".getBytes()); setActivePasswordState(passwordMetricsWithSymbols); - assertTrue(dpm.isActivePasswordSufficient()); + assertThat(dpm.isActivePasswordSufficient()).isTrue(); } + @Test public void testIsActivePasswordSufficient_noLockScreen() throws Exception { // If there is no lock screen, the password is considered empty no matter what, because // it provides no security. @@ -4885,7 +5056,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(new PasswordMetrics(CREDENTIAL_TYPE_NONE)); // If no password requirements are set, isActivePasswordSufficient should succeed. - assertTrue(dpm.isActivePasswordSufficient()); + assertThat(dpm.isActivePasswordSufficient()).isTrue(); // Now set some password quality requirements. dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); @@ -4900,9 +5071,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { MockUtils.checkUserHandle(userHandle)); // The active (nonexistent) password doesn't comply with the requirements. - assertFalse(dpm.isActivePasswordSufficient()); + assertThat(dpm.isActivePasswordSufficient()).isFalse(); } + @Test public void testIsPasswordSufficientAfterProfileUnification() throws Exception { final int managedProfileUserId = CALLER_USER_HANDLE; final int managedProfileAdminUid = @@ -4921,13 +5093,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Numeric password is compliant with current requirement (QUALITY_NUMERIC set explicitly // on the parent admin) - assertTrue(dpm.isPasswordSufficientAfterProfileUnification(UserHandle.USER_SYSTEM, - UserHandle.USER_NULL)); + assertThat(dpm.isPasswordSufficientAfterProfileUnification(UserHandle.USER_SYSTEM, + UserHandle.USER_NULL)).isTrue(); // Numeric password is not compliant if profile is to be unified: the profile has a // QUALITY_ALPHABETIC policy on itself which will be enforced on the password after // unification. - assertFalse(dpm.isPasswordSufficientAfterProfileUnification(UserHandle.USER_SYSTEM, - managedProfileUserId)); + assertThat(dpm.isPasswordSufficientAfterProfileUnification(UserHandle.USER_SYSTEM, + managedProfileUserId)).isFalse(); } private void setActivePasswordState(PasswordMetrics passwordMetrics) @@ -4961,6 +5133,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.restoreCallingIdentity(ident); } + @Test public void testIsCurrentInputMethodSetByOwnerForDeviceOwner() throws Exception { final String currentIme = Settings.Secure.DEFAULT_INPUT_METHOD; final Uri currentImeUri = Settings.Secure.getUriFor(currentIme); @@ -4976,70 +5149,71 @@ public class DevicePolicyManagerTest extends DpmTestBase { // First and second user set IMEs manually. mContext.binder.callingUid = firstUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); mContext.binder.callingUid = secondUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); // Device owner changes IME for first user. mContext.binder.callingUid = deviceOwnerUid; - when(getServices().settings.settingsSecureGetStringForUser(currentIme, UserHandle.USER_SYSTEM)) - .thenReturn("ime1"); + when(getServices().settings.settingsSecureGetStringForUser(currentIme, + UserHandle.USER_SYSTEM)).thenReturn("ime1"); dpm.setSecureSetting(admin1, currentIme, "ime2"); verify(getServices().settings).settingsSecurePutStringForUser(currentIme, "ime2", UserHandle.USER_SYSTEM); reset(getServices().settings); dpms.notifyChangeToContentObserver(currentImeUri, UserHandle.USER_SYSTEM); mContext.binder.callingUid = firstUserSystemUid; - assertTrue(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isTrue(); mContext.binder.callingUid = secondUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); // Second user changes IME manually. dpms.notifyChangeToContentObserver(currentImeUri, CALLER_USER_HANDLE); mContext.binder.callingUid = firstUserSystemUid; - assertTrue(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isTrue(); mContext.binder.callingUid = secondUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); // First user changes IME manually. dpms.notifyChangeToContentObserver(currentImeUri, UserHandle.USER_SYSTEM); mContext.binder.callingUid = firstUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); mContext.binder.callingUid = secondUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); // Device owner changes IME for first user again. mContext.binder.callingUid = deviceOwnerUid; - when(getServices().settings.settingsSecureGetStringForUser(currentIme, UserHandle.USER_SYSTEM)) - .thenReturn("ime2"); + when(getServices().settings.settingsSecureGetStringForUser(currentIme, + UserHandle.USER_SYSTEM)).thenReturn("ime2"); dpm.setSecureSetting(admin1, currentIme, "ime3"); verify(getServices().settings).settingsSecurePutStringForUser(currentIme, "ime3", UserHandle.USER_SYSTEM); dpms.notifyChangeToContentObserver(currentImeUri, UserHandle.USER_SYSTEM); mContext.binder.callingUid = firstUserSystemUid; - assertTrue(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isTrue(); mContext.binder.callingUid = secondUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); // Restarting the DPMS should not lose information. initializeDpms(); mContext.binder.callingUid = firstUserSystemUid; - assertTrue(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isTrue(); mContext.binder.callingUid = secondUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); // Device owner can find out whether it set the current IME itself. mContext.binder.callingUid = deviceOwnerUid; - assertTrue(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isTrue(); // Removing the device owner should clear the information that it set the current IME. clearDeviceOwner(); mContext.binder.callingUid = firstUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); mContext.binder.callingUid = secondUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); } + @Test public void testIsCurrentInputMethodSetByOwnerForProfileOwner() throws Exception { final String currentIme = Settings.Secure.DEFAULT_INPUT_METHOD; final Uri currentImeUri = Settings.Secure.getUriFor(currentIme); @@ -5055,9 +5229,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { // First and second user set IMEs manually. mContext.binder.callingUid = firstUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); mContext.binder.callingUid = secondUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); // Profile owner changes IME for second user. mContext.binder.callingUid = profileOwnerUid; @@ -5069,23 +5243,23 @@ public class DevicePolicyManagerTest extends DpmTestBase { reset(getServices().settings); dpms.notifyChangeToContentObserver(currentImeUri, CALLER_USER_HANDLE); mContext.binder.callingUid = firstUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); mContext.binder.callingUid = secondUserSystemUid; - assertTrue(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isTrue(); // First user changes IME manually. dpms.notifyChangeToContentObserver(currentImeUri, UserHandle.USER_SYSTEM); mContext.binder.callingUid = firstUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); mContext.binder.callingUid = secondUserSystemUid; - assertTrue(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isTrue(); // Second user changes IME manually. dpms.notifyChangeToContentObserver(currentImeUri, CALLER_USER_HANDLE); mContext.binder.callingUid = firstUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); mContext.binder.callingUid = secondUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); // Profile owner changes IME for second user again. mContext.binder.callingUid = profileOwnerUid; @@ -5096,29 +5270,30 @@ public class DevicePolicyManagerTest extends DpmTestBase { CALLER_USER_HANDLE); dpms.notifyChangeToContentObserver(currentImeUri, CALLER_USER_HANDLE); mContext.binder.callingUid = firstUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); mContext.binder.callingUid = secondUserSystemUid; - assertTrue(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isTrue(); // Restarting the DPMS should not lose information. initializeDpms(); mContext.binder.callingUid = firstUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); mContext.binder.callingUid = secondUserSystemUid; - assertTrue(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isTrue(); // Profile owner can find out whether it set the current IME itself. mContext.binder.callingUid = profileOwnerUid; - assertTrue(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isTrue(); // Removing the profile owner should clear the information that it set the current IME. dpm.clearProfileOwner(admin1); mContext.binder.callingUid = firstUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); mContext.binder.callingUid = secondUserSystemUid; - assertFalse(dpm.isCurrentInputMethodSetByOwner()); + assertThat(dpm.isCurrentInputMethodSetByOwner()).isFalse(); } + @Test public void testSetPermittedCrossProfileNotificationListeners_unavailableForDo() throws Exception { // Set up a device owner. @@ -5127,6 +5302,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertSetPermittedCrossProfileNotificationListenersUnavailable(mContext.binder.callingUid); } + @Test public void testSetPermittedCrossProfileNotificationListeners_unavailableForPoOnUser() throws Exception { // Set up a profile owner. @@ -5141,23 +5317,24 @@ public class DevicePolicyManagerTest extends DpmTestBase { final int userId = UserHandle.getUserId(adminUid); final String packageName = "some.package"; - assertFalse(dpms.setPermittedCrossProfileNotificationListeners( - admin1, Collections.singletonList(packageName))); - assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1)); + assertThat(dpms.setPermittedCrossProfileNotificationListeners( + admin1, Collections.singletonList(packageName))).isFalse(); + assertThat(dpms.getPermittedCrossProfileNotificationListeners(admin1)).isNull(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertTrue(dpms.isNotificationListenerServicePermitted(packageName, userId)); + assertThat(dpms.isNotificationListenerServicePermitted(packageName, userId)).isTrue(); // Attempt to set to empty list (which means no listener is allowlisted) mContext.binder.callingUid = adminUid; - assertFalse(dpms.setPermittedCrossProfileNotificationListeners( - admin1, Collections.emptyList())); - assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1)); + assertThat(dpms.setPermittedCrossProfileNotificationListeners( + admin1, Collections.emptyList())).isFalse(); + assertThat(dpms.getPermittedCrossProfileNotificationListeners(admin1)).isNull(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertTrue(dpms.isNotificationListenerServicePermitted(packageName, userId)); + assertThat(dpms.isNotificationListenerServicePermitted(packageName, userId)).isTrue(); } + @Test public void testIsNotificationListenerServicePermitted_onlySystemCanCall() throws Exception { // Set up a managed profile final int MANAGED_PROFILE_USER_ID = 15; @@ -5171,8 +5348,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { UserHandle.USER_SYSTEM, // We check the packageInfo from the primary user. /*appId=*/ 12345, /*flags=*/ 0); - assertTrue(dpms.setPermittedCrossProfileNotificationListeners( - admin1, Collections.singletonList(permittedListener))); + assertThat(dpms.setPermittedCrossProfileNotificationListeners( + admin1, Collections.singletonList(permittedListener))).isTrue(); // isNotificationListenerServicePermitted should throw if not called from System. assertExpectException(SecurityException.class, /* messageRegex= */ null, @@ -5180,10 +5357,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { permittedListener, MANAGED_PROFILE_USER_ID)); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertTrue(dpms.isNotificationListenerServicePermitted( - permittedListener, MANAGED_PROFILE_USER_ID)); + assertThat(dpms.isNotificationListenerServicePermitted( + permittedListener, MANAGED_PROFILE_USER_ID)).isTrue(); } + @Test public void testSetPermittedCrossProfileNotificationListeners_managedProfile() throws Exception { // Set up a managed profile @@ -5212,63 +5390,64 @@ public class DevicePolicyManagerTest extends DpmTestBase { ++appId, ApplicationInfo.FLAG_SYSTEM); // By default all packages are allowed - assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1)); + assertThat(dpms.getPermittedCrossProfileNotificationListeners(admin1)).isNull(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertTrue(dpms.isNotificationListenerServicePermitted( - permittedListener, MANAGED_PROFILE_USER_ID)); - assertTrue(dpms.isNotificationListenerServicePermitted( - notPermittedListener, MANAGED_PROFILE_USER_ID)); - assertTrue(dpms.isNotificationListenerServicePermitted( - systemListener, MANAGED_PROFILE_USER_ID)); + assertThat(dpms.isNotificationListenerServicePermitted( + permittedListener, MANAGED_PROFILE_USER_ID)).isTrue(); + assertThat(dpms.isNotificationListenerServicePermitted( + notPermittedListener, MANAGED_PROFILE_USER_ID)).isTrue(); + assertThat(dpms.isNotificationListenerServicePermitted( + systemListener, MANAGED_PROFILE_USER_ID)).isTrue(); // Setting only one package in the allowlist mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; - assertTrue(dpms.setPermittedCrossProfileNotificationListeners( - admin1, Collections.singletonList(permittedListener))); + assertThat(dpms.setPermittedCrossProfileNotificationListeners( + admin1, Collections.singletonList(permittedListener))).isTrue(); final List<String> permittedListeners = dpms.getPermittedCrossProfileNotificationListeners(admin1); - assertEquals(1, permittedListeners.size()); - assertEquals(permittedListener, permittedListeners.get(0)); + assertThat(permittedListeners.size()).isEqualTo(1); + assertThat(permittedListeners.get(0)).isEqualTo(permittedListener); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertTrue(dpms.isNotificationListenerServicePermitted( - permittedListener, MANAGED_PROFILE_USER_ID)); - assertFalse(dpms.isNotificationListenerServicePermitted( - notPermittedListener, MANAGED_PROFILE_USER_ID)); + assertThat(dpms.isNotificationListenerServicePermitted( + permittedListener, MANAGED_PROFILE_USER_ID)).isTrue(); + assertThat(dpms.isNotificationListenerServicePermitted( + notPermittedListener, MANAGED_PROFILE_USER_ID)).isFalse(); // System packages are always allowed (even if not in the allowlist) - assertTrue(dpms.isNotificationListenerServicePermitted( - systemListener, MANAGED_PROFILE_USER_ID)); + assertThat(dpms.isNotificationListenerServicePermitted( + systemListener, MANAGED_PROFILE_USER_ID)).isTrue(); // Setting an empty allowlist - only system listeners allowed mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; - assertTrue(dpms.setPermittedCrossProfileNotificationListeners( - admin1, Collections.emptyList())); - assertEquals(0, dpms.getPermittedCrossProfileNotificationListeners(admin1).size()); + assertThat(dpms.setPermittedCrossProfileNotificationListeners( + admin1, Collections.emptyList())).isTrue(); + assertThat(dpms.getPermittedCrossProfileNotificationListeners(admin1).size()).isEqualTo(0); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertFalse(dpms.isNotificationListenerServicePermitted( - permittedListener, MANAGED_PROFILE_USER_ID)); - assertFalse(dpms.isNotificationListenerServicePermitted( - notPermittedListener, MANAGED_PROFILE_USER_ID)); + assertThat(dpms.isNotificationListenerServicePermitted( + permittedListener, MANAGED_PROFILE_USER_ID)).isFalse(); + assertThat(dpms.isNotificationListenerServicePermitted( + notPermittedListener, MANAGED_PROFILE_USER_ID)).isFalse(); // System packages are always allowed (even if not in the allowlist) - assertTrue(dpms.isNotificationListenerServicePermitted( - systemListener, MANAGED_PROFILE_USER_ID)); + assertThat(dpms.isNotificationListenerServicePermitted( + systemListener, MANAGED_PROFILE_USER_ID)).isTrue(); // Setting a null allowlist - all listeners allowed mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; - assertTrue(dpms.setPermittedCrossProfileNotificationListeners(admin1, null)); - assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1)); + assertThat(dpms.setPermittedCrossProfileNotificationListeners(admin1, null)).isTrue(); + assertThat(dpms.getPermittedCrossProfileNotificationListeners(admin1)).isNull(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertTrue(dpms.isNotificationListenerServicePermitted( - permittedListener, MANAGED_PROFILE_USER_ID)); - assertTrue(dpms.isNotificationListenerServicePermitted( - notPermittedListener, MANAGED_PROFILE_USER_ID)); - assertTrue(dpms.isNotificationListenerServicePermitted( - systemListener, MANAGED_PROFILE_USER_ID)); + assertThat(dpms.isNotificationListenerServicePermitted( + permittedListener, MANAGED_PROFILE_USER_ID)).isTrue(); + assertThat(dpms.isNotificationListenerServicePermitted( + notPermittedListener, MANAGED_PROFILE_USER_ID)).isTrue(); + assertThat(dpms.isNotificationListenerServicePermitted( + systemListener, MANAGED_PROFILE_USER_ID)).isTrue(); } + @Test public void testSetPermittedCrossProfileNotificationListeners_doesNotAffectPrimaryProfile() throws Exception { // Set up a managed profile @@ -5291,36 +5470,37 @@ public class DevicePolicyManagerTest extends DpmTestBase { ++appId, ApplicationInfo.FLAG_SYSTEM); // By default all packages are allowed (for all profiles) - assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1)); + assertThat(dpms.getPermittedCrossProfileNotificationListeners(admin1)).isNull(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertTrue(dpms.isNotificationListenerServicePermitted( - nonSystemPackage, MANAGED_PROFILE_USER_ID)); - assertTrue(dpms.isNotificationListenerServicePermitted( - systemListener, MANAGED_PROFILE_USER_ID)); - assertTrue(dpms.isNotificationListenerServicePermitted( - nonSystemPackage, UserHandle.USER_SYSTEM)); - assertTrue(dpms.isNotificationListenerServicePermitted( - systemListener, UserHandle.USER_SYSTEM)); + assertThat(dpms.isNotificationListenerServicePermitted( + nonSystemPackage, MANAGED_PROFILE_USER_ID)).isTrue(); + assertThat(dpms.isNotificationListenerServicePermitted( + systemListener, MANAGED_PROFILE_USER_ID)).isTrue(); + assertThat(dpms.isNotificationListenerServicePermitted( + nonSystemPackage, UserHandle.USER_SYSTEM)).isTrue(); + assertThat(dpms.isNotificationListenerServicePermitted( + systemListener, UserHandle.USER_SYSTEM)).isTrue(); // Setting an empty allowlist - only system listeners allowed in managed profile, but // all allowed in primary profile mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; - assertTrue(dpms.setPermittedCrossProfileNotificationListeners( - admin1, Collections.emptyList())); - assertEquals(0, dpms.getPermittedCrossProfileNotificationListeners(admin1).size()); + assertThat(dpms.setPermittedCrossProfileNotificationListeners( + admin1, Collections.emptyList())).isTrue(); + assertThat(dpms.getPermittedCrossProfileNotificationListeners(admin1).size()).isEqualTo(0); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertFalse(dpms.isNotificationListenerServicePermitted( - nonSystemPackage, MANAGED_PROFILE_USER_ID)); - assertTrue(dpms.isNotificationListenerServicePermitted( - systemListener, MANAGED_PROFILE_USER_ID)); - assertTrue(dpms.isNotificationListenerServicePermitted( - nonSystemPackage, UserHandle.USER_SYSTEM)); - assertTrue(dpms.isNotificationListenerServicePermitted( - systemListener, UserHandle.USER_SYSTEM)); + assertThat(dpms.isNotificationListenerServicePermitted( + nonSystemPackage, MANAGED_PROFILE_USER_ID)).isFalse(); + assertThat(dpms.isNotificationListenerServicePermitted( + systemListener, MANAGED_PROFILE_USER_ID)).isTrue(); + assertThat(dpms.isNotificationListenerServicePermitted( + nonSystemPackage, UserHandle.USER_SYSTEM)).isTrue(); + assertThat(dpms.isNotificationListenerServicePermitted( + systemListener, UserHandle.USER_SYSTEM)).isTrue(); } + @Test public void testGetOwnerInstalledCaCertsForDeviceOwner() throws Exception { mServiceContext.packageName = mRealTestContext.getPackageName(); mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID; @@ -5330,6 +5510,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verifyCanGetOwnerInstalledCaCerts(admin1, mAdmin1Context); } + @Test public void testGetOwnerInstalledCaCertsForProfileOwner() throws Exception { mServiceContext.packageName = mRealTestContext.getPackageName(); mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID; @@ -5340,6 +5521,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(admin1, mAdmin1Context); } + @Test public void testGetOwnerInstalledCaCertsForDelegate() throws Exception { mServiceContext.packageName = mRealTestContext.getPackageName(); mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID; @@ -5359,6 +5541,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null, caller); } + @Test public void testDisallowSharingIntoProfileSetRestriction() { when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package)) .thenReturn("com.android.managedprovisioning"); @@ -5371,6 +5554,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verifyDataSharingChangedBroadcast(); } + @Test public void testDisallowSharingIntoProfileClearRestriction() { when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package)) .thenReturn("com.android.managedprovisioning"); @@ -5383,6 +5567,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { verifyDataSharingChangedBroadcast(); } + @Test public void testDisallowSharingIntoProfileUnchanged() { RestrictionsListener listener = new RestrictionsListener(mContext); listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), new Bundle()); @@ -5399,6 +5584,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)); } + @Test public void testOverrideApnAPIsFailWithPO() throws Exception { setupProfileOwner(); ApnSetting apn = (new ApnSetting.Builder()) @@ -5446,13 +5632,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { runAsCaller(callerContext, dpms, (dpm) -> { when(getServices().keyChainConnection.getService().installCaCertificate(caCert)) .thenReturn(alias); - assertTrue(dpm.installCaCert(caller, caCert)); + assertThat(dpm.installCaCert(caller, caCert)).isTrue(); when(getServices().keyChainConnection.getService().getUserCaAliases()) .thenReturn(asSlice(new String[] {alias})); }); - getServices().injectBroadcast(mServiceContext, new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) - .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()), + getServices().injectBroadcast(mServiceContext, + new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) + .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()), callerUser.getIdentifier()); flushTasks(dpms); @@ -5461,25 +5648,27 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Device Owner / Profile Owner can find out which CA certs were installed by itself. runAsCaller(admin1Context, dpms, (dpm) -> { final List<String> installedCaCerts = dpm.getOwnerInstalledCaCerts(callerUser); - assertEquals(Collections.singletonList(alias), installedCaCerts); + assertThat(installedCaCerts).isEqualTo(Collections.singletonList(alias)); ownerInstalledCaCerts.addAll(installedCaCerts); }); // Restarting the DPMS should not lose information. initializeDpms(); - runAsCaller(admin1Context, dpms, (dpm) -> - assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(callerUser))); + runAsCaller(admin1Context, dpms, + (dpm) -> assertThat(dpm.getOwnerInstalledCaCerts(callerUser)) + .isEqualTo(ownerInstalledCaCerts)); // System can find out which CA certs were installed by the Device Owner / Profile Owner. runAsCaller(serviceContext, dpms, (dpm) -> { - assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(callerUser)); + assertThat(dpm.getOwnerInstalledCaCerts(callerUser)).isEqualTo(ownerInstalledCaCerts); // Remove the CA cert. reset(getServices().keyChainConnection.getService()); }); - getServices().injectBroadcast(mServiceContext, new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) - .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()), + getServices().injectBroadcast(mServiceContext, + new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) + .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()), callerUser.getIdentifier()); flushTasks(dpms); @@ -5516,14 +5705,15 @@ public class DevicePolicyManagerTest extends DpmTestBase { runAsCaller(callerContext, dpms, (dpm) -> { when(getServices().keyChainConnection.getService().installCaCertificate(caCert)) .thenReturn(alias); - assertTrue(dpm.installCaCert(callerName, caCert)); + assertThat(dpm.installCaCert(callerName, caCert)).isTrue(); }); // Fake the CA cert as having been installed when(getServices().keyChainConnection.getService().getUserCaAliases()) .thenReturn(asSlice(new String[] {alias})); - getServices().injectBroadcast(mServiceContext, new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) - .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()), + getServices().injectBroadcast(mServiceContext, + new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) + .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()), callerUser.getIdentifier()); flushTasks(dpms); @@ -5532,8 +5722,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { runAsCaller(serviceContext, dpms, (dpm) -> { final List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(callerUser); - assertNotNull(ownerInstalledCaCerts); - assertTrue(ownerInstalledCaCerts.isEmpty()); + assertThat(ownerInstalledCaCerts).isNotNull(); + assertThat(ownerInstalledCaCerts.isEmpty()).isTrue(); }); } @@ -5541,9 +5731,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { int[] gotFlags = DevicePolicyManagerService.translateIdAttestationFlags(attestationFlags); Arrays.sort(gotFlags); Arrays.sort(expectedFlags); - assertTrue(Arrays.equals(expectedFlags, gotFlags)); + assertThat(Arrays.equals(expectedFlags, gotFlags)).isTrue(); } + @Test public void testTranslationOfIdAttestationFlag() { int[] allIdTypes = new int[]{ID_TYPE_SERIAL, ID_TYPE_IMEI, ID_TYPE_MEID}; int[] correspondingAttUtilsTypes = new int[]{ @@ -5551,7 +5742,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { AttestationUtils.ID_TYPE_MEID}; // Test translation of zero flags - assertNull(DevicePolicyManagerService.translateIdAttestationFlags(0)); + assertThat(DevicePolicyManagerService.translateIdAttestationFlags(0)).isNull(); // Test translation of the ID_TYPE_BASE_INFO flag, which should yield an empty, but // non-null array @@ -5578,39 +5769,41 @@ public class DevicePolicyManagerTest extends DpmTestBase { AttestationUtils.ID_TYPE_MEID}); } + @Test public void testRevertDeviceOwnership_noMetadataFile() throws Exception { setDeviceOwner(); initializeDpms(); - assertFalse(getMockTransferMetadataManager().metadataFileExists()); - assertTrue(dpms.isDeviceOwner(admin1, UserHandle.USER_SYSTEM)); - assertTrue(dpms.isAdminActive(admin1, UserHandle.USER_SYSTEM)); - } - - // @FlakyTest(bugId = 148934649) - // public void testRevertDeviceOwnership_adminAndDeviceMigrated() throws Exception { - // DpmTestUtils.writeInputStreamToFile( - // getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated), - // getDeviceOwnerPoliciesFile()); - // DpmTestUtils.writeInputStreamToFile( - // getRawStream(com.android.frameworks.servicestests.R.raw.device_owner_migrated), - // getDeviceOwnerFile()); - // assertDeviceOwnershipRevertedWithFakeTransferMetadata(); - // } - - // @FlakyTest(bugId = 148934649) - // public void testRevertDeviceOwnership_deviceNotMigrated() - // throws Exception { - // DpmTestUtils.writeInputStreamToFile( - // getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated), - // getDeviceOwnerPoliciesFile()); - // DpmTestUtils.writeInputStreamToFile( - // getRawStream(com.android.frameworks.servicestests.R.raw.device_owner_not_migrated), - // getDeviceOwnerFile()); - // assertDeviceOwnershipRevertedWithFakeTransferMetadata(); - // } - - public void testRevertDeviceOwnership_adminAndDeviceNotMigrated() - throws Exception { + assertThat(getMockTransferMetadataManager().metadataFileExists()).isFalse(); + assertThat(dpms.isDeviceOwner(admin1, UserHandle.USER_SYSTEM)).isTrue(); + assertThat(dpms.isAdminActive(admin1, UserHandle.USER_SYSTEM)).isTrue(); + } + + @FlakyTest(bugId = 148934649) + @Test + public void testRevertDeviceOwnership_adminAndDeviceMigrated() throws Exception { + DpmTestUtils.writeInputStreamToFile( + getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated), + getDeviceOwnerPoliciesFile()); + DpmTestUtils.writeInputStreamToFile( + getRawStream(com.android.frameworks.servicestests.R.raw.device_owner_migrated), + getDeviceOwnerFile()); + assertDeviceOwnershipRevertedWithFakeTransferMetadata(); + } + + @FlakyTest(bugId = 148934649) + @Test + public void testRevertDeviceOwnership_deviceNotMigrated() throws Exception { + DpmTestUtils.writeInputStreamToFile( + getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated), + getDeviceOwnerPoliciesFile()); + DpmTestUtils.writeInputStreamToFile( + getRawStream(com.android.frameworks.servicestests.R.raw.device_owner_not_migrated), + getDeviceOwnerFile()); + assertDeviceOwnershipRevertedWithFakeTransferMetadata(); + } + + @Test + public void testRevertDeviceOwnership_adminAndDeviceNotMigrated() throws Exception { DpmTestUtils.writeInputStreamToFile( getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_not_migrated), getDeviceOwnerPoliciesFile()); @@ -5620,40 +5813,44 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertDeviceOwnershipRevertedWithFakeTransferMetadata(); } + @Test public void testRevertProfileOwnership_noMetadataFile() throws Exception { setupProfileOwner(); initializeDpms(); - assertFalse(getMockTransferMetadataManager().metadataFileExists()); - assertTrue(dpms.isProfileOwner(admin1, CALLER_USER_HANDLE)); - assertTrue(dpms.isAdminActive(admin1, CALLER_USER_HANDLE)); - } - - // @FlakyTest(bugId = 148934649) - // public void testRevertProfileOwnership_adminAndProfileMigrated() throws Exception { - // getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0, - // UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); - // DpmTestUtils.writeInputStreamToFile( - // getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated), - // getProfileOwnerPoliciesFile()); - // DpmTestUtils.writeInputStreamToFile( - // getRawStream(com.android.frameworks.servicestests.R.raw.profile_owner_migrated), - // getProfileOwnerFile()); - // assertProfileOwnershipRevertedWithFakeTransferMetadata(); - // } - - // @FlakyTest(bugId = 148934649) - // public void testRevertProfileOwnership_profileNotMigrated() throws Exception { - // getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0, - // UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); - // DpmTestUtils.writeInputStreamToFile( - // getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated), - // getProfileOwnerPoliciesFile()); - // DpmTestUtils.writeInputStreamToFile( - // getRawStream(com.android.frameworks.servicestests.R.raw.profile_owner_not_migrated), - // getProfileOwnerFile()); - // assertProfileOwnershipRevertedWithFakeTransferMetadata(); - // } + assertThat(getMockTransferMetadataManager().metadataFileExists()).isFalse(); + assertThat(dpms.isProfileOwner(admin1, CALLER_USER_HANDLE)).isTrue(); + assertThat(dpms.isAdminActive(admin1, CALLER_USER_HANDLE)).isTrue(); + } + + @FlakyTest(bugId = 148934649) + @Test + public void testRevertProfileOwnership_adminAndProfileMigrated() throws Exception { + getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0, + UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); + DpmTestUtils.writeInputStreamToFile( + getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated), + getProfileOwnerPoliciesFile()); + DpmTestUtils.writeInputStreamToFile( + getRawStream(com.android.frameworks.servicestests.R.raw.profile_owner_migrated), + getProfileOwnerFile()); + assertProfileOwnershipRevertedWithFakeTransferMetadata(); + } + @FlakyTest(bugId = 148934649) + @Test + public void testRevertProfileOwnership_profileNotMigrated() throws Exception { + getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0, + UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); + DpmTestUtils.writeInputStreamToFile( + getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated), + getProfileOwnerPoliciesFile()); + DpmTestUtils.writeInputStreamToFile( + getRawStream(com.android.frameworks.servicestests.R.raw.profile_owner_not_migrated), + getProfileOwnerFile()); + assertProfileOwnershipRevertedWithFakeTransferMetadata(); + } + + @Test public void testRevertProfileOwnership_adminAndProfileNotMigrated() throws Exception { getServices().addUser(CALLER_USER_HANDLE, 0, UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); @@ -5666,6 +5863,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertProfileOwnershipRevertedWithFakeTransferMetadata(); } + @Test public void testGrantDeviceIdsAccess_notToProfileOwner() throws Exception { setupProfileOwner(); configureContextForAccess(mContext, false); @@ -5674,6 +5872,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.markProfileOwnerOnOrganizationOwnedDevice(admin2)); } + @Test public void testGrantDeviceIdsAccess_notByAuthorizedCaller() throws Exception { setupProfileOwner(); configureContextForAccess(mContext, false); @@ -5682,6 +5881,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.markProfileOwnerOnOrganizationOwnedDevice(admin1)); } + @Test public void testGrantDeviceIdsAccess_byAuthorizedSystemCaller() throws Exception { setupProfileOwner(); @@ -5700,6 +5900,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(UserHandle.SYSTEM); } + @Test public void testGrantDeviceIdsAccess_byAuthorizedManagedProvisioning() throws Exception { setupProfileOwner(); @@ -5719,6 +5920,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } } + @Test public void testEnforceCallerCanRequestDeviceIdAttestation_deviceOwnerCaller() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; @@ -5741,6 +5943,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpms.getCallerIdentity(admin2))); } + @Test public void testEnforceCallerCanRequestDeviceIdAttestation_profileOwnerCaller() throws Exception { configureContextForAccess(mContext, false); @@ -5781,6 +5984,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } } + @Test public void testEnforceCallerCanRequestDeviceIdAttestation_delegateCaller() throws Exception { setupProfileOwner(); markDelegatedCertInstallerAsInstalled(); @@ -5800,6 +6004,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpms.getCallerIdentity(null, DpmMockContext.DELEGATE_PACKAGE_NAME))); } + @Test public void testEnforceCallerCanRequestDeviceIdAttestation_delegateCallerWithoutPermissions() throws Exception { setupProfileOwner(); @@ -5821,6 +6026,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { }); } + @Test public void testGetPasswordComplexity_securityExceptionNotThrownForParentInstance() { mContext.binder.callingUid = DpmMockContext.CALLER_UID; when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn( @@ -5830,9 +6036,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { parentDpm.getPasswordComplexity(); - assertEquals(PASSWORD_COMPLEXITY_NONE, dpm.getPasswordComplexity()); + assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_NONE); } + @Test public void testGetPasswordComplexity_illegalStateExceptionIfLocked() { mContext.binder.callingUid = DpmMockContext.CALLER_UID; when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn( @@ -5841,6 +6048,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThrows(IllegalStateException.class, () -> dpm.getPasswordComplexity()); } + @Test public void testGetPasswordComplexity_securityExceptionWithoutPermissions() { mContext.binder.callingUid = DpmMockContext.CALLER_UID; when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn( @@ -5850,6 +6058,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } + @Test public void testGetPasswordComplexity_currentUserNoPassword() { mContext.binder.callingUid = DpmMockContext.CALLER_UID; when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn( @@ -5859,9 +6068,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().userManager.getCredentialOwnerProfile(CALLER_USER_HANDLE)) .thenReturn(CALLER_USER_HANDLE); - assertEquals(PASSWORD_COMPLEXITY_NONE, dpm.getPasswordComplexity()); + assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_NONE); } + @Test public void testGetPasswordComplexity_currentUserHasPassword() { mContext.binder.callingUid = DpmMockContext.CALLER_UID; when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn( @@ -5874,9 +6084,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { .getUserPasswordMetrics(CALLER_USER_HANDLE)) .thenReturn(computeForPassword("asdf".getBytes())); - assertEquals(PASSWORD_COMPLEXITY_MEDIUM, dpm.getPasswordComplexity()); + assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_MEDIUM); } + @Test public void testGetPasswordComplexity_unifiedChallengeReturnsParentUserPassword() { mContext.binder.callingUid = DpmMockContext.CALLER_UID; when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn( @@ -5896,23 +6107,25 @@ public class DevicePolicyManagerTest extends DpmTestBase { .getUserPasswordMetrics(parentUser.id)) .thenReturn(computeForPassword("parentUser".getBytes())); - assertEquals(PASSWORD_COMPLEXITY_HIGH, dpm.getPasswordComplexity()); + assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_HIGH); } + @Test public void testCrossProfileCalendarPackages_initiallyEmpty() { setAsProfileOwner(admin1); final Set<String> packages = dpm.getCrossProfileCalendarPackages(admin1); assertCrossProfileCalendarPackagesEqual(packages, Collections.emptySet()); } + @Test public void testCrossProfileCalendarPackages_reopenDpms() { setAsProfileOwner(admin1); dpm.setCrossProfileCalendarPackages(admin1, null); Set<String> packages = dpm.getCrossProfileCalendarPackages(admin1); - assertTrue(packages == null); + assertThat(packages == null).isTrue(); initializeDpms(); packages = dpm.getCrossProfileCalendarPackages(admin1); - assertTrue(packages == null); + assertThat(packages == null).isTrue(); dpm.setCrossProfileCalendarPackages(admin1, Collections.emptySet()); packages = dpm.getCrossProfileCalendarPackages(admin1); @@ -5932,21 +6145,22 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void assertCrossProfileCalendarPackagesEqual(Set<String> expected, Set<String> actual) { - assertTrue(expected != null); - assertTrue(actual != null); - assertTrue(expected.containsAll(actual)); - assertTrue(actual.containsAll(expected)); + assertThat(expected).isNotNull(); + assertThat(actual).isNotNull(); + assertThat(actual).containsExactlyElementsIn(expected); } + @Test public void testIsPackageAllowedToAccessCalendar_adminNotAllowed() { setAsProfileOwner(admin1); dpm.setCrossProfileCalendarPackages(admin1, Collections.emptySet()); when(getServices().settings.settingsSecureGetIntForUser( Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, 0, CALLER_USER_HANDLE)).thenReturn(1); - assertFalse(dpm.isPackageAllowedToAccessCalendar("TEST_PACKAGE")); + assertThat(dpm.isPackageAllowedToAccessCalendar("TEST_PACKAGE")).isFalse(); } + @Test public void testIsPackageAllowedToAccessCalendar_settingOff() { final String testPackage = "TEST_PACKAGE"; setAsProfileOwner(admin1); @@ -5954,9 +6168,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().settings.settingsSecureGetIntForUser( Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, 0, CALLER_USER_HANDLE)).thenReturn(0); - assertFalse(dpm.isPackageAllowedToAccessCalendar(testPackage)); + assertThat(dpm.isPackageAllowedToAccessCalendar(testPackage)).isFalse(); } + @Test public void testIsPackageAllowedToAccessCalendar_bothAllowed() { final String testPackage = "TEST_PACKAGE"; setAsProfileOwner(admin1); @@ -5964,9 +6179,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().settings.settingsSecureGetIntForUser( Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, 0, CALLER_USER_HANDLE)).thenReturn(1); - assertTrue(dpm.isPackageAllowedToAccessCalendar(testPackage)); + assertThat(dpm.isPackageAllowedToAccessCalendar(testPackage)).isTrue(); } + @Test public void testSetUserControlDisabledPackages_asDO() throws Exception { final List<String> testPackages = new ArrayList<>(); testPackages.add("package_1"); @@ -5979,9 +6195,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { verify(getServices().packageManagerInternal).setDeviceOwnerProtectedPackages(testPackages); - assertEquals(testPackages, dpm.getUserControlDisabledPackages(admin1)); + assertThat(dpm.getUserControlDisabledPackages(admin1)).isEqualTo(testPackages); } + @Test public void testSetUserControlDisabledPackages_failingAsPO() throws Exception { final List<String> testPackages = new ArrayList<>(); testPackages.add("package_1"); @@ -6008,28 +6225,32 @@ public class DevicePolicyManagerTest extends DpmTestBase { mServiceContext.binder.restoreCallingIdentity(ident); } + @Test public void testGetCrossProfilePackages_notSet_returnsEmpty() { setAsProfileOwner(admin1); - assertTrue(dpm.getCrossProfilePackages(admin1).isEmpty()); + assertThat(dpm.getCrossProfilePackages(admin1).isEmpty()).isTrue(); } + @Test public void testGetCrossProfilePackages_notSet_dpmsReinitialized_returnsEmpty() { setAsProfileOwner(admin1); initializeDpms(); - assertTrue(dpm.getCrossProfilePackages(admin1).isEmpty()); + assertThat(dpm.getCrossProfilePackages(admin1).isEmpty()).isTrue(); } + @Test public void testGetCrossProfilePackages_whenSet_returnsEqual() { setAsProfileOwner(admin1); Set<String> packages = Collections.singleton("TEST_PACKAGE"); dpm.setCrossProfilePackages(admin1, packages); - assertEquals(packages, dpm.getCrossProfilePackages(admin1)); + assertThat(dpm.getCrossProfilePackages(admin1)).isEqualTo(packages); } + @Test public void testGetCrossProfilePackages_whenSet_dpmsReinitialized_returnsEqual() { setAsProfileOwner(admin1); Set<String> packages = Collections.singleton("TEST_PACKAGE"); @@ -6037,9 +6258,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setCrossProfilePackages(admin1, packages); initializeDpms(); - assertEquals(packages, dpm.getCrossProfilePackages(admin1)); + assertThat(dpm.getCrossProfilePackages(admin1)).isEqualTo(packages); } + @Test public void testGetAllCrossProfilePackages_notSet_returnsEmpty() throws Exception { addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1); mContext.packageName = admin1.getPackageName(); @@ -6047,9 +6269,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { setCrossProfileAppsList(); setVendorCrossProfileAppsList(); - assertTrue(dpm.getAllCrossProfilePackages().isEmpty()); + assertThat(dpm.getAllCrossProfilePackages().isEmpty()).isTrue(); } + @Test public void testGetAllCrossProfilePackages_notSet_dpmsReinitialized_returnsEmpty() throws Exception { addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1); @@ -6059,9 +6282,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { setVendorCrossProfileAppsList(); initializeDpms(); - assertTrue(dpm.getAllCrossProfilePackages().isEmpty()); + assertThat(dpm.getAllCrossProfilePackages().isEmpty()).isTrue(); } + @Test public void testGetAllCrossProfilePackages_whenSet_returnsCombinedSet() throws Exception { addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1); final Set<String> packages = Sets.newSet("TEST_PACKAGE", "TEST_COMMON_PACKAGE"); @@ -6071,13 +6295,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"); setVendorCrossProfileAppsList("TEST_VENDOR_DEFAULT_PACKAGE"); - assertEquals(Sets.newSet( - "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE", - "TEST_VENDOR_DEFAULT_PACKAGE"), - dpm.getAllCrossProfilePackages()); - + assertThat(dpm.getAllCrossProfilePackages()).containsExactly( + "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE", + "TEST_VENDOR_DEFAULT_PACKAGE"); } + @Test public void testGetAllCrossProfilePackages_whenSet_dpmsReinitialized_returnsCombinedSet() throws Exception { addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1); @@ -6089,12 +6312,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { setVendorCrossProfileAppsList("TEST_VENDOR_DEFAULT_PACKAGE"); initializeDpms(); - assertEquals(Sets.newSet( + assertThat(dpm.getAllCrossProfilePackages()).containsExactly( "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE", - "TEST_VENDOR_DEFAULT_PACKAGE"), - dpm.getAllCrossProfilePackages()); + "TEST_VENDOR_DEFAULT_PACKAGE"); } + @Test public void testGetDefaultCrossProfilePackages_noPackagesSet_returnsEmpty() { setCrossProfileAppsList(); setVendorCrossProfileAppsList(); @@ -6102,27 +6325,29 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(dpm.getDefaultCrossProfilePackages()).isEmpty(); } + @Test public void testGetDefaultCrossProfilePackages_packagesSet_returnsCombinedSet() { setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"); setVendorCrossProfileAppsList("TEST_VENDOR_DEFAULT_PACKAGE"); - assertThat(dpm.getDefaultCrossProfilePackages()).isEqualTo(Sets.newSet( - "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE", "TEST_VENDOR_DEFAULT_PACKAGE" - )); + assertThat(dpm.getDefaultCrossProfilePackages()).containsExactly( + "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE", "TEST_VENDOR_DEFAULT_PACKAGE"); } + @Test public void testSetCommonCriteriaMode_asDeviceOwner() throws Exception { setDeviceOwner(); - assertFalse(dpm.isCommonCriteriaModeEnabled(admin1)); - assertFalse(dpm.isCommonCriteriaModeEnabled(null)); + assertThat(dpm.isCommonCriteriaModeEnabled(admin1)).isFalse(); + assertThat(dpm.isCommonCriteriaModeEnabled(null)).isFalse(); dpm.setCommonCriteriaModeEnabled(admin1, true); - assertTrue(dpm.isCommonCriteriaModeEnabled(admin1)); - assertTrue(dpm.isCommonCriteriaModeEnabled(null)); + assertThat(dpm.isCommonCriteriaModeEnabled(admin1)).isTrue(); + assertThat(dpm.isCommonCriteriaModeEnabled(null)).isTrue(); } + @Test public void testSetCommonCriteriaMode_asPoOfOrgOwnedDevice() throws Exception { final int managedProfileUserId = 15; final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436); @@ -6130,15 +6355,16 @@ public class DevicePolicyManagerTest extends DpmTestBase { configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId); mContext.binder.callingUid = managedProfileAdminUid; - assertFalse(dpm.isCommonCriteriaModeEnabled(admin1)); - assertFalse(dpm.isCommonCriteriaModeEnabled(null)); + assertThat(dpm.isCommonCriteriaModeEnabled(admin1)).isFalse(); + assertThat(dpm.isCommonCriteriaModeEnabled(null)).isFalse(); dpm.setCommonCriteriaModeEnabled(admin1, true); - assertTrue(dpm.isCommonCriteriaModeEnabled(admin1)); - assertTrue(dpm.isCommonCriteriaModeEnabled(null)); + assertThat(dpm.isCommonCriteriaModeEnabled(admin1)).isTrue(); + assertThat(dpm.isCommonCriteriaModeEnabled(null)).isTrue(); } + @Test public void testCanProfileOwnerResetPasswordWhenLocked_nonDirectBootAwarePo() throws Exception { setDeviceEncryptionPerUser(); @@ -6146,30 +6372,33 @@ public class DevicePolicyManagerTest extends DpmTestBase { setupPasswordResetToken(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertFalse("po is not direct boot aware", - dpm.canProfileOwnerResetPasswordWhenLocked(CALLER_USER_HANDLE)); + assertWithMessage("po is not direct boot aware") + .that(dpm.canProfileOwnerResetPasswordWhenLocked(CALLER_USER_HANDLE)).isFalse(); } + @Test public void testCanProfileOwnerResetPasswordWhenLocked_noActiveToken() throws Exception { setDeviceEncryptionPerUser(); setupProfileOwner(); makeAdmin1DirectBootAware(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertFalse("po doesn't have an active password reset token", - dpm.canProfileOwnerResetPasswordWhenLocked(CALLER_USER_HANDLE)); + assertWithMessage("po doesn't have an active password reset token") + .that(dpm.canProfileOwnerResetPasswordWhenLocked(CALLER_USER_HANDLE)).isFalse(); } + @Test public void testCanProfileOwnerResetPasswordWhenLocked_nonFbeDevice() throws Exception { setupProfileOwner(); makeAdmin1DirectBootAware(); setupPasswordResetToken(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertFalse("device is not FBE", - dpm.canProfileOwnerResetPasswordWhenLocked(CALLER_USER_HANDLE)); + assertWithMessage("device is not FBE") + .that(dpm.canProfileOwnerResetPasswordWhenLocked(CALLER_USER_HANDLE)).isFalse(); } + @Test public void testCanProfileOwnerResetPasswordWhenLocked() throws Exception { setDeviceEncryptionPerUser(); setupProfileOwner(); @@ -6177,8 +6406,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { setupPasswordResetToken(); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; - assertTrue("direct boot aware po with active password reset token", - dpm.canProfileOwnerResetPasswordWhenLocked(CALLER_USER_HANDLE)); + assertWithMessage("direct boot aware po with active password reset token") + .that(dpm.canProfileOwnerResetPasswordWhenLocked(CALLER_USER_HANDLE)).isTrue(); } private void setupPasswordResetToken() { @@ -6196,7 +6425,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { .isEscrowTokenActive(eq(handle), eq(CALLER_USER_HANDLE))) .thenReturn(true); - assertTrue("failed to activate token", dpm.isResetPasswordTokenActive(admin1)); + assertWithMessage("failed to activate token").that(dpm.isResetPasswordTokenActive(admin1)) + .isTrue(); } private void makeAdmin1DirectBootAware() @@ -6231,6 +6461,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(packages); } + @Test public void testSetAccountTypesWithManagementDisabledOnManagedProfile() throws Exception { setupProfileOwner(); @@ -6249,6 +6480,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty(); } + @Test public void testSetAccountTypesWithManagementDisabledOnOrgOwnedManagedProfile() throws Exception { final int managedProfileUserId = 15; @@ -6284,6 +6516,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * Tests the case when the user doesn't turn the profile on in time, verifies that the user is * warned with a notification and then the apps get suspended. */ + @Test public void testMaximumProfileTimeOff_profileOffTimeExceeded() throws Exception { prepareMocksForSetMaximumProfileTimeOff(); @@ -6346,6 +6579,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /** * Tests the case when the user turns the profile back on long before the deadline (> 1 day). */ + @Test public void testMaximumProfileTimeOff_turnOnBeforeWarning() throws Exception { prepareMocksForSetMaximumProfileTimeOff(); @@ -6366,6 +6600,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /** * Tests the case when the user turns the profile back on after the warning notification. */ + @Test public void testMaximumProfileTimeOff_turnOnAfterWarning() throws Exception { prepareMocksForSetMaximumProfileTimeOff(); @@ -6395,6 +6630,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { /** * Tests the case when the user turns the profile back on when the apps are already suspended. */ + @Test public void testMaximumProfileTimeOff_turnOnAfterDeadline() throws Exception { prepareMocksForSetMaximumProfileTimeOff(); @@ -6482,7 +6718,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private static Matcher<Notification> hasExtra(String... extras) { - assertEquals("Odd number of extra key-values", 0, extras.length % 2); + assertWithMessage("Odd number of extra key-values").that(extras.length % 2).isEqualTo(0); return new BaseMatcher<Notification>() { @Override public boolean matches(Object item) { @@ -6520,17 +6756,17 @@ public class DevicePolicyManagerTest extends DpmTestBase { // To simulate a reboot, we just reinitialize dpms and call systemReady initializeDpms(); - assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName())); - assertFalse(dpm.isDeviceOwnerApp(adminAnotherPackage.getPackageName())); - assertFalse(dpm.isAdminActive(adminAnotherPackage)); - assertTrue(dpm.isAdminActive(admin1)); - assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser()); + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isTrue(); + assertThat(dpm.isDeviceOwnerApp(adminAnotherPackage.getPackageName())).isFalse(); + assertThat(dpm.isAdminActive(adminAnotherPackage)).isFalse(); + assertThat(dpm.isAdminActive(admin1)).isTrue(); + assertThat(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName())).isTrue(); + assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(admin1); - assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())); - assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser()); - assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId()); - assertFalse(getMockTransferMetadataManager().metadataFileExists()); + assertThat(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName())).isTrue(); + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); + assertThat(dpm.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); + assertThat(getMockTransferMetadataManager().metadataFileExists()).isFalse(); mServiceContext.binder.restoreCallingIdentity(ident); } @@ -6547,12 +6783,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { // To simulate a reboot, we just reinitialize dpms and call systemReady initializeDpms(); - assertTrue(dpm.isProfileOwnerApp(admin1.getPackageName())); - assertTrue(dpm.isAdminActive(admin1)); - assertFalse(dpm.isProfileOwnerApp(adminAnotherPackage.getPackageName())); - assertFalse(dpm.isAdminActive(adminAnotherPackage)); - assertEquals(dpm.getProfileOwnerAsUser(CALLER_USER_HANDLE), admin1); - assertFalse(getMockTransferMetadataManager().metadataFileExists()); + assertThat(dpm.isProfileOwnerApp(admin1.getPackageName())).isTrue(); + assertThat(dpm.isAdminActive(admin1)).isTrue(); + assertThat(dpm.isProfileOwnerApp(adminAnotherPackage.getPackageName())).isFalse(); + assertThat(dpm.isAdminActive(adminAnotherPackage)).isFalse(); + assertThat(admin1).isEqualTo(dpm.getProfileOwnerAsUser(CALLER_USER_HANDLE)); + assertThat(getMockTransferMetadataManager().metadataFileExists()).isFalse(); } private void writeFakeTransferMetadataFile(int callerUserHandle, String adminType) { @@ -6597,8 +6833,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void assertProvisioningAllowed(String action, boolean expected) { - assertEquals("isProvisioningAllowed(" + action + ") returning unexpected result", expected, - dpm.isProvisioningAllowed(action)); + assertWithMessage("isProvisioningAllowed(%s) returning unexpected result", action) + .that(dpm.isProvisioningAllowed(action)).isEqualTo(expected); } private void assertProvisioningAllowed(String action, boolean expected, String packageName, @@ -6622,9 +6858,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { private void assertCheckProvisioningPreCondition( String action, String packageName, int provisioningCondition) { - assertEquals("checkProvisioningPreCondition(" - + action + ", " + packageName + ") returning unexpected result", - provisioningCondition, dpm.checkProvisioningPreCondition(action, packageName)); + assertWithMessage("checkProvisioningPreCondition(%s, %s) returning unexpected result", + action, packageName).that(dpm.checkProvisioningPreCondition(action, packageName)) + .isEqualTo(provisioningCondition); } /** @@ -6642,7 +6878,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS); setUpPackageManagerForFakeAdmin(admin, adminUid, copyFromAdmin); dpm.setActiveAdmin(admin, false, userId); - assertTrue(dpm.setProfileOwner(admin, null, userId)); + assertThat(dpm.setProfileOwner(admin, null, userId)).isTrue(); mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java index 41d54e9010d3..81570a10fc13 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java @@ -16,6 +16,8 @@ package com.android.server.devicepolicy; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; @@ -35,18 +37,28 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.UserHandle; -import android.test.AndroidTestCase; + +import androidx.test.InstrumentationRegistry; + +import org.junit.Before; import java.io.InputStream; import java.util.List; -public abstract class DpmTestBase extends AndroidTestCase { +/** + * Temporary copy of DpmTestBase using JUnit 4 - once all tests extend it, it should be renamed + * back to DpmTestBase (with the temporary methods removed. + * + */ +public abstract class DpmTestBase { + public static final String TAG = "DpmTest"; - protected Context mRealTestContext; + protected final Context mRealTestContext = InstrumentationRegistry.getTargetContext(); protected DpmMockContext mMockContext; private MockSystemServices mServices; + // Attributes below are public so they don't need to be prefixed with m public ComponentName admin1; public ComponentName admin2; public ComponentName admin3; @@ -54,12 +66,8 @@ public abstract class DpmTestBase extends AndroidTestCase { public ComponentName adminNoPerm; public ComponentName delegateCertInstaller; - @Override - protected void setUp() throws Exception { - super.setUp(); - - mRealTestContext = super.getContext(); - + @Before + public void setFixtures() throws Exception { mServices = new MockSystemServices(mRealTestContext, "test-data"); mMockContext = new DpmMockContext(mServices, mRealTestContext); @@ -74,8 +82,7 @@ public abstract class DpmTestBase extends AndroidTestCase { mockSystemPropertiesToReturnDefault(); } - @Override - public DpmMockContext getContext() { + protected DpmMockContext getContext() { return mMockContext; } @@ -136,20 +143,15 @@ public abstract class DpmTestBase extends AndroidTestCase { final PackageInfo pi = DpmTestUtils.cloneParcelable( mRealTestContext.getPackageManager().getPackageInfo( mRealTestContext.getPackageName(), 0)); - assertTrue(pi.applicationInfo.flags != 0); + assertThat(pi.applicationInfo.flags).isNotEqualTo(0); if (ai != null) { pi.applicationInfo = ai; } - doReturn(pi).when(mServices.ipackageManager).getPackageInfo( - eq(packageName), - eq(0), - eq(userId)); + doReturn(pi).when(mServices.ipackageManager).getPackageInfo(packageName, 0, userId); - doReturn(ai.uid).when(mServices.packageManager).getPackageUidAsUser( - eq(packageName), - eq(userId)); + doReturn(ai.uid).when(mServices.packageManager).getPackageUidAsUser(packageName, userId); } protected void markDelegatedCertInstallerAsInstalled() throws Exception { @@ -230,8 +232,8 @@ public abstract class DpmTestBase extends AndroidTestCase { mRealTestContext.getPackageManager().queryBroadcastReceivers( resolveIntent, PackageManager.GET_META_DATA); - assertNotNull(realResolveInfo); - assertEquals(1, realResolveInfo.size()); + assertThat(realResolveInfo).isNotNull(); + assertThat(realResolveInfo).hasSize(1); // We need to change AI, so set a clone. realResolveInfo.set(0, DpmTestUtils.cloneParcelable(realResolveInfo.get(0))); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java index e8818a3f4940..3aa5a80d814f 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java @@ -16,8 +16,8 @@ package com.android.server.devicepolicy; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -100,7 +100,7 @@ public class FactoryResetProtectionPolicyTest { ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); XmlPullParser parser = Xml.newPullParser(); parser.setInput(new InputStreamReader(inStream)); - assertEquals(XmlPullParser.START_TAG, parser.next()); + assertThat(parser.next()).isEqualTo(XmlPullParser.START_TAG); assertPoliciesAreEqual(policy, policy.readFromXml(parser)); } @@ -114,7 +114,7 @@ public class FactoryResetProtectionPolicyTest { parser.setInput(new InputStreamReader(inStream)); // If deserialization fails, then null is returned. - assertNull(policy.readFromXml(parser)); + assertThat(policy.readFromXml(parser)).isNull(); } private ByteArrayOutputStream serialize(FactoryResetProtectionPolicy policy) @@ -133,17 +133,17 @@ public class FactoryResetProtectionPolicyTest { private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy, FactoryResetProtectionPolicy actualPolicy) { - assertEquals(expectedPolicy.isFactoryResetProtectionEnabled(), - actualPolicy.isFactoryResetProtectionEnabled()); + assertThat(actualPolicy.isFactoryResetProtectionEnabled()) + .isEqualTo(expectedPolicy.isFactoryResetProtectionEnabled()); assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(), actualPolicy.getFactoryResetProtectionAccounts()); } private void assertAccountsAreEqual(List<String> expectedAccounts, List<String> actualAccounts) { - assertEquals(expectedAccounts.size(), actualAccounts.size()); + assertThat(actualAccounts.size()).isEqualTo(expectedAccounts.size()); for (int i = 0; i < expectedAccounts.size(); i++) { - assertEquals(expectedAccounts.get(i), actualAccounts.get(i)); + assertThat(actualAccounts.get(i)).isEqualTo(expectedAccounts.get(i)); } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java index c698312eedec..7506dd45ad82 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java @@ -17,6 +17,9 @@ package com.android.server.devicepolicy; import static com.android.server.devicepolicy.NetworkLoggingHandler.LOG_NETWORK_EVENT_MSG; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; @@ -37,8 +40,9 @@ import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; import com.android.server.LocalServices; -import com.android.server.SystemService; +import org.junit.Before; +import org.junit.Test; import org.mockito.ArgumentCaptor; import java.util.List; @@ -50,9 +54,8 @@ public class NetworkEventTest extends DpmTestBase { private DpmMockContext mSpiedDpmMockContext; private DevicePolicyManagerServiceTestable mDpmTestable; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { mSpiedDpmMockContext = spy(mMockContext); mSpiedDpmMockContext.callerPermissions.add( android.Manifest.permission.MANAGE_DEVICE_ADMINS); @@ -64,6 +67,7 @@ public class NetworkEventTest extends DpmTestBase { mDpmTestable.setActiveAdmin(admin1, true, DpmMockContext.CALLER_USER_HANDLE); } + @Test public void testNetworkEventId_monotonicallyIncreasing() throws Exception { // GIVEN the handler has not processed any events. long startingId = 0; @@ -72,17 +76,20 @@ public class NetworkEventTest extends DpmTestBase { List<NetworkEvent> events = fillHandlerWithFullBatchOfEvents(startingId); // THEN the events are in a batch. - assertTrue("Batch not at the returned token.", - events != null && events.size() == MAX_EVENTS_PER_BATCH); + assertWithMessage("Batch not at the returned token.").that(events).isNotNull(); + assertWithMessage("Batch not at the returned token.").that(events) + .hasSize(MAX_EVENTS_PER_BATCH); + // THEN event ids are monotonically increasing. long expectedId = startingId; for (int i = 0; i < MAX_EVENTS_PER_BATCH; i++) { - assertEquals("At index " + i + ", the event has the wrong id.", expectedId, - events.get(i).getId()); + assertWithMessage("At index %s, the event has the wrong id.", i) + .that(events.get(i).getId()).isEqualTo(expectedId); expectedId++; } } + @Test public void testNetworkEventId_wrapsAround() throws Exception { // GIVEN the handler has almost processed Long.MAX_VALUE events. int gap = 5; @@ -92,24 +99,25 @@ public class NetworkEventTest extends DpmTestBase { List<NetworkEvent> events = fillHandlerWithFullBatchOfEvents(startingId); // THEN the events are in a batch. - assertTrue("Batch not at the returned token.", - events != null && events.size() == MAX_EVENTS_PER_BATCH); + assertWithMessage("Batch not at the returned token.").that(events).isNotNull(); + assertWithMessage("Batch not at the returned token.").that(events) + .hasSize(MAX_EVENTS_PER_BATCH); // THEN event ids are monotonically increasing. long expectedId = startingId; for (int i = 0; i < gap; i++) { - assertEquals("At index " + i + ", the event has the wrong id.", expectedId, - events.get(i).getId()); + assertWithMessage("At index %s, the event has the wrong id.", i) + .that(events.get(i).getId()).isEqualTo(expectedId); expectedId++; } // THEN event ids are reset when the id reaches the maximum possible value. - assertEquals("Event was not assigned the maximum id value.", Long.MAX_VALUE, - events.get(gap).getId()); - assertEquals("Event id was not reset.", 0, events.get(gap + 1).getId()); + assertWithMessage("Event was not assigned the maximum id value.") + .that(events.get(gap).getId()).isEqualTo(Long.MAX_VALUE); + assertWithMessage("Event id was not reset.").that(events.get(gap + 1).getId()).isEqualTo(0); // THEN event ids are monotonically increasing. expectedId = 0; for (int i = gap + 1; i < MAX_EVENTS_PER_BATCH; i++) { - assertEquals("At index " + i + ", the event has the wrong id.", expectedId, - events.get(i).getId()); + assertWithMessage("At index %s, the event has the wrong id.", i) + .that(events.get(i).getId()).isEqualTo(expectedId); expectedId++; } } @@ -134,8 +142,8 @@ public class NetworkEventTest extends DpmTestBase { ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mSpiedDpmMockContext).sendBroadcastAsUser(intentCaptor.capture(), any(UserHandle.class)); - assertEquals(intentCaptor.getValue().getAction(), - DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE); + assertThat(DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE) + .isEqualTo(intentCaptor.getValue().getAction()); long token = intentCaptor.getValue().getExtras().getLong( DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN, 0); return handler.retrieveFullLogBatch(token); @@ -144,6 +152,7 @@ public class NetworkEventTest extends DpmTestBase { /** * Test parceling and unparceling of a ConnectEvent. */ + @Test public void testConnectEventParceling() { ConnectEvent event = new ConnectEvent("127.0.0.1", 80, "com.android.whateverdude", 100000); event.setId(5L); @@ -152,16 +161,17 @@ public class NetworkEventTest extends DpmTestBase { p.setDataPosition(0); ConnectEvent unparceledEvent = p.readParcelable(NetworkEventTest.class.getClassLoader()); p.recycle(); - assertEquals(event.getInetAddress(), unparceledEvent.getInetAddress()); - assertEquals(event.getPort(), unparceledEvent.getPort()); - assertEquals(event.getPackageName(), unparceledEvent.getPackageName()); - assertEquals(event.getTimestamp(), unparceledEvent.getTimestamp()); - assertEquals(event.getId(), unparceledEvent.getId()); + assertThat(unparceledEvent.getInetAddress()).isEqualTo(event.getInetAddress()); + assertThat(unparceledEvent.getPort()).isEqualTo(event.getPort()); + assertThat(unparceledEvent.getPackageName()).isEqualTo(event.getPackageName()); + assertThat(unparceledEvent.getTimestamp()).isEqualTo(event.getTimestamp()); + assertThat(unparceledEvent.getId()).isEqualTo(event.getId()); } /** * Test parceling and unparceling of a DnsEvent. */ + @Test public void testDnsEventParceling() { DnsEvent event = new DnsEvent("d.android.com", new String[]{"192.168.0.1", "127.0.0.1"}, 2, "com.android.whateverdude", 100000); @@ -171,13 +181,15 @@ public class NetworkEventTest extends DpmTestBase { p.setDataPosition(0); DnsEvent unparceledEvent = p.readParcelable(NetworkEventTest.class.getClassLoader()); p.recycle(); - assertEquals(event.getHostname(), unparceledEvent.getHostname()); - assertEquals(event.getInetAddresses().get(0), unparceledEvent.getInetAddresses().get(0)); - assertEquals(event.getInetAddresses().get(1), unparceledEvent.getInetAddresses().get(1)); - assertEquals(event.getTotalResolvedAddressCount(), - unparceledEvent.getTotalResolvedAddressCount()); - assertEquals(event.getPackageName(), unparceledEvent.getPackageName()); - assertEquals(event.getTimestamp(), unparceledEvent.getTimestamp()); - assertEquals(event.getId(), unparceledEvent.getId()); + assertThat(unparceledEvent.getHostname()).isEqualTo(event.getHostname()); + assertThat(unparceledEvent.getInetAddresses().get(0)) + .isEqualTo(event.getInetAddresses().get(0)); + assertThat(unparceledEvent.getInetAddresses().get(1)) + .isEqualTo(event.getInetAddresses().get(1)); + assertThat(unparceledEvent.getTotalResolvedAddressCount()) + .isEqualTo(event.getTotalResolvedAddressCount()); + assertThat(unparceledEvent.getPackageName()).isEqualTo(event.getPackageName()); + assertThat(unparceledEvent.getTimestamp()).isEqualTo(event.getTimestamp()); + assertThat(unparceledEvent.getId()).isEqualTo(event.getId()); } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java index a3cc915b3eba..24e226a64917 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java @@ -20,6 +20,9 @@ import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEV import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; @@ -32,16 +35,17 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; -import android.test.AndroidTestCase; import android.test.mock.MockPackageManager; import android.view.inputmethod.InputMethodInfo; import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -51,18 +55,23 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -public class OverlayPackagesProviderTest extends AndroidTestCase { +/** + * Run this test with: + * + * {@code atest FrameworksServicesTests:com.android.server.devicepolicy.OwnersTest} + * + */ +@RunWith(AndroidJUnit4.class) +public class OverlayPackagesProviderTest { private static final String TEST_DPC_PACKAGE_NAME = "dpc.package.name"; private static final ComponentName TEST_MDM_COMPONENT_NAME = new ComponentName( TEST_DPC_PACKAGE_NAME, "pc.package.name.DeviceAdmin"); private static final int TEST_USER_ID = 123; - private @Mock - Resources mResources; - @Mock - private OverlayPackagesProvider.Injector mInjector; - private @Mock - Context mTestContext; + private @Mock Resources mResources; + + private @Mock OverlayPackagesProvider.Injector mInjector; + private @Mock Context mTestContext; private Resources mRealResources; private FakePackageManager mPackageManager; @@ -256,12 +265,12 @@ public class OverlayPackagesProviderTest extends AndroidTestCase { ArrayList<String> required = getStringArrayInRealResources(requiredId); ArrayList<String> disallowed = getStringArrayInRealResources(disallowedId); required.retainAll(disallowed); - assertTrue(required.isEmpty()); + assertThat(required.isEmpty()).isTrue(); } private void verifyAppsAreNonRequired(String action, String... appArray) { - assertEquals(setFromArray(appArray), - mHelper.getNonRequiredApps(TEST_MDM_COMPONENT_NAME, TEST_USER_ID, action)); + assertThat(mHelper.getNonRequiredApps(TEST_MDM_COMPONENT_NAME, TEST_USER_ID, action)) + .containsExactlyElementsIn(setFromArray(appArray)); } private void setRequiredAppsManagedDevice(String... apps) { @@ -348,19 +357,19 @@ public class OverlayPackagesProviderTest extends AndroidTestCase { class FakePackageManager extends MockPackageManager { @Override public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) { - assertTrue("Expected an intent with action ACTION_MAIN", - Intent.ACTION_MAIN.equals(intent.getAction())); - assertEquals("Expected an intent with category CATEGORY_LAUNCHER", - setFromArray(Intent.CATEGORY_LAUNCHER), intent.getCategories()); - assertTrue("Expected the flag MATCH_UNINSTALLED_PACKAGES", - (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0); - assertTrue("Expected the flag MATCH_DISABLED_COMPONENTS", - (flags & PackageManager.MATCH_DISABLED_COMPONENTS) != 0); - assertTrue("Expected the flag MATCH_DIRECT_BOOT_AWARE", - (flags & PackageManager.MATCH_DIRECT_BOOT_AWARE) != 0); - assertTrue("Expected the flag MATCH_DIRECT_BOOT_UNAWARE", - (flags & PackageManager.MATCH_DIRECT_BOOT_UNAWARE) != 0); - assertEquals(userId, TEST_USER_ID); + assertWithMessage("Expected an intent with action ACTION_MAIN") + .that(Intent.ACTION_MAIN.equals(intent.getAction())).isTrue(); + assertWithMessage("Expected an intent with category CATEGORY_LAUNCHER") + .that(intent.getCategories()).containsExactly(Intent.CATEGORY_LAUNCHER); + assertWithMessage("Expected the flag MATCH_UNINSTALLED_PACKAGES") + .that((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES)).isNotEqualTo(0); + assertWithMessage("Expected the flag MATCH_DISABLED_COMPONENTS") + .that((flags & PackageManager.MATCH_DISABLED_COMPONENTS)).isNotEqualTo(0); + assertWithMessage("Expected the flag MATCH_DIRECT_BOOT_AWARE") + .that((flags & PackageManager.MATCH_DIRECT_BOOT_AWARE)).isNotEqualTo(0); + assertWithMessage("Expected the flag MATCH_DIRECT_BOOT_UNAWARE") + .that((flags & PackageManager.MATCH_DIRECT_BOOT_UNAWARE)).isNotEqualTo(0); + assertThat(TEST_USER_ID).isEqualTo(userId); List<ResolveInfo> result = new ArrayList<>(); if (mSystemAppsWithLauncher == null) { return result; diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java index 5899bb0e94fe..bfe183cc608b 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java @@ -16,25 +16,32 @@ package com.android.server.devicepolicy; +import static com.google.common.truth.Truth.assertThat; + import android.content.ComponentName; import android.os.UserHandle; import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.OwnersTestable; +import org.junit.Test; +import org.junit.runner.RunWith; + /** * Tests for the DeviceOwner object that saves & loads device and policy owner information. - * run this test with: - m FrameworksServicesTests && - adb install \ - -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && - adb shell am instrument -e class com.android.server.devicepolicy.OwnersTest \ - -w com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner - - (mmma frameworks/base/services/tests/servicestests/ for non-ninja build) + * + * <p>Run this test with: + * + * {@code atest FrameworksServicesTests:com.android.server.devicepolicy.OwnersTest} + * */ @SmallTest +@RunWith(AndroidJUnit4.class) public class OwnersTest extends DpmTestBase { + + @Test public void testUpgrade01() throws Exception { getServices().addUsers(10, 11, 20, 21); @@ -48,26 +55,26 @@ public class OwnersTest extends DpmTestBase { owners.load(); // The legacy file should be removed. - assertFalse(owners.getLegacyConfigFile().exists()); + assertThat(owners.getLegacyConfigFile().exists()).isFalse(); // File was empty, so no new files should be created. - assertFalse(owners.getDeviceOwnerFile().exists()); - - assertFalse(owners.getProfileOwnerFile(10).exists()); - assertFalse(owners.getProfileOwnerFile(11).exists()); - assertFalse(owners.getProfileOwnerFile(20).exists()); - assertFalse(owners.getProfileOwnerFile(21).exists()); - - assertFalse(owners.hasDeviceOwner()); - assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertNull(owners.getSystemUpdatePolicy()); - assertEquals(0, owners.getProfileOwnerKeys().size()); - - assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getDeviceOwnerFile().exists()).isFalse(); + + assertThat(owners.getProfileOwnerFile(10).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(11).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(20).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(21).exists()).isFalse(); + + assertThat(owners.hasDeviceOwner()).isFalse(); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getSystemUpdatePolicy()).isNull(); + assertThat(owners.getProfileOwnerKeys()).isEmpty(); + + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); } // Then re-read and check. @@ -75,19 +82,20 @@ public class OwnersTest extends DpmTestBase { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); - assertFalse(owners.hasDeviceOwner()); - assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertNull(owners.getSystemUpdatePolicy()); - assertEquals(0, owners.getProfileOwnerKeys().size()); + assertThat(owners.hasDeviceOwner()).isFalse(); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getSystemUpdatePolicy()).isNull(); + assertThat(owners.getProfileOwnerKeys()).isEmpty(); - assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); } } + @Test public void testUpgrade02() throws Exception { getServices().addUsers(10, 11, 20, 21); @@ -101,28 +109,28 @@ public class OwnersTest extends DpmTestBase { owners.load(); // The legacy file should be removed. - assertFalse(owners.getLegacyConfigFile().exists()); + assertThat(owners.getLegacyConfigFile().exists()).isFalse(); - assertTrue(owners.getDeviceOwnerFile().exists()); // TODO Check content + assertThat(owners.getDeviceOwnerFile().exists()).isTrue(); // TODO Check content - assertFalse(owners.getProfileOwnerFile(10).exists()); - assertFalse(owners.getProfileOwnerFile(11).exists()); - assertFalse(owners.getProfileOwnerFile(20).exists()); - assertFalse(owners.getProfileOwnerFile(21).exists()); + assertThat(owners.getProfileOwnerFile(10).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(11).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(20).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(21).exists()).isFalse(); - assertTrue(owners.hasDeviceOwner()); - assertEquals(null, owners.getDeviceOwnerName()); - assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName()); - assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId()); + assertThat(owners.hasDeviceOwner()).isTrue(); + assertThat(owners.getDeviceOwnerName()).isEqualTo(null); + assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); - assertNull(owners.getSystemUpdatePolicy()); - assertEquals(0, owners.getProfileOwnerKeys().size()); + assertThat(owners.getSystemUpdatePolicy()).isNull(); + assertThat(owners.getProfileOwnerKeys()).isEmpty(); - assertTrue(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); } // Then re-read and check. @@ -130,22 +138,23 @@ public class OwnersTest extends DpmTestBase { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); - assertTrue(owners.hasDeviceOwner()); - assertEquals(null, owners.getDeviceOwnerName()); - assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName()); - assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId()); + assertThat(owners.hasDeviceOwner()).isTrue(); + assertThat(owners.getDeviceOwnerName()).isEqualTo(null); + assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); - assertNull(owners.getSystemUpdatePolicy()); - assertEquals(0, owners.getProfileOwnerKeys().size()); + assertThat(owners.getSystemUpdatePolicy()).isNull(); + assertThat(owners.getProfileOwnerKeys()).isEmpty(); - assertTrue(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); } } + @Test public void testUpgrade03() throws Exception { getServices().addUsers(10, 11, 20, 21); @@ -159,36 +168,36 @@ public class OwnersTest extends DpmTestBase { owners.load(); // The legacy file should be removed. - assertFalse(owners.getLegacyConfigFile().exists()); - - assertFalse(owners.getDeviceOwnerFile().exists()); - - assertTrue(owners.getProfileOwnerFile(10).exists()); - assertTrue(owners.getProfileOwnerFile(11).exists()); - assertFalse(owners.getProfileOwnerFile(20).exists()); - assertFalse(owners.getProfileOwnerFile(21).exists()); - - assertFalse(owners.hasDeviceOwner()); - assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertNull(owners.getSystemUpdatePolicy()); - - assertEquals(2, owners.getProfileOwnerKeys().size()); - assertEquals(new ComponentName("com.google.android.testdpc", - "com.google.android.testdpc.DeviceAdminReceiver0"), - owners.getProfileOwnerComponent(10)); - assertEquals("0", owners.getProfileOwnerName(10)); - assertEquals("com.google.android.testdpc", owners.getProfileOwnerPackage(10)); - - assertEquals(new ComponentName("com.google.android.testdpc1", ""), - owners.getProfileOwnerComponent(11)); - assertEquals("1", owners.getProfileOwnerName(11)); - assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11)); - - assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getLegacyConfigFile().exists()).isFalse(); + + assertThat(owners.getDeviceOwnerFile().exists()).isFalse(); + + assertThat(owners.getProfileOwnerFile(10).exists()).isTrue(); + assertThat(owners.getProfileOwnerFile(11).exists()).isTrue(); + assertThat(owners.getProfileOwnerFile(20).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(21).exists()).isFalse(); + + assertThat(owners.hasDeviceOwner()).isFalse(); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getSystemUpdatePolicy()).isNull(); + + assertThat(owners.getProfileOwnerKeys()).hasSize(2); + assertThat(owners.getProfileOwnerComponent(10)) + .isEqualTo(new ComponentName("com.google.android.testdpc", + "com.google.android.testdpc.DeviceAdminReceiver0")); + assertThat(owners.getProfileOwnerName(10)).isEqualTo("0"); + assertThat(owners.getProfileOwnerPackage(10)).isEqualTo("com.google.android.testdpc"); + + assertThat(owners.getProfileOwnerComponent(11)) + .isEqualTo(new ComponentName("com.google.android.testdpc1", "")); + assertThat(owners.getProfileOwnerName(11)).isEqualTo("1"); + assertThat(owners.getProfileOwnerPackage(11)).isEqualTo("com.google.android.testdpc1"); + + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); } // Then re-read and check. @@ -196,27 +205,27 @@ public class OwnersTest extends DpmTestBase { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); - assertFalse(owners.hasDeviceOwner()); - assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertNull(owners.getSystemUpdatePolicy()); - - assertEquals(2, owners.getProfileOwnerKeys().size()); - assertEquals(new ComponentName("com.google.android.testdpc", - "com.google.android.testdpc.DeviceAdminReceiver0"), - owners.getProfileOwnerComponent(10)); - assertEquals("0", owners.getProfileOwnerName(10)); - assertEquals("com.google.android.testdpc", owners.getProfileOwnerPackage(10)); - - assertEquals(new ComponentName("com.google.android.testdpc1", ""), - owners.getProfileOwnerComponent(11)); - assertEquals("1", owners.getProfileOwnerName(11)); - assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11)); - - assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.hasDeviceOwner()).isFalse(); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getSystemUpdatePolicy()).isNull(); + + assertThat(owners.getProfileOwnerKeys()).hasSize(2); + assertThat(owners.getProfileOwnerComponent(10)) + .isEqualTo(new ComponentName("com.google.android.testdpc", + "com.google.android.testdpc.DeviceAdminReceiver0")); + assertThat(owners.getProfileOwnerName(10)).isEqualTo("0"); + assertThat(owners.getProfileOwnerPackage(10)).isEqualTo("com.google.android.testdpc"); + + assertThat(owners.getProfileOwnerComponent(11)) + .isEqualTo(new ComponentName("com.google.android.testdpc1", "")); + assertThat(owners.getProfileOwnerName(11)).isEqualTo("1"); + assertThat(owners.getProfileOwnerPackage(11)).isEqualTo("com.google.android.testdpc1"); + + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); } } @@ -224,6 +233,7 @@ public class OwnersTest extends DpmTestBase { * Note this also tests {@link Owners#setDeviceOwnerUserRestrictionsMigrated()} * and {@link Owners#setProfileOwnerUserRestrictionsMigrated(int)}. */ + @Test public void testUpgrade04() throws Exception { getServices().addUsers(10, 11, 20, 21); @@ -237,40 +247,40 @@ public class OwnersTest extends DpmTestBase { owners.load(); // The legacy file should be removed. - assertFalse(owners.getLegacyConfigFile().exists()); - - assertTrue(owners.getDeviceOwnerFile().exists()); - - assertTrue(owners.getProfileOwnerFile(10).exists()); - assertTrue(owners.getProfileOwnerFile(11).exists()); - assertFalse(owners.getProfileOwnerFile(20).exists()); - assertFalse(owners.getProfileOwnerFile(21).exists()); - - assertTrue(owners.hasDeviceOwner()); - assertEquals(null, owners.getDeviceOwnerName()); - assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName()); - assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId()); - - assertNotNull(owners.getSystemUpdatePolicy()); - assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType()); - - assertEquals(2, owners.getProfileOwnerKeys().size()); - assertEquals(new ComponentName("com.google.android.testdpc", - "com.google.android.testdpc.DeviceAdminReceiver0"), - owners.getProfileOwnerComponent(10)); - assertEquals("0", owners.getProfileOwnerName(10)); - assertEquals("com.google.android.testdpc", owners.getProfileOwnerPackage(10)); - - assertEquals(new ComponentName("com.google.android.testdpc1", ""), - owners.getProfileOwnerComponent(11)); - assertEquals("1", owners.getProfileOwnerName(11)); - assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11)); - - assertTrue(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getLegacyConfigFile().exists()).isFalse(); + + assertThat(owners.getDeviceOwnerFile().exists()).isTrue(); + + assertThat(owners.getProfileOwnerFile(10).exists()).isTrue(); + assertThat(owners.getProfileOwnerFile(11).exists()).isTrue(); + assertThat(owners.getProfileOwnerFile(20).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(21).exists()).isFalse(); + + assertThat(owners.hasDeviceOwner()).isTrue(); + assertThat(owners.getDeviceOwnerName()).isEqualTo(null); + assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); + + assertThat(owners.getSystemUpdatePolicy()).isNotNull(); + assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5); + + assertThat(owners.getProfileOwnerKeys()).hasSize(2); + assertThat(owners.getProfileOwnerComponent(10)) + .isEqualTo(new ComponentName("com.google.android.testdpc", + "com.google.android.testdpc.DeviceAdminReceiver0")); + assertThat(owners.getProfileOwnerName(10)).isEqualTo("0"); + assertThat(owners.getProfileOwnerPackage(10)).isEqualTo("com.google.android.testdpc"); + + assertThat(owners.getProfileOwnerComponent(11)) + .isEqualTo(new ComponentName("com.google.android.testdpc1", "")); + assertThat(owners.getProfileOwnerName(11)).isEqualTo("1"); + assertThat(owners.getProfileOwnerPackage(11)).isEqualTo("com.google.android.testdpc1"); + + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); } // Then re-read and check. @@ -278,31 +288,31 @@ public class OwnersTest extends DpmTestBase { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); - assertTrue(owners.hasDeviceOwner()); - assertEquals(null, owners.getDeviceOwnerName()); - assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName()); - assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId()); + assertThat(owners.hasDeviceOwner()).isTrue(); + assertThat(owners.getDeviceOwnerName()).isEqualTo(null); + assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); - assertNotNull(owners.getSystemUpdatePolicy()); - assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType()); + assertThat(owners.getSystemUpdatePolicy()).isNotNull(); + assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5); - assertEquals(2, owners.getProfileOwnerKeys().size()); - assertEquals(new ComponentName("com.google.android.testdpc", - "com.google.android.testdpc.DeviceAdminReceiver0"), - owners.getProfileOwnerComponent(10)); - assertEquals("0", owners.getProfileOwnerName(10)); - assertEquals("com.google.android.testdpc", owners.getProfileOwnerPackage(10)); + assertThat(owners.getProfileOwnerKeys()).hasSize(2); + assertThat(owners.getProfileOwnerComponent(10)) + .isEqualTo(new ComponentName("com.google.android.testdpc", + "com.google.android.testdpc.DeviceAdminReceiver0")); + assertThat(owners.getProfileOwnerName(10)).isEqualTo("0"); + assertThat(owners.getProfileOwnerPackage(10)).isEqualTo("com.google.android.testdpc"); - assertEquals(new ComponentName("com.google.android.testdpc1", ""), - owners.getProfileOwnerComponent(11)); - assertEquals("1", owners.getProfileOwnerName(11)); - assertEquals("com.google.android.testdpc1", owners.getProfileOwnerPackage(11)); + assertThat(owners.getProfileOwnerComponent(11)) + .isEqualTo(new ComponentName("com.google.android.testdpc1", "")); + assertThat(owners.getProfileOwnerName(11)).isEqualTo("1"); + assertThat(owners.getProfileOwnerPackage(11)).isEqualTo("com.google.android.testdpc1"); - assertTrue(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); owners.setDeviceOwnerUserRestrictionsMigrated(); } @@ -311,11 +321,11 @@ public class OwnersTest extends DpmTestBase { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); - assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); owners.setProfileOwnerUserRestrictionsMigrated(11); } @@ -324,16 +334,17 @@ public class OwnersTest extends DpmTestBase { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); - assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertTrue(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); owners.setProfileOwnerUserRestrictionsMigrated(11); } } + @Test public void testUpgrade05() throws Exception { getServices().addUsers(10, 11, 20, 21); @@ -347,27 +358,27 @@ public class OwnersTest extends DpmTestBase { owners.load(); // The legacy file should be removed. - assertFalse(owners.getLegacyConfigFile().exists()); + assertThat(owners.getLegacyConfigFile().exists()).isFalse(); // Note device initializer is no longer supported. No need to write the DO file. - assertFalse(owners.getDeviceOwnerFile().exists()); + assertThat(owners.getDeviceOwnerFile().exists()).isFalse(); - assertFalse(owners.getProfileOwnerFile(10).exists()); - assertFalse(owners.getProfileOwnerFile(11).exists()); - assertFalse(owners.getProfileOwnerFile(20).exists()); + assertThat(owners.getProfileOwnerFile(10).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(11).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(20).exists()).isFalse(); - assertFalse(owners.hasDeviceOwner()); - assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); + assertThat(owners.hasDeviceOwner()).isFalse(); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); - assertNull(owners.getSystemUpdatePolicy()); - assertEquals(0, owners.getProfileOwnerKeys().size()); + assertThat(owners.getSystemUpdatePolicy()).isNull(); + assertThat(owners.getProfileOwnerKeys()).isEmpty(); - assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); } // Then re-read and check. @@ -375,21 +386,22 @@ public class OwnersTest extends DpmTestBase { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); - assertFalse(owners.hasDeviceOwner()); - assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); + assertThat(owners.hasDeviceOwner()).isFalse(); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); - assertNull(owners.getSystemUpdatePolicy()); - assertEquals(0, owners.getProfileOwnerKeys().size()); + assertThat(owners.getSystemUpdatePolicy()).isNull(); + assertThat(owners.getProfileOwnerKeys()).isEmpty(); - assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); } } + @Test public void testUpgrade06() throws Exception { getServices().addUsers(10, 11, 20, 21); @@ -403,26 +415,26 @@ public class OwnersTest extends DpmTestBase { owners.load(); // The legacy file should be removed. - assertFalse(owners.getLegacyConfigFile().exists()); + assertThat(owners.getLegacyConfigFile().exists()).isFalse(); - assertTrue(owners.getDeviceOwnerFile().exists()); + assertThat(owners.getDeviceOwnerFile().exists()).isTrue(); - assertFalse(owners.getProfileOwnerFile(10).exists()); - assertFalse(owners.getProfileOwnerFile(11).exists()); - assertFalse(owners.getProfileOwnerFile(20).exists()); + assertThat(owners.getProfileOwnerFile(10).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(11).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(20).exists()).isFalse(); - assertFalse(owners.hasDeviceOwner()); - assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertEquals(0, owners.getProfileOwnerKeys().size()); + assertThat(owners.hasDeviceOwner()).isFalse(); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getProfileOwnerKeys()).isEmpty(); - assertNotNull(owners.getSystemUpdatePolicy()); - assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType()); + assertThat(owners.getSystemUpdatePolicy()).isNotNull(); + assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5); - assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); } // Then re-read and check. @@ -430,21 +442,22 @@ public class OwnersTest extends DpmTestBase { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); - assertFalse(owners.hasDeviceOwner()); - assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId()); - assertEquals(0, owners.getProfileOwnerKeys().size()); + assertThat(owners.hasDeviceOwner()).isFalse(); + assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getProfileOwnerKeys()).isEmpty(); - assertNotNull(owners.getSystemUpdatePolicy()); - assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType()); + assertThat(owners.getSystemUpdatePolicy()).isNotNull(); + assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5); - assertFalse(owners.getDeviceOwnerUserRestrictionsNeedsMigration()); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)); - assertFalse(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); + assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); } } + @Test public void testRemoveExistingFiles() throws Exception { getServices().addUsers(10, 11, 20, 21); @@ -456,11 +469,11 @@ public class OwnersTest extends DpmTestBase { owners.load(); - assertFalse(owners.getLegacyConfigFile().exists()); + assertThat(owners.getLegacyConfigFile().exists()).isFalse(); - assertTrue(owners.getDeviceOwnerFile().exists()); - assertTrue(owners.getProfileOwnerFile(10).exists()); - assertTrue(owners.getProfileOwnerFile(11).exists()); + assertThat(owners.getDeviceOwnerFile().exists()).isTrue(); + assertThat(owners.getProfileOwnerFile(10).exists()).isTrue(); + assertThat(owners.getProfileOwnerFile(11).exists()).isTrue(); // Then clear all information and save. owners.clearDeviceOwner(); @@ -475,8 +488,8 @@ public class OwnersTest extends DpmTestBase { owners.writeProfileOwner(21); // Now all files should be removed. - assertFalse(owners.getDeviceOwnerFile().exists()); - assertFalse(owners.getProfileOwnerFile(10).exists()); - assertFalse(owners.getProfileOwnerFile(11).exists()); + assertThat(owners.getDeviceOwnerFile().exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(10).exists()).isFalse(); + assertThat(owners.getProfileOwnerFile(11).exists()).isFalse(); } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java index 8dcf21f9fe77..6cefaebbff7a 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.server.devicepolicy; import static android.app.admin.SecurityLog.TAG_ADB_SHELL_CMD; @@ -11,6 +26,8 @@ import static android.app.admin.SecurityLog.TAG_KEY_INTEGRITY_VIOLATION; import static android.app.admin.SecurityLog.TAG_MEDIA_MOUNT; import static android.app.admin.SecurityLog.TAG_MEDIA_UNMOUNT; +import static com.google.common.truth.Truth.assertThat; + import android.app.admin.SecurityLog.SecurityEvent; import android.os.Parcel; import android.os.UserHandle; @@ -18,21 +35,37 @@ import android.text.TextUtils; import android.util.EventLog; import android.util.EventLog.Event; +import androidx.test.runner.AndroidJUnit4; + import junit.framework.AssertionFailedError; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.ArrayList; import java.util.List; +/** + * Tests for the DeviceOwner object that saves & loads device and policy owner information. + * + * <p>Run this test with: + * + * {@code atest FrameworksServicesTests:com.android.server.devicepolicy.SecurityEventTest} + * + */ +@RunWith(AndroidJUnit4.class) public class SecurityEventTest extends DpmTestBase { + @Test public void testSecurityEventId() throws Exception { SecurityEvent event = createEvent(() -> { EventLog.writeEvent(TAG_ADB_SHELL_CMD, 0); }, TAG_ADB_SHELL_CMD); event.setId(20); - assertEquals(20, event.getId()); + assertThat(event.getId()).isEqualTo(20); } + @Test public void testSecurityEventParceling() throws Exception { // GIVEN an event. SecurityEvent event = createEvent(() -> { @@ -45,12 +78,13 @@ public class SecurityEventTest extends DpmTestBase { SecurityEvent unparceledEvent = p.readParcelable(SecurityEventTest.class.getClassLoader()); p.recycle(); // THEN the event state is preserved. - assertEquals(event.getTag(), unparceledEvent.getTag()); - assertEquals(event.getData(), unparceledEvent.getData()); - assertEquals(event.getTimeNanos(), unparceledEvent.getTimeNanos()); - assertEquals(event.getId(), unparceledEvent.getId()); + assertThat(unparceledEvent.getTag()).isEqualTo(event.getTag()); + assertThat(unparceledEvent.getData()).isEqualTo(event.getData()); + assertThat(unparceledEvent.getTimeNanos()).isEqualTo(event.getTimeNanos()); + assertThat(unparceledEvent.getId()).isEqualTo(event.getId()); } + @Test public void testSecurityEventRedaction() throws Exception { SecurityEvent event; @@ -58,75 +92,75 @@ public class SecurityEventTest extends DpmTestBase { event = createEvent(() -> { EventLog.writeEvent(TAG_ADB_SHELL_CMD, "command"); }, TAG_ADB_SHELL_CMD); - assertFalse(TextUtils.isEmpty((String) event.getData())); + assertThat(TextUtils.isEmpty((String) event.getData())).isFalse(); // TAG_MEDIA_MOUNT will have the volume label redacted (second data) event = createEvent(() -> { EventLog.writeEvent(TAG_MEDIA_MOUNT, new Object[] {"path", "label"}); }, TAG_MEDIA_MOUNT); - assertFalse(TextUtils.isEmpty(event.getStringData(1))); - assertTrue(TextUtils.isEmpty(event.redact(0).getStringData(1))); + assertThat(TextUtils.isEmpty(event.getStringData(1))).isFalse(); + assertThat(TextUtils.isEmpty(event.redact(0).getStringData(1))).isTrue(); // TAG_MEDIA_UNMOUNT will have the volume label redacted (second data) event = createEvent(() -> { EventLog.writeEvent(TAG_MEDIA_UNMOUNT, new Object[] {"path", "label"}); }, TAG_MEDIA_UNMOUNT); - assertFalse(TextUtils.isEmpty(event.getStringData(1))); - assertTrue(TextUtils.isEmpty(event.redact(0).getStringData(1))); + assertThat(TextUtils.isEmpty(event.getStringData(1))).isFalse(); + assertThat(TextUtils.isEmpty(event.redact(0).getStringData(1))).isTrue(); // TAG_APP_PROCESS_START will be fully redacted if user does not match event = createEvent(() -> { EventLog.writeEvent(TAG_APP_PROCESS_START, new Object[] {"process", 12345L, UserHandle.getUid(10, 123), 456, "seinfo", "hash"}); }, TAG_APP_PROCESS_START); - assertNotNull(event.redact(10)); - assertNull(event.redact(11)); + assertThat(event.redact(10)).isNotNull(); + assertThat(event.redact(11)).isNull(); // TAG_CERT_AUTHORITY_INSTALLED will be fully redacted if user does not match event = createEvent(() -> { EventLog.writeEvent(TAG_CERT_AUTHORITY_INSTALLED, new Object[] {1, "subject", 10}); }, TAG_CERT_AUTHORITY_INSTALLED); - assertNotNull(event.redact(10)); - assertNull(event.redact(11)); + assertThat(event.redact(10)).isNotNull(); + assertThat(event.redact(11)).isNull(); // TAG_CERT_AUTHORITY_REMOVED will be fully redacted if user does not match event = createEvent(() -> { EventLog.writeEvent(TAG_CERT_AUTHORITY_REMOVED, new Object[] {1, "subject", 20}); }, TAG_CERT_AUTHORITY_REMOVED); - assertNotNull(event.redact(20)); - assertNull(event.redact(0)); + assertThat(event.redact(20)).isNotNull(); + assertThat(event.redact(0)).isNull(); // TAG_KEY_GENERATED will be fully redacted if user does not match event = createEvent(() -> { EventLog.writeEvent(TAG_KEY_GENERATED, new Object[] {1, "alias", UserHandle.getUid(0, 123)}); }, TAG_KEY_GENERATED); - assertNotNull(event.redact(0)); - assertNull(event.redact(10)); + assertThat(event.redact(0)).isNotNull(); + assertThat(event.redact(10)).isNull(); // TAG_KEY_IMPORT will be fully redacted if user does not match event = createEvent(() -> { EventLog.writeEvent(TAG_KEY_IMPORT, new Object[] {1, "alias", UserHandle.getUid(1, 123)}); }, TAG_KEY_IMPORT); - assertNotNull(event.redact(1)); - assertNull(event.redact(10)); + assertThat(event.redact(1)).isNotNull(); + assertThat(event.redact(10)).isNull(); // TAG_KEY_DESTRUCTION will be fully redacted if user does not match event = createEvent(() -> { EventLog.writeEvent(TAG_KEY_DESTRUCTION, new Object[] {1, "alias", UserHandle.getUid(2, 123)}); }, TAG_KEY_DESTRUCTION); - assertNotNull(event.redact(2)); - assertNull(event.redact(10)); + assertThat(event.redact(2)).isNotNull(); + assertThat(event.redact(10)).isNull(); // TAG_KEY_INTEGRITY_VIOLATION will be fully redacted if user does not match event = createEvent(() -> { EventLog.writeEvent(TAG_KEY_INTEGRITY_VIOLATION, new Object[] {"alias", UserHandle.getUid(2, 123)}); }, TAG_KEY_INTEGRITY_VIOLATION); - assertNotNull(event.redact(2)); - assertNull(event.redact(10)); + assertThat(event.redact(2)).isNotNull(); + assertThat(event.redact(10)).isNull(); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java index e51859b5c829..0a9aad771ff0 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java @@ -21,8 +21,9 @@ import static android.app.admin.SystemUpdatePolicy.ValidationFailedException.ERR import static android.app.admin.SystemUpdatePolicy.ValidationFailedException.ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE; import static android.app.admin.SystemUpdatePolicy.ValidationFailedException.ERROR_NEW_FREEZE_PERIOD_TOO_LONG; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.fail; import android.app.admin.FreezePeriod; @@ -53,10 +54,12 @@ import java.util.concurrent.TimeUnit; /** * Unit tests for {@link android.app.admin.SystemUpdatePolicy}. - * Throughout this test, we use "MM-DD" format to denote dates without year. * - * atest com.android.server.devicepolicy.SystemUpdatePolicyTest - * runtest -c com.android.server.devicepolicy.SystemUpdatePolicyTest frameworks-services + * <p>NOTE: Throughout this test, we use {@code "MM-DD"} format to denote dates without year. + * + * <p>Run this test with: + * + * {@code atest FrameworksServicesTests:com.android.server.devicepolicy.SystemUpdatePolicyTest} */ @RunWith(AndroidJUnit4.class) public final class SystemUpdatePolicyTest { @@ -224,37 +227,37 @@ public final class SystemUpdatePolicyTest { @Test public void testDistanceWithoutLeapYear() { - assertEquals(364, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2016, 12, 31), LocalDate.of(2016, 1, 1))); - assertEquals(365, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2017, 1, 1), LocalDate.of(2016, 1, 1))); - assertEquals(365, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2017, 2, 28), LocalDate.of(2016, 2, 29))); - assertEquals(-365, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2016, 1, 1), LocalDate.of(2017, 1, 1))); - assertEquals(1, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2016, 3, 1), LocalDate.of(2016, 2, 29))); - assertEquals(1, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2016, 3, 1), LocalDate.of(2016, 2, 28))); - assertEquals(0, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2016, 2, 29), LocalDate.of(2016, 2, 28))); - assertEquals(0, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2016, 2, 28), LocalDate.of(2016, 2, 28))); - - assertEquals(59, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2016, 3, 1), LocalDate.of(2016, 1, 1))); - assertEquals(59, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2017, 3, 1), LocalDate.of(2017, 1, 1))); - - assertEquals(365 * 40, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2040, 1, 1), LocalDate.of(2000, 1, 1))); - - assertEquals(365 * 2, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2019, 3, 1), LocalDate.of(2017, 3, 1))); - assertEquals(365 * 2, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2018, 3, 1), LocalDate.of(2016, 3, 1))); - assertEquals(365 * 2, FreezePeriod.distanceWithoutLeapYear( - LocalDate.of(2017, 3, 1), LocalDate.of(2015, 3, 1))); + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2016, 12, 31), LocalDate.of(2016, 1, 1))).isEqualTo(364); + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2017, 1, 1), LocalDate.of(2016, 1, 1))).isEqualTo(365); + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2017, 2, 28), LocalDate.of(2016, 2, 29))).isEqualTo(365); + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2016, 1, 1), LocalDate.of(2017, 1, 1))).isEqualTo(-365); + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2016, 3, 1), LocalDate.of(2016, 2, 29))).isEqualTo(1); + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2016, 3, 1), LocalDate.of(2016, 2, 28))).isEqualTo(1); + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2016, 2, 29), LocalDate.of(2016, 2, 28))).isEqualTo(0); + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2016, 2, 28), LocalDate.of(2016, 2, 28))).isEqualTo(0); + + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2016, 3, 1), LocalDate.of(2016, 1, 1))).isEqualTo(59); + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2017, 3, 1), LocalDate.of(2017, 1, 1))).isEqualTo(59); + + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2040, 1, 1), LocalDate.of(2000, 1, 1))).isEqualTo(365 * 40); + + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2019, 3, 1), LocalDate.of(2017, 3, 1))).isEqualTo(365 * 2); + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2018, 3, 1), LocalDate.of(2016, 3, 1))).isEqualTo(365 * 2); + assertThat(FreezePeriod.distanceWithoutLeapYear( + LocalDate.of(2017, 3, 1), LocalDate.of(2015, 3, 1))).isEqualTo(365 * 2); } @@ -395,8 +398,8 @@ public final class SystemUpdatePolicyTest { private void assertInstallationOption(int expectedType, long expectedTime, long now, SystemUpdatePolicy p) { - assertEquals(expectedType, p.getInstallationOptionAt(now).getType()); - assertEquals(expectedTime, p.getInstallationOptionAt(now).getEffectiveTime()); + assertThat(p.getInstallationOptionAt(now).getType()).isEqualTo(expectedType); + assertThat(p.getInstallationOptionAt(now).getEffectiveTime()).isEqualTo(expectedTime); } private void testFreezePeriodsSucceeds(String...dates) throws Exception { @@ -410,8 +413,8 @@ public final class SystemUpdatePolicyTest { setFreezePeriods(p, dates); fail("Invalid periods (" + expectedError + ") not flagged: " + String.join(" ", dates)); } catch (SystemUpdatePolicy.ValidationFailedException e) { - assertTrue("Exception not expected: " + e.getMessage(), - e.getErrorCode() == expectedError); + assertWithMessage("Exception not expected: %s", e.getMessage()).that(e.getErrorCode()) + .isEqualTo(expectedError); } } @@ -426,8 +429,8 @@ public final class SystemUpdatePolicyTest { createPrevFreezePeriod(prevStart, prevEnd, now, dates); fail("Invalid period (" + expectedError + ") not flagged: " + String.join(" ", dates)); } catch (SystemUpdatePolicy.ValidationFailedException e) { - assertTrue("Exception not expected: " + e.getMessage(), - e.getErrorCode() == expectedError); + assertWithMessage("Exception not expected: %s", e.getMessage()).that(e.getErrorCode()) + .isEqualTo(expectedError); } } @@ -480,7 +483,7 @@ public final class SystemUpdatePolicyTest { ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); XmlPullParser parser = Xml.newPullParser(); parser.setInput(new InputStreamReader(inStream)); - assertEquals(XmlPullParser.START_TAG, parser.next()); + assertThat(parser.next()).isEqualTo(XmlPullParser.START_TAG); checkFreezePeriods(SystemUpdatePolicy.restoreFromXml(parser), expectedPeriods); } @@ -488,8 +491,8 @@ public final class SystemUpdatePolicyTest { List<FreezePeriod> expectedPeriods) { int i = 0; for (FreezePeriod period : policy.getFreezePeriods()) { - assertEquals(expectedPeriods.get(i).getStart(), period.getStart()); - assertEquals(expectedPeriods.get(i).getEnd(), period.getEnd()); + assertThat(period.getStart()).isEqualTo(expectedPeriods.get(i).getStart()); + assertThat(period.getEnd()).isEqualTo(expectedPeriods.get(i).getEnd()); i++; } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java index 2005dd901ad1..07ea85535f50 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java @@ -25,14 +25,13 @@ import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.T import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.TAG_TARGET_COMPONENT; import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.TAG_USER_ID; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; import android.os.Environment; -import androidx.test.runner.AndroidJUnit4; import android.util.Log; +import androidx.test.runner.AndroidJUnit4; + import com.android.server.devicepolicy.TransferOwnershipMetadataManager.Injector; import com.android.server.devicepolicy.TransferOwnershipMetadataManager.Metadata; @@ -51,12 +50,14 @@ import java.nio.file.Paths; /** * Unit tests for {@link TransferOwnershipMetadataManager}. * - * bit FrameworksServicesTests:com.android.server.devicepolicy.TransferOwnershipMetadataManagerTest - * runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/TransferOwnershipMetadataManagerTest.java -* */ - + * <p>Run this test with: + * + * <pre><code> + atest FrameworksServicesTests:com.android.server.devicepolicy.TransferOwnershipMetadataManagerTest + * </code></pre> + */ @RunWith(AndroidJUnit4.class) -public class TransferOwnershipMetadataManagerTest { +public final class TransferOwnershipMetadataManagerTest { private final static String TAG = TransferOwnershipMetadataManagerTest.class.getName(); private final static String SOURCE_COMPONENT = "com.dummy.admin.package/com.dummy.admin.package.SourceClassName"; @@ -77,28 +78,27 @@ public class TransferOwnershipMetadataManagerTest { @Test public void testSave() { TransferOwnershipMetadataManager paramsManager = getOwnerTransferParams(); - assertTrue(paramsManager.saveMetadataFile(TEST_PARAMS)); - assertTrue(paramsManager.metadataFileExists()); + assertThat(paramsManager.saveMetadataFile(TEST_PARAMS)).isTrue(); + assertThat(paramsManager.metadataFileExists()).isTrue(); } @Test public void testFileContentValid() { TransferOwnershipMetadataManager paramsManager = getOwnerTransferParams(); - assertTrue(paramsManager.saveMetadataFile(TEST_PARAMS)); + assertThat(paramsManager.saveMetadataFile(TEST_PARAMS)).isTrue(); Path path = Paths.get(new File(mMockInjector.getOwnerTransferMetadataDir(), OWNER_TRANSFER_METADATA_XML).getAbsolutePath()); try { String contents = new String(Files.readAllBytes(path), Charset.forName("UTF-8")); - assertEquals( - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" - + "<" + TAG_USER_ID + ">" + USER_ID + "</" + TAG_USER_ID + ">\n" - + "<" + TAG_SOURCE_COMPONENT + ">" + SOURCE_COMPONENT + "</" - + TAG_SOURCE_COMPONENT + ">\n" - + "<" + TAG_TARGET_COMPONENT + ">" + TARGET_COMPONENT + "</" - + TAG_TARGET_COMPONENT + ">\n" - + "<" + TAG_ADMIN_TYPE + ">" + ADMIN_TYPE_DEVICE_OWNER + "</" - + TAG_ADMIN_TYPE + ">\n", - contents); + assertThat(contents).isEqualTo( + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<" + TAG_USER_ID + ">" + USER_ID + "</" + TAG_USER_ID + ">\n" + + "<" + TAG_SOURCE_COMPONENT + ">" + SOURCE_COMPONENT + "</" + + TAG_SOURCE_COMPONENT + ">\n" + + "<" + TAG_TARGET_COMPONENT + ">" + TARGET_COMPONENT + "</" + + TAG_TARGET_COMPONENT + ">\n" + + "<" + TAG_ADMIN_TYPE + ">" + ADMIN_TYPE_DEVICE_OWNER + "</" + + TAG_ADMIN_TYPE + ">\n"); } catch (IOException e) { e.printStackTrace(); } @@ -124,7 +124,7 @@ public class TransferOwnershipMetadataManagerTest { Log.d(TAG, "testLoad: failed to get canonical file"); } paramsManager.saveMetadataFile(TEST_PARAMS); - assertEquals(TEST_PARAMS, paramsManager.loadMetadataFile()); + assertThat(paramsManager.loadMetadataFile()).isEqualTo(TEST_PARAMS); } @Test @@ -132,7 +132,7 @@ public class TransferOwnershipMetadataManagerTest { TransferOwnershipMetadataManager paramsManager = getOwnerTransferParams(); paramsManager.saveMetadataFile(TEST_PARAMS); paramsManager.deleteMetadataFile(); - assertFalse(paramsManager.metadataFileExists()); + assertThat(paramsManager.metadataFileExists()).isFalse(); } @After diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 6255630712ae..95c881e1d927 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -63,7 +63,6 @@ import com.android.server.LocalServices; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.permission.LegacyPermissionDataProvider; -import com.android.server.pm.permission.PermissionSettings; import com.google.common.truth.Truth; @@ -92,8 +91,6 @@ public class PackageManagerSettingsTests { private static final int TEST_RESOURCE_ID = 2131231283; @Mock - PermissionSettings mPermissionSettings; - @Mock RuntimePermissionsPersistence mRuntimePermissionsPersistence; @Mock LegacyPermissionDataProvider mPermissionDataProvider; @@ -117,7 +114,7 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, + Settings settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); verifyKeySetMetaData(settings); @@ -131,7 +128,7 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, + Settings settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -147,7 +144,7 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, + Settings settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue())); @@ -169,13 +166,13 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, + Settings settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); settings.writeLPr(); // Create Settings again to make it read from the new files - settings = new Settings(context.getFilesDir(), mPermissionSettings, + settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -199,7 +196,7 @@ public class PackageManagerSettingsTests { writePackageRestrictions_noSuspendingPackageXml(0); final Object lock = new Object(); final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, lock); settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1)); settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2)); @@ -223,7 +220,7 @@ public class PackageManagerSettingsTests { writePackageRestrictions_noSuspendParamsMapXml(0); final Object lock = new Object(); final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, lock); settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1)); settingsUnderTest.readPackageRestrictionsLPr(0); @@ -251,7 +248,7 @@ public class PackageManagerSettingsTests { @Test public void testReadWritePackageRestrictions_suspendInfo() { final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, new Object()); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2); @@ -349,7 +346,7 @@ public class PackageManagerSettingsTests { @Test public void testReadWritePackageRestrictions_distractionFlags() { final Context context = InstrumentationRegistry.getTargetContext(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, new Object()); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2); @@ -393,7 +390,7 @@ public class PackageManagerSettingsTests { public void testWriteReadUsesStaticLibraries() { final Context context = InstrumentationRegistry.getTargetContext(); final Object lock = new Object(); - final Settings settingsUnderTest = new Settings(context.getFilesDir(), mPermissionSettings, + final Settings settingsUnderTest = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); ps1.appId = Process.FIRST_APPLICATION_UID; @@ -469,7 +466,7 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - Settings settings = new Settings(context.getFilesDir(), mPermissionSettings, + Settings settings = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -642,7 +639,7 @@ public class PackageManagerSettingsTests { public void testUpdatePackageSetting03() { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings, + final Settings testSettings01 = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); final SharedUserSetting testUserSetting01 = createSharedUserSetting( testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/); @@ -752,7 +749,7 @@ public class PackageManagerSettingsTests { public void testCreateNewSetting03() { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings, + final Settings testSettings01 = new Settings(context.getFilesDir(), mRuntimePermissionsPersistence, mPermissionDataProvider, lock); final SharedUserSetting testUserSetting01 = createSharedUserSetting( testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index e304083dfd27..c50792866582 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -126,6 +126,7 @@ public class ManagedServicesTest extends UiServiceTestCase { users.add(mTen); users.add(new UserInfo(11, "11", 0)); users.add(new UserInfo(12, "12", 0)); + users.add(new UserInfo(13, "13", 0)); for (UserInfo user : users) { when(mUm.getUserInfo(eq(user.id))).thenReturn(user); } @@ -135,6 +136,7 @@ public class ManagedServicesTest extends UiServiceTestCase { profileIds.add(11); profileIds.add(10); profileIds.add(12); + profileIds.add(13); when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds); mVersionString = "2"; @@ -145,11 +147,13 @@ public class ManagedServicesTest extends UiServiceTestCase { mExpectedPrimaryPackages.put(10, "this.is.another.package"); mExpectedPrimaryPackages.put(11, ""); mExpectedPrimaryPackages.put(12, "bananas!"); + mExpectedPrimaryPackages.put(13, "non.user.set.package"); mExpectedPrimaryComponentNames = new ArrayMap<>(); mExpectedPrimaryComponentNames.put(0, "this.is.a.package.name/Ba:another.package/B1"); mExpectedPrimaryComponentNames.put(10, "this.is.another.package/M1"); mExpectedPrimaryComponentNames.put(11, ""); mExpectedPrimaryComponentNames.put(12, "bananas!/Bananas!"); + mExpectedPrimaryComponentNames.put(13, "non.user.set.package/M1"); mExpectedPrimary.put(APPROVAL_BY_PACKAGE, mExpectedPrimaryPackages); mExpectedPrimary.put(APPROVAL_BY_COMPONENT, mExpectedPrimaryComponentNames); @@ -341,6 +345,35 @@ public class ManagedServicesTest extends UiServiceTestCase { } } + /** Test that restore correctly parses the user_set attribute. */ + @Test + public void testReadXml_restoresUserSet() throws Exception { + for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { + ManagedServices service = + new TestManagedServices( + getContext(), mLock, mUserProfiles, mIpm, approvalLevel); + String testPackage = "user.test.package"; + String testComponent = "user.test.component/C1"; + String resolvedValue = + (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage; + String xmlEntry = getXmlEntry(resolvedValue, 0, true, false); + XmlPullParser parser = getParserWithEntries(service, xmlEntry); + + service.readXml(parser, null, true, 0); + + assertFalse("Failed while parsing xml:\n" + xmlEntry, + service.isPackageOrComponentUserSet(resolvedValue, 0)); + + xmlEntry = getXmlEntry(resolvedValue, 0, true, true); + parser = getParserWithEntries(service, xmlEntry); + + service.readXml(parser, null, true, 0); + + assertTrue("Failed while parsing xml:\n" + xmlEntry, + service.isPackageOrComponentUserSet(resolvedValue, 0)); + } + } + /** Test that restore ignores the user id attribute and applies the data to the target user. */ @Test public void testWriteReadXml_writeReadDefaults() throws Exception { @@ -374,7 +407,6 @@ public class ManagedServicesTest extends UiServiceTestCase { assertEquals(1, defaults.size()); assertEquals(new ComponentName("package", "class"), defaults.valueAt(0)); - } @Test @@ -628,6 +660,47 @@ public class ManagedServicesTest extends UiServiceTestCase { } @Test + public void testWriteXml_writesUserSet() throws Exception { + for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { + ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, + mIpm, approvalLevel); + loadXml(service); + + XmlSerializer serializer = new FastXmlSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + service.writeXml(serializer, false, UserHandle.USER_ALL); + serializer.endDocument(); + serializer.flush(); + + XmlPullParser parser = Xml.newPullParser(); + byte[] rawOutput = baos.toByteArray(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(rawOutput)), null); + parser.nextTag(); + for (UserInfo userInfo : mUm.getUsers()) { + service.readXml(parser, null, true, userInfo.id); + } + + String resolvedUserSetComponent = approvalLevel == APPROVAL_BY_PACKAGE + ? mExpectedPrimaryPackages.get(10) + : mExpectedPrimaryComponentNames.get(10); + String resolvedNonUserSetComponent = approvalLevel == APPROVAL_BY_PACKAGE + ? mExpectedPrimaryPackages.get(13) + : mExpectedPrimaryComponentNames.get(13); + + try { + assertFalse(service.isPackageOrComponentUserSet(resolvedNonUserSetComponent, 13)); + assertTrue(service.isPackageOrComponentUserSet(resolvedUserSetComponent, 10)); + } catch (AssertionError e) { + throw new AssertionError( + "Assertion failed while parsing xml:\n" + new String(rawOutput), e); + } + } + } + + @Test public void rebindServices_onlyBindsExactMatchesIfComponent() throws Exception { // If the primary and secondary lists contain component names, only those components within // the package should be matched @@ -965,6 +1038,7 @@ public class ManagedServicesTest extends UiServiceTestCase { allowedPackages.add("package"); allowedPackages.add("component"); allowedPackages.add("bananas!"); + allowedPackages.add("non.user.set.package"); Set<String> actual = service.getAllowedPackages(); assertEquals(allowedPackages.size(), actual.size()); @@ -1037,6 +1111,9 @@ public class ManagedServicesTest extends UiServiceTestCase { expected12.add(ComponentName.unflattenFromString("bananas!/Bananas!")); expected.put(12, expected12); expected.put(11, new ArraySet<>()); + ArraySet<ComponentName> expected13 = new ArraySet<>(); + expected13.add(ComponentName.unflattenFromString("non.user.set.package/M1")); + expected.put(13, expected13); SparseArray<ArraySet<ComponentName>> actual = service.getAllowedComponents(mUserProfiles.getCurrentProfileIds()); @@ -1309,6 +1386,15 @@ public class ManagedServicesTest extends UiServiceTestCase { } private void loadXml(ManagedServices service) throws Exception { + String xmlString = createXml(service); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(xmlString.getBytes())), null); + parser.nextTag(); + service.readXml(parser, null, false, UserHandle.USER_ALL); + } + + private String createXml(ManagedServices service) { final StringBuffer xml = new StringBuffer(); String xmlTag = service.getConfig().xmlTag; xml.append("<" + xmlTag @@ -1316,8 +1402,9 @@ public class ManagedServicesTest extends UiServiceTestCase { + (mVersionString != null ? " version=\"" + mVersionString + "\" " : "") + ">\n"); for (int userId : mExpectedPrimary.get(service.mApprovalLevel).keySet()) { + String pkgOrCmp = mExpectedPrimary.get(service.mApprovalLevel).get(userId); xml.append(getXmlEntry( - mExpectedPrimary.get(service.mApprovalLevel).get(userId), userId, true)); + pkgOrCmp, userId, true, !(pkgOrCmp.startsWith("non.user.set.package")))); } for (int userId : mExpectedSecondary.get(service.mApprovalLevel).keySet()) { xml.append(getXmlEntry( @@ -1333,11 +1420,7 @@ public class ManagedServicesTest extends UiServiceTestCase { + ManagedServices.ATT_APPROVED_LIST + "=\"98\" />\n"); xml.append("</" + xmlTag + ">"); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(new BufferedInputStream( - new ByteArrayInputStream(xml.toString().getBytes())), null); - parser.nextTag(); - service.readXml(parser, null, false, UserHandle.USER_ALL); + return xml.toString(); } private XmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries) @@ -1524,10 +1607,15 @@ public class ManagedServicesTest extends UiServiceTestCase { } private String getXmlEntry(String approved, int userId, boolean isPrimary) { + return getXmlEntry(approved, userId, isPrimary, true); + } + + private String getXmlEntry(String approved, int userId, boolean isPrimary, boolean userSet) { return "<" + ManagedServices.TAG_MANAGED_SERVICES + " " + ManagedServices.ATT_USER_ID + "=\"" + userId +"\" " + ManagedServices.ATT_IS_PRIMARY + "=\"" + isPrimary +"\" " + ManagedServices.ATT_APPROVED_LIST + "=\"" + approved +"\" " + + ManagedServices.ATT_USER_SET + "=\"" + (userSet ? approved : "") + "\" " + "/>\n"; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index 5796e848ff6e..f649911b6bb9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -134,7 +134,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { verify(mNm, never()).setDefaultAssistantForUser(anyInt()); verify(mAssistants, times(1)).addApprovedList( - new ComponentName("b", "b").flattenToString(),10, true); + new ComponentName("b", "b").flattenToString(), 10, true, null); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 740505e0d3d7..87aaba2b164a 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -111,9 +111,9 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentUris; import android.content.Context; +import android.content.IIntentSender; import android.content.Intent; import android.content.IntentFilter; -import android.content.IIntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; @@ -2880,16 +2880,17 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetListenerAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); ComponentName c = ComponentName.unflattenFromString("package/Component"); - mBinderService.setNotificationListenerAccessGrantedForUser(c, user.getIdentifier(), true); + mBinderService.setNotificationListenerAccessGrantedForUser( + c, user.getIdentifier(), true, true); verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); verify(mListeners, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), user.getIdentifier(), true, true); + c.flattenToString(), user.getIdentifier(), true, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), user.getIdentifier(), false, true); + c.flattenToString(), user.getIdentifier(), false, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); } @Test @@ -2958,12 +2959,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetListenerAccess() throws Exception { ComponentName c = ComponentName.unflattenFromString("package/Component"); - mBinderService.setNotificationListenerAccessGranted(c, true); + mBinderService.setNotificationListenerAccessGranted(c, true, true); verify(mListeners, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), 0, true, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, true); + c.flattenToString(), 0, false, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -3112,12 +3113,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetListenerAccess_onLowRam() throws Exception { when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); - mBinderService.setNotificationListenerAccessGranted(c, true); + mBinderService.setNotificationListenerAccessGranted(c, true, true); verify(mListeners).setPackageOrComponentEnabled( - anyString(), anyInt(), anyBoolean(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); verify(mConditionProviders).setPackageOrComponentEnabled( - anyString(), anyInt(), anyBoolean(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); verify(mAssistants).migrateToXml(); verify(mAssistants).resetDefaultAssistantsIfNecessary(); } @@ -3160,14 +3161,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); - mBinderService.setNotificationListenerAccessGranted(c, true); + mBinderService.setNotificationListenerAccessGranted(c, true, true); verify(mListeners, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), 0, true, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, true); + c.flattenToString(), 0, false, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 91e55ed7ab6a..b6b3d66d9c04 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -17,11 +17,15 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -33,6 +37,8 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.clearInvocations; +import android.app.WindowConfiguration; +import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -220,4 +226,35 @@ public class TaskTests extends WindowTestsBase { assertTrue(activity1.isVisible()); assertTrue(activity2.isVisible()); } + + @Test + public void testResolveNonResizableTaskWindowingMode() { + final Task task = createTaskStackOnDisplay(mDisplayContent); + Configuration parentConfig = task.getParent().getConfiguration(); + parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + doReturn(false).when(task).isResizeable(); + WindowConfiguration requestedOverride = + task.getRequestedOverrideConfiguration().windowConfiguration; + WindowConfiguration resolvedOverride = + task.getResolvedOverrideConfiguration().windowConfiguration; + + // The resolved override windowing mode of a non-resizeable task should be resolved as + // fullscreen even as a child of a freeform display. + requestedOverride.setWindowingMode(WINDOWING_MODE_UNDEFINED); + task.resolveOverrideConfiguration(parentConfig); + assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN); + + // The resolved override windowing mode of a non-resizeable task should be resolved as + // fullscreen, even when requested as freeform windowing mode + requestedOverride.setWindowingMode(WINDOWING_MODE_FREEFORM); + task.resolveOverrideConfiguration(parentConfig); + assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN); + + // The resolved override windowing mode of a non-resizeable task can be undefined as long + // as its parents is not in multi-window mode. + parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + requestedOverride.setWindowingMode(WINDOWING_MODE_UNDEFINED); + task.resolveOverrideConfiguration(parentConfig); + assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index b4480aea3ce4..f1d49d5fc6c2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -976,6 +976,21 @@ public class WindowOrganizerTests extends WindowTestsBase { }); } + @Test + public void testReparentToOrganizedTask() { + final ITaskOrganizer organizer = registerMockOrganizer(); + Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask( + mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null); + final Task task1 = createStack(); + final Task task2 = createTask(rootTask, false /* fakeDraw */); + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.reparent(task1.mRemoteToken.toWindowContainerToken(), + rootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); + assertTrue(task1.isOrganized()); + assertTrue(task2.isOrganized()); + } + /** * Verifies that task vanished is called for a specific task. */ diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java index cfc7ac176f89..b92d410f3d6c 100644 --- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java @@ -125,11 +125,7 @@ public final class CarrierAppUtils { } private static boolean isUpdatedSystemApp(ApplicationInfo ai) { - if ((ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { - return true; - } - - return false; + return (ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 556e0b2b4096..c982f490e0fd 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -5703,14 +5703,14 @@ public class TelephonyManager { public static final int ERI_ICON_MODE_FLASH = 1; /** - * Returns the CDMA ERI icon index to display. The number is assigned by - * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI icon index. + * Returns the CDMA ERI icon display number. The number is assigned by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI display numbers. * Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public @EriIconIndex int getCdmaEnhancedRoamingIndicatorIconIndex() { + public @EriIconIndex int getCdmaEnhancedRoamingIndicatorDisplayNumber() { return getCdmaEriIconIndex(getSubId()); } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 953a2924dc44..b1700a10a288 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -496,6 +496,10 @@ public interface RILConstants { int RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION = 212; int RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY = 213; int RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED = 214; + int RIL_REQUEST_ALLOCATE_PDU_SESSION_ID = 215; + int RIL_REQUEST_RELEASE_PDU_SESSION_ID = 216; + int RIL_REQUEST_BEGIN_HANDOVER = 217; + int RIL_REQUEST_CANCEL_HANDOVER = 218; /* Responses begin */ int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 2a0c99c3bc52..7470cd898396 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -251,6 +251,7 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.AdditionalAnswers; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; @@ -452,6 +453,13 @@ public class ConnectivityServiceTest { } @Override + public Context createContextAsUser(UserHandle user, int flags) { + final Context asUser = mock(Context.class, AdditionalAnswers.delegatesTo(this)); + doReturn(user).when(asUser).getUser(); + return asUser; + } + + @Override public ContentResolver getContentResolver() { return mContentResolver; } @@ -4992,22 +5000,22 @@ public class ConnectivityServiceTest { // simulate that situation and check if ConnectivityService could filter that case. mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); waitForIdle(); - verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).notifyAsUser(anyString(), - eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL)); + verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).notify(anyString(), + eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any()); // If private DNS resolution successful, the PRIVATE_DNS_BROKEN notification shouldn't be // shown. mWiFiNetworkAgent.setNetworkValid(true /* isStrictMode */); mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); waitForIdle(); - verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).cancelAsUser(anyString(), - eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), eq(UserHandle.ALL)); + verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).cancel(anyString(), + eq(NotificationType.PRIVATE_DNS_BROKEN.eventId)); // If private DNS resolution failed again, the PRIVATE_DNS_BROKEN notification should be // shown again. mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */); mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); waitForIdle(); - verify(mNotificationManager, timeout(TIMEOUT_MS).times(2)).notifyAsUser(anyString(), - eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL)); + verify(mNotificationManager, timeout(TIMEOUT_MS).times(2)).notify(anyString(), + eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any()); } @Test diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java index 47db5d431671..b47be97ed002 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; @@ -36,6 +37,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.NetworkCapabilities; import android.net.NetworkInfo; +import android.os.UserHandle; import android.telephony.TelephonyManager; import androidx.test.filters.SmallTest; @@ -47,6 +49,7 @@ import com.android.server.connectivity.NetworkNotificationManager.NotificationTy import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.AdditionalAnswers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -104,17 +107,22 @@ public class NetworkNotificationManagerTest { when(mCtx.getResources()).thenReturn(mResources); when(mCtx.getPackageManager()).thenReturn(mPm); when(mCtx.getApplicationInfo()).thenReturn(new ApplicationInfo()); + final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mCtx)); + doReturn(UserHandle.ALL).when(asUserCtx).getUser(); + when(mCtx.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx); + when(mCtx.getSystemService(eq(Context.NOTIFICATION_SERVICE))) + .thenReturn(mNotificationManager); when(mNetworkInfo.getExtraInfo()).thenReturn("extra"); when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B); - mManager = new NetworkNotificationManager(mCtx, mTelephonyManager, mNotificationManager); + mManager = new NetworkNotificationManager(mCtx, mTelephonyManager); } private void verifyTitleByNetwork(final int id, final NetworkAgentInfo nai, final int title) { final String tag = NetworkNotificationManager.tagFor(id); mManager.showNotification(id, PRIVATE_DNS_BROKEN, nai, null, null, true); verify(mNotificationManager, times(1)) - .notifyAsUser(eq(tag), eq(PRIVATE_DNS_BROKEN.eventId), any(), any()); + .notify(eq(tag), eq(PRIVATE_DNS_BROKEN.eventId), any()); final int transportType = NetworkNotificationManager.approximateTransportType(nai); if (transportType == NetworkCapabilities.TRANSPORT_WIFI) { verify(mResources, times(1)).getString(title, eq(any())); @@ -164,8 +172,8 @@ public class NetworkNotificationManagerTest { final int id = ids.get(i); final int eventId = types.get(i).eventId; final String tag = NetworkNotificationManager.tagFor(id); - verify(mNotificationManager, times(1)).notifyAsUser(eq(tag), eq(eventId), any(), any()); - verify(mNotificationManager, times(1)).cancelAsUser(eq(tag), eq(eventId), any()); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(eventId), any()); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(eventId)); } } @@ -174,13 +182,13 @@ public class NetworkNotificationManagerTest { mManager.showNotification(100, NO_INTERNET, mCellNai, mWifiNai, null, false); mManager.showNotification(101, LOST_INTERNET, mCellNai, mWifiNai, null, false); - verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any()); + verify(mNotificationManager, never()).notify(any(), anyInt(), any()); mManager.showNotification(102, NO_INTERNET, mWifiNai, mCellNai, null, false); final int eventId = NO_INTERNET.eventId; final String tag = NetworkNotificationManager.tagFor(102); - verify(mNotificationManager, times(1)).notifyAsUser(eq(tag), eq(eventId), any(), any()); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(eventId), any()); } @Test @@ -191,7 +199,7 @@ public class NetworkNotificationManagerTest { mManager.showNotification(103, LOST_INTERNET, mWifiNai, mCellNai, null, false); mManager.showNotification(104, NETWORK_SWITCH, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any()); + verify(mNotificationManager, never()).notify(any(), anyInt(), any()); } @Test @@ -201,19 +209,16 @@ public class NetworkNotificationManagerTest { // Show first NO_INTERNET mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(1)) - .notifyAsUser(eq(tag), eq(NO_INTERNET.eventId), any(), any()); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(NO_INTERNET.eventId), any()); // Captive portal detection triggers SIGN_IN a bit later, clearing the previous NO_INTERNET mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(1)) - .cancelAsUser(eq(tag), eq(NO_INTERNET.eventId), any()); - verify(mNotificationManager, times(1)) - .notifyAsUser(eq(tag), eq(SIGN_IN.eventId), any(), any()); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId)); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(SIGN_IN.eventId), any()); // Network disconnects mManager.clearNotification(id); - verify(mNotificationManager, times(1)).cancelAsUser(eq(tag), eq(SIGN_IN.eventId), any()); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId)); } @Test @@ -223,18 +228,17 @@ public class NetworkNotificationManagerTest { // Show first SIGN_IN mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(1)) - .notifyAsUser(eq(tag), eq(SIGN_IN.eventId), any(), any()); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(SIGN_IN.eventId), any()); reset(mNotificationManager); // NO_INTERNET arrives after, but is ignored. mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, never()).cancelAsUser(any(), anyInt(), any()); - verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any()); + verify(mNotificationManager, never()).cancel(any(), anyInt()); + verify(mNotificationManager, never()).notify(any(), anyInt(), any()); // Network disconnects mManager.clearNotification(id); - verify(mNotificationManager, times(1)).cancelAsUser(eq(tag), eq(SIGN_IN.eventId), any()); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId)); } @Test @@ -246,24 +250,20 @@ public class NetworkNotificationManagerTest { // to previous type or not. If they are equal then clear the notification; if they are not // equal then return. mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(1)) - .notifyAsUser(eq(tag), eq(NO_INTERNET.eventId), any(), any()); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(NO_INTERNET.eventId), any()); // Previous notification is NO_INTERNET and given type is NO_INTERNET too. The notification // should be cleared. mManager.clearNotification(id, NO_INTERNET); - verify(mNotificationManager, times(1)) - .cancelAsUser(eq(tag), eq(NO_INTERNET.eventId), any()); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId)); // SIGN_IN is popped-up. mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(1)) - .notifyAsUser(eq(tag), eq(SIGN_IN.eventId), any(), any()); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(SIGN_IN.eventId), any()); // The notification type is not matching previous one, PARTIAL_CONNECTIVITY won't be // cleared. mManager.clearNotification(id, PARTIAL_CONNECTIVITY); - verify(mNotificationManager, never()) - .cancelAsUser(eq(tag), eq(PARTIAL_CONNECTIVITY.eventId), any()); + verify(mNotificationManager, never()).cancel(eq(tag), eq(PARTIAL_CONNECTIVITY.eventId)); } } diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index bc3db11ccd3c..e3ba3e106e14 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -45,6 +45,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -106,6 +107,7 @@ import com.android.server.IpSecService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.AdditionalAnswers; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; @@ -215,6 +217,8 @@ public class VpnTest { when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG); when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager); when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps); + when(mContext.getSystemServiceName(NotificationManager.class)) + .thenReturn(Context.NOTIFICATION_SERVICE); when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE))) .thenReturn(mNotificationManager); when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))) @@ -594,26 +598,23 @@ public class VpnTest { // Don't show a notification for regular disconnected states. vpn.updateState(DetailedState.DISCONNECTED, TAG); - order.verify(mNotificationManager, atLeastOnce()) - .cancelAsUser(anyString(), anyInt(), eq(userHandle)); + order.verify(mNotificationManager, atLeastOnce()).cancel(anyString(), anyInt()); // Start showing a notification for disconnected once always-on. vpn.setAlwaysOnPackage(PKGS[0], false, null, mKeyStore); - order.verify(mNotificationManager) - .notifyAsUser(anyString(), anyInt(), any(), eq(userHandle)); + order.verify(mNotificationManager).notify(anyString(), anyInt(), any()); // Stop showing the notification once connected. vpn.updateState(DetailedState.CONNECTED, TAG); - order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle)); + order.verify(mNotificationManager).cancel(anyString(), anyInt()); // Show the notification if we disconnect again. vpn.updateState(DetailedState.DISCONNECTED, TAG); - order.verify(mNotificationManager) - .notifyAsUser(anyString(), anyInt(), any(), eq(userHandle)); + order.verify(mNotificationManager).notify(anyString(), anyInt(), any()); // Notification should be cleared after unsetting always-on package. vpn.setAlwaysOnPackage(null, false, null, mKeyStore); - order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle)); + order.verify(mNotificationManager).cancel(anyString(), anyInt()); } @Test @@ -1272,6 +1273,10 @@ public class VpnTest { * Mock some methods of vpn object. */ private Vpn createVpn(@UserIdInt int userId) { + final Context asUserContext = mock(Context.class, AdditionalAnswers.delegatesTo(mContext)); + doReturn(UserHandle.of(userId)).when(asUserContext).getUser(); + when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt())) + .thenReturn(asUserContext); return new Vpn(Looper.myLooper(), mContext, new TestDeps(), mNetService, userId, mKeyStore, mSystemServices, mIkev2SessionCreator); } diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt index 5dc5dfc02bce..edbd46300191 100644 --- a/wifi/api/system-current.txt +++ b/wifi/api/system-current.txt @@ -246,6 +246,7 @@ package android.net.wifi { field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR; field public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1L; // 0x1L field public static final long SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 2L; // 0x2L + field public static final long SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION = 8L; // 0x8L field public static final long SOFTAP_FEATURE_WPA3_SAE = 4L; // 0x4L } @@ -336,8 +337,11 @@ package android.net.wifi { field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0 field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1 field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11 + field @Deprecated public static final int RECENT_FAILURE_DISCONNECTION_AP_BUSY = 1004; // 0x3ec field @Deprecated public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001; // 0x3e9 field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0 + field @Deprecated public static final int RECENT_FAILURE_POOR_CHANNEL_CONDITIONS = 1003; // 0x3eb + field @Deprecated public static final int RECENT_FAILURE_REFUSED_TEMPORARILY = 1002; // 0x3ea field @Deprecated public boolean allowAutojoin; field @Deprecated public int carrierId; field @Deprecated public String creatorName; @@ -424,8 +428,11 @@ package android.net.wifi { method public double getSuccessfulRxPacketsPerSecond(); method public double getSuccessfulTxPacketsPerSecond(); method public boolean isEphemeral(); + method public boolean isOemPaid(); + method public boolean isOemPrivate(); method public boolean isOsuAp(); method public boolean isPasspointAp(); + method public boolean isTrusted(); method @Nullable public static String sanitizeSsid(@Nullable String); field public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00"; field public static final int INVALID_RSSI = -127; // 0xffffff81 @@ -614,11 +621,13 @@ package android.net.wifi { public final class WifiNetworkSuggestion implements android.os.Parcelable { method @NonNull public android.net.wifi.WifiConfiguration getWifiConfiguration(); method public boolean isOemPaid(); + method public boolean isOemPrivate(); } public static final class WifiNetworkSuggestion.Builder { method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int); method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setOemPaid(boolean); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setOemPrivate(boolean); } public class WifiScanner { diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt index b0ff4bb27e70..ff06a180b8c1 100644 --- a/wifi/jarjar-rules.txt +++ b/wifi/jarjar-rules.txt @@ -36,7 +36,7 @@ rule android.net.ipmemorystore.IOnStatusListener* com.android.wifi.x.@0 rule android.net.ipmemorystore.NetworkAttributesParcelable* com.android.wifi.x.@0 rule android.net.ipmemorystore.SameL3NetworkResponseParcelable* com.android.wifi.x.@0 rule android.net.ipmemorystore.StatusParcelable* com.android.wifi.x.@0 -rule android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk* com.android.wifi.x.@0 +rule android.net.networkstack.aidl.** com.android.wifi.x.@0 # Net utils (includes Network Stack helper classes). rule android.net.DhcpResults* com.android.wifi.x.@0 diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java index 6bd4211d8214..d570b7e8afa9 100644 --- a/wifi/java/android/net/wifi/SoftApCapability.java +++ b/wifi/java/android/net/wifi/SoftApCapability.java @@ -46,7 +46,7 @@ public final class SoftApCapability implements Parcelable { * Support for automatic channel selection in driver (ACS). * Driver will auto select best channel based on interference to optimize performance. * - * flag when {@link R.bool.config_wifi_softap_acs_supported)} is true. + * flag when {@link R.bool.config_wifi_softap_acs_supported} is true. * * <p> * Use {@link WifiManager.SoftApCallback#onInfoChanged(SoftApInfo)} and @@ -57,7 +57,7 @@ public final class SoftApCapability implements Parcelable { /** * Support for client force disconnect. - * flag when {@link R.bool.config_wifi_sofap_client_force_disconnect_supported)} is true + * flag when {@link R.bool.config_wifiSofapClientForceDisconnectSupported} is true * * <p> * Several Soft AP client control features, e.g. specifying the maximum number of @@ -67,20 +67,32 @@ public final class SoftApCapability implements Parcelable { */ public static final long SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 1 << 1; - /** * Support for WPA3 Simultaneous Authentication of Equals (WPA3-SAE). * - * flag when {@link config_wifi_softap_sae_supported)} is true. + * flag when {@link config_wifi_softap_sae_supported} is true. */ public static final long SOFTAP_FEATURE_WPA3_SAE = 1 << 2; + /** + * Support for MAC address customization. + * flag when {@link R.bool.config_wifiSoftapMacAddressCustomizationSupported} is true + * + * <p> + * Check feature support before invoking + * {@link SoftApConfiguration.Builder#setBssid(MadAddress)} or + * {@link SoftApConfiguration.Builder#setMacRandomizationSetting(int)} with + * {@link SoftApConfiguration.RANDOMIZATION_PERSISTENT} + */ + public static final long SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION = 1 << 3; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @LongDef(flag = true, prefix = { "SOFTAP_FEATURE_" }, value = { SOFTAP_FEATURE_ACS_OFFLOAD, SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT, SOFTAP_FEATURE_WPA3_SAE, + SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION, }) public @interface HotspotFeatures {} diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index 2c53daaf55a7..c0f6e7a20f95 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -819,6 +819,11 @@ public final class SoftApConfiguration implements Parcelable { * Derived MAC address 1: e2:c7:60:c4:0e:b7 * Derived MAC address 2: e2:38:9f:c4:0e:b7 * + * <p> + * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and + * {@link SoftApCapability#areFeaturesSupported(long)} + * with {@link SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION} to determine + * whether or not this feature is supported. * * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is * responsible for avoiding collisions. @@ -957,9 +962,9 @@ public final class SoftApConfiguration implements Parcelable { * {@link #setBand(int)}. * * The channel auto selection will be offloaded to driver when - * {@link SoftApCapability#areFeaturesSupported( - * SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)} - * returns true. The driver will auto select the best channel (e.g. best performance) + * {@link SoftApCapability#areFeaturesSupported(long)} + * with {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD} + * return true. The driver will auto select the best channel (e.g. best performance) * based on environment interference. Check {@link SoftApCapability} for more detail. * * The API contains (band, channel) input since the 6GHz band uses the same channel @@ -998,8 +1003,8 @@ public final class SoftApConfiguration implements Parcelable { * auto-select a valid channel from the band configured with {@link #setBands(int[])}. * * The channel auto selection will be offloaded to driver when - * {@link SoftApCapability#areFeaturesSupported( - * SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)} + * {@link SoftApCapability#areFeaturesSupported(long)} + * with {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD} * returns true. The driver will auto select the best channel (e.g. best performance) * based on environment interference. Check {@link SoftApCapability} for more detail. * @@ -1046,14 +1051,14 @@ public final class SoftApConfiguration implements Parcelable { * <p> * <li>If not set, defaults to 0.</li> * - * This method requires hardware support. If the method is used to set a + * This method requires HAL support. If the method is used to set a * non-zero {@code maxNumberOfClients} value then * {@link WifiManager#startTetheredHotspot} will report error code * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}. * * <p> * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and - * {@link SoftApCapability#areFeaturesSupported(int)} + * {@link SoftApCapability#areFeaturesSupported(long)} * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} to determine whether * or not this feature is supported. * @@ -1127,13 +1132,13 @@ public final class SoftApConfiguration implements Parcelable { * {@link #setBlockedClientList(List)} and {@link #setAllowedClientList(List)}. * * <p> - * This method requires hardware support. Hardware support can be determined using + * This method requires HAL support. HAL support can be determined using * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and - * {@link SoftApCapability#areFeaturesSupported(int)} + * {@link SoftApCapability#areFeaturesSupported(long)} * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} * * <p> - * If the method is called on a device without hardware support then starting the soft AP + * If the method is called on a device without HAL support then starting the soft AP * using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}. * @@ -1182,13 +1187,13 @@ public final class SoftApConfiguration implements Parcelable { * to the Soft AP. * * <p> - * This method requires hardware support. Hardware support can be determined using + * This method requires HAL support. HAL support can be determined using * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and - * {@link SoftApCapability#areFeaturesSupported(int)} + * {@link SoftApCapability#areFeaturesSupported(long)} * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} * * <p> - * If the method is called on a device without hardware support then starting the soft AP + * If the method is called on a device without HAL support then starting the soft AP * using {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will fail with * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}. * @@ -1210,7 +1215,14 @@ public final class SoftApConfiguration implements Parcelable { * <p> * <li>If not set, defaults to {@link #RANDOMIZATION_PERSISTENT}</li> * - * @param macRandomizationSetting One of the following setting:. + * <p> + * Requires HAL support when set to {@link #RANDOMIZATION_PERSISTENT}. + * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and + * {@link SoftApCapability#areFeaturesSupported(long)} + * with {@link SoftApCapability.SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION} to determine + * whether or not this feature is supported. + * + * @param macRandomizationSetting One of the following setting: * {@link #RANDOMIZATION_NONE} or {@link #RANDOMIZATION_PERSISTENT}. * @return Builder for chaining. * diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 9a8a5ad4b41f..8103ff717ef3 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -1011,6 +1011,17 @@ public class WifiConfiguration implements Parcelable { */ public boolean oemPaid; + + /** + * Indicate whether the network is oem private or not. Networks are considered oem private + * if the corresponding connection is only available to system apps. + * + * This bit can only be used by suggestion network, see + * {@link WifiNetworkSuggestion.Builder#setOemPrivate(boolean)} + * @hide + */ + public boolean oemPrivate; + /** * True if this Wifi configuration is created from a {@link WifiNetworkSuggestion}, * false otherwise. @@ -2074,27 +2085,42 @@ public class WifiConfiguration implements Parcelable { */ @RecentFailureReason private int mAssociationStatus = RECENT_FAILURE_NONE; + private long mLastUpdateTimeSinceBootMillis; /** * @param status the association status code for the recent failure */ - public void setAssociationStatus(@RecentFailureReason int status) { + public void setAssociationStatus(@RecentFailureReason int status, + long updateTimeSinceBootMs) { mAssociationStatus = status; + mLastUpdateTimeSinceBootMillis = updateTimeSinceBootMs; } /** * Sets the RecentFailure to NONE */ public void clear() { mAssociationStatus = RECENT_FAILURE_NONE; + mLastUpdateTimeSinceBootMillis = 0; } /** - * Get the recent failure code. One of {@link #RECENT_FAILURE_NONE} or - * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}. + * Get the recent failure code. One of {@link #RECENT_FAILURE_NONE}, + * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}, + * {@link #RECENT_FAILURE_MBO_OCE_DISCONNECT}, + * {@link #RECENT_FAILURE_REFUSED_TEMPORARILY}, + * {@link #RECENT_FAILURE_POOR_CHANNEL_CONDITIONS}. + * {@link #RECENT_FAILURE_DISCONNECTION_AP_BUSY} */ @RecentFailureReason public int getAssociationStatus() { return mAssociationStatus; } + + /** + * Get the timestamp the failure status is last updated, in milliseconds since boot. + */ + public long getLastUpdateTimeSinceBootMillis() { + return mLastUpdateTimeSinceBootMillis; + } } /** @@ -2111,7 +2137,11 @@ public class WifiConfiguration implements Parcelable { @IntDef(prefix = "RECENT_FAILURE_", value = { RECENT_FAILURE_NONE, RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA, - RECENT_FAILURE_MBO_OCE_DISCONNECT}) + RECENT_FAILURE_MBO_OCE_DISCONNECT, + RECENT_FAILURE_REFUSED_TEMPORARILY, + RECENT_FAILURE_POOR_CHANNEL_CONDITIONS, + RECENT_FAILURE_DISCONNECTION_AP_BUSY + }) public @interface RecentFailureReason {} /** @@ -2136,12 +2166,39 @@ public class WifiConfiguration implements Parcelable { public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001; /** + * Failed to connect because the association is rejected by the AP. + * IEEE 802.11 association status code 30. + * @hide + */ + @SystemApi + public static final int RECENT_FAILURE_REFUSED_TEMPORARILY = 1002; + + /** + * Failed to connect because of excess frame loss and/or poor channel conditions. + * IEEE 802.11 association status code 34. + * @hide + */ + @SystemApi + public static final int RECENT_FAILURE_POOR_CHANNEL_CONDITIONS = 1003; + + /** + * Disconnected by the AP because the AP can't handle all the associated stations. + * IEEE 802.11 disconnection reason code 5. + * @hide + */ + @SystemApi + public static final int RECENT_FAILURE_DISCONNECTION_AP_BUSY = 1004; + + /** * Get the failure reason for the most recent connection attempt, or * {@link #RECENT_FAILURE_NONE} if there was no failure. * * Failure reasons include: * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA} - * + * {@link #RECENT_FAILURE_MBO_OCE_DISCONNECT} + * {@link #RECENT_FAILURE_REFUSED_TEMPORARILY} + * {@link #RECENT_FAILURE_POOR_CHANNEL_CONDITIONS} + * {@link #RECENT_FAILURE_DISCONNECTION_AP_BUSY} * @hide */ @RecentFailureReason @@ -2203,6 +2260,7 @@ public class WifiConfiguration implements Parcelable { osu = false; trusted = true; // Networks are considered trusted by default. oemPaid = false; + oemPrivate = false; fromWifiNetworkSuggestion = false; fromWifiNetworkSpecifier = false; meteredHint = false; @@ -2326,13 +2384,14 @@ public class WifiConfiguration implements Parcelable { if (this.osu) sbuf.append(" osu"); if (this.trusted) sbuf.append(" trusted"); if (this.oemPaid) sbuf.append(" oemPaid"); + if (this.oemPrivate) sbuf.append(" oemPrivate"); if (this.fromWifiNetworkSuggestion) sbuf.append(" fromWifiNetworkSuggestion"); if (this.fromWifiNetworkSpecifier) sbuf.append(" fromWifiNetworkSpecifier"); if (this.meteredHint) sbuf.append(" meteredHint"); if (this.useExternalScores) sbuf.append(" useExternalScores"); if (this.validatedInternetAccess || this.ephemeral || this.trusted || this.oemPaid - || this.fromWifiNetworkSuggestion || this.fromWifiNetworkSpecifier - || this.meteredHint || this.useExternalScores) { + || this.oemPrivate || this.fromWifiNetworkSuggestion + || this.fromWifiNetworkSpecifier || this.meteredHint || this.useExternalScores) { sbuf.append("\n"); } if (this.meteredOverride != METERED_OVERRIDE_NONE) { @@ -2479,7 +2538,8 @@ public class WifiConfiguration implements Parcelable { } } sbuf.append("recentFailure: ").append("Association Rejection code: ") - .append(recentFailure.getAssociationStatus()).append("\n"); + .append(recentFailure.getAssociationStatus()).append(", last update time: ") + .append(recentFailure.getLastUpdateTimeSinceBootMillis()).append("\n"); return sbuf.toString(); } @@ -2896,6 +2956,7 @@ public class WifiConfiguration implements Parcelable { osu = source.osu; trusted = source.trusted; oemPaid = source.oemPaid; + oemPrivate = source.oemPrivate; fromWifiNetworkSuggestion = source.fromWifiNetworkSuggestion; fromWifiNetworkSpecifier = source.fromWifiNetworkSpecifier; meteredHint = source.meteredHint; @@ -2918,7 +2979,8 @@ public class WifiConfiguration implements Parcelable { numNoInternetAccessReports = source.numNoInternetAccessReports; noInternetAccessExpected = source.noInternetAccessExpected; shared = source.shared; - recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus()); + recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus(), + source.recentFailure.getLastUpdateTimeSinceBootMillis()); mRandomizedMacAddress = source.mRandomizedMacAddress; macRandomizationSetting = source.macRandomizationSetting; randomizedMacExpirationTimeMs = source.randomizedMacExpirationTimeMs; @@ -2976,6 +3038,7 @@ public class WifiConfiguration implements Parcelable { dest.writeInt(ephemeral ? 1 : 0); dest.writeInt(trusted ? 1 : 0); dest.writeInt(oemPaid ? 1 : 0); + dest.writeInt(oemPrivate ? 1 : 0); dest.writeInt(fromWifiNetworkSuggestion ? 1 : 0); dest.writeInt(fromWifiNetworkSpecifier ? 1 : 0); dest.writeInt(meteredHint ? 1 : 0); @@ -2995,6 +3058,7 @@ public class WifiConfiguration implements Parcelable { dest.writeInt(shared ? 1 : 0); dest.writeString(mPasspointManagementObjectTree); dest.writeInt(recentFailure.getAssociationStatus()); + dest.writeLong(recentFailure.getLastUpdateTimeSinceBootMillis()); dest.writeParcelable(mRandomizedMacAddress, flags); dest.writeInt(macRandomizationSetting); dest.writeInt(osu ? 1 : 0); @@ -3053,6 +3117,7 @@ public class WifiConfiguration implements Parcelable { config.ephemeral = in.readInt() != 0; config.trusted = in.readInt() != 0; config.oemPaid = in.readInt() != 0; + config.oemPrivate = in.readInt() != 0; config.fromWifiNetworkSuggestion = in.readInt() != 0; config.fromWifiNetworkSpecifier = in.readInt() != 0; config.meteredHint = in.readInt() != 0; @@ -3071,7 +3136,7 @@ public class WifiConfiguration implements Parcelable { config.noInternetAccessExpected = in.readInt() != 0; config.shared = in.readInt() != 0; config.mPasspointManagementObjectTree = in.readString(); - config.recentFailure.setAssociationStatus(in.readInt()); + config.recentFailure.setAssociationStatus(in.readInt(), in.readLong()); config.mRandomizedMacAddress = in.readParcelable(null); config.macRandomizationSetting = in.readInt(); config.osu = in.readInt() != 0; diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index fe5002eb8854..774c043136e7 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -164,6 +164,11 @@ public class WifiInfo implements Parcelable { private boolean mOemPaid; /** + * Whether the network is oem private or not. + */ + private boolean mOemPrivate; + + /** * OSU (Online Sign Up) AP for Passpoint R2. */ private boolean mOsuAp; @@ -327,6 +332,9 @@ public class WifiInfo implements Parcelable { setFrequency(-1); setMeteredHint(false); setEphemeral(false); + setTrusted(false); + setOemPaid(false); + setOemPrivate(false); setOsuAp(false); setRequestingPackageName(null); setFQDN(null); @@ -363,7 +371,8 @@ public class WifiInfo implements Parcelable { mMeteredHint = source.mMeteredHint; mEphemeral = source.mEphemeral; mTrusted = source.mTrusted; - mTrusted = source.mOemPaid; + mOemPaid = source.mOemPaid; + mOemPrivate = source.mOemPrivate; mRequestingPackageName = source.mRequestingPackageName; mOsuAp = source.mOsuAp; @@ -722,7 +731,12 @@ public class WifiInfo implements Parcelable { mTrusted = trusted; } - /** {@hide} */ + /** + * Returns true if the current Wifi network is a trusted network, false otherwise. + * @see WifiNetworkSuggestion.Builder#setUntrusted(boolean). + * {@hide} + */ + @SystemApi public boolean isTrusted() { return mTrusted; } @@ -732,12 +746,32 @@ public class WifiInfo implements Parcelable { mOemPaid = oemPaid; } - /** {@hide} */ + /** + * Returns true if the current Wifi network is an oem paid network, false otherwise. + * @see WifiNetworkSuggestion.Builder#setOemPaid(boolean). + * {@hide} + */ + @SystemApi public boolean isOemPaid() { return mOemPaid; } /** {@hide} */ + public void setOemPrivate(boolean oemPrivate) { + mOemPrivate = oemPrivate; + } + + /** + * Returns true if the current Wifi network is an oem private network, false otherwise. + * @see WifiNetworkSuggestion.Builder#setOemPrivate(boolean). + * {@hide} + */ + @SystemApi + public boolean isOemPrivate() { + return mOemPrivate; + } + + /** {@hide} */ public void setOsuAp(boolean osuAp) { mOsuAp = osuAp; } @@ -975,6 +1009,7 @@ public class WifiInfo implements Parcelable { dest.writeInt(mEphemeral ? 1 : 0); dest.writeInt(mTrusted ? 1 : 0); dest.writeInt(mOemPaid ? 1 : 0); + dest.writeInt(mOemPrivate ? 1 : 0); dest.writeInt(score); dest.writeLong(txSuccess); dest.writeDouble(mSuccessfulTxPacketsPerSecond); @@ -1021,6 +1056,7 @@ public class WifiInfo implements Parcelable { info.mEphemeral = in.readInt() != 0; info.mTrusted = in.readInt() != 0; info.mOemPaid = in.readInt() != 0; + info.mOemPrivate = in.readInt() != 0; info.score = in.readInt(); info.txSuccess = in.readLong(); info.mSuccessfulTxPacketsPerSecond = in.readDouble(); diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index acae218b74e0..dc6ec907ab95 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.net.MacAddress; import android.net.NetworkCapabilities; +import android.net.NetworkRequest; import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Parcel; import android.os.Parcelable; @@ -180,6 +181,12 @@ public final class WifiNetworkSuggestion implements Parcelable { private boolean mIsNetworkOemPaid; /** + * Whether this network will be brought up as OEM private (OEM_PRIVATE capability bit + * added). + */ + private boolean mIsNetworkOemPrivate; + + /** * Whether this network will use enhanced MAC randomization. */ private boolean mIsEnhancedMacRandomizationEnabled; @@ -206,6 +213,7 @@ public final class WifiNetworkSuggestion implements Parcelable { mWapiEnterpriseConfig = null; mIsNetworkUntrusted = false; mIsNetworkOemPaid = false; + mIsNetworkOemPrivate = false; mPriorityGroup = 0; mIsEnhancedMacRandomizationEnabled = false; mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -667,6 +675,9 @@ public final class WifiNetworkSuggestion implements Parcelable { * reduce it). The connectivity service may use this information to influence the overall * network configuration of the device. * <p> + * <li> These suggestions are only considered for network selection if a + * {@link NetworkRequest} without {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} + * capability is filed. * <li> An untrusted network's credentials may not be shared with the user using * {@link #setCredentialSharedWithUser(boolean)}.</li> * <li> If not set, defaults to false (i.e. network is trusted).</li> @@ -688,7 +699,7 @@ public final class WifiNetworkSuggestion implements Parcelable { * <li>The connectivity service may use this information to influence the overall * network configuration of the device. This network is typically only available to system * apps. - * <li>On devices which support only 1 concurrent connection (indicated via + * <li>On devices which do not support concurrent connection (indicated via * {@link WifiManager#isMultiStaConcurrencySupported()}, Wi-Fi network selection process may * use this information to influence priority of the suggested network for Wi-Fi network * selection (most likely to reduce it). @@ -699,6 +710,13 @@ public final class WifiNetworkSuggestion implements Parcelable { * <p> * <li> An OEM paid network's credentials may not be shared with the user using * {@link #setCredentialSharedWithUser(boolean)}.</li> + * <li> These suggestions are only considered for network selection if a + * {@link NetworkRequest} with {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID} + * capability is filed. + * <li> Each suggestion can have both {@link #setOemPaid(boolean)} and + * {@link #setOemPrivate(boolean)} set if the app wants these suggestions considered + * for creating either an OEM paid network or OEM private network determined based on + * the {@link NetworkRequest} that is active. * <li> If not set, defaults to false (i.e. network is not OEM paid).</li> * * @param isOemPaid Boolean indicating whether the network should be brought up as OEM paid @@ -715,6 +733,48 @@ public final class WifiNetworkSuggestion implements Parcelable { return this; } + /** + * Specifies whether the system will bring up the network (if selected) as OEM private. An + * OEM private network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE} capability + * added. + * Note: + * <li>The connectivity service may use this information to influence the overall + * network configuration of the device. This network is typically only available to system + * apps. + * <li>On devices which do not support concurrent connection (indicated via + * {@link WifiManager#isMultiStaConcurrencySupported()}, Wi-Fi network selection process may + * use this information to influence priority of the suggested network for Wi-Fi network + * selection (most likely to reduce it). + * <li>On devices which support more than 1 concurrent connections (indicated via + * {@link WifiManager#isMultiStaConcurrencySupported()}, these OEM private networks will be + * brought up as a secondary concurrent connection (primary connection will be used + * for networks available to the user and all apps. + * <p> + * <li> An OEM private network's credentials may not be shared with the user using + * {@link #setCredentialSharedWithUser(boolean)}.</li> + * <li> These suggestions are only considered for network selection if a + * {@link NetworkRequest} with {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE} + * capability is filed. + * <li> Each suggestion can have both {@link #setOemPaid(boolean)} and + * {@link #setOemPrivate(boolean)} set if the app wants these suggestions considered + * for creating either an OEM paid network or OEM private network determined based on + * the {@link NetworkRequest} that is active. + * <li> If not set, defaults to false (i.e. network is not OEM private).</li> + * + * @param isOemPrivate Boolean indicating whether the network should be brought up as OEM + * private (if true) or not OEM private (if false). + * @return Instance of {@link Builder} to enable chaining of the builder method. + * @hide + */ + @SystemApi + public @NonNull Builder setOemPrivate(boolean isOemPrivate) { + if (!SdkLevel.isAtLeastS()) { + throw new UnsupportedOperationException(); + } + mIsNetworkOemPrivate = isOemPrivate; + return this; + } + private void setSecurityParamsInWifiConfiguration( @NonNull WifiConfiguration configuration) { if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network. @@ -786,6 +846,7 @@ public final class WifiNetworkSuggestion implements Parcelable { wifiConfiguration.carrierId = mCarrierId; wifiConfiguration.trusted = !mIsNetworkUntrusted; wifiConfiguration.oemPaid = mIsNetworkOemPaid; + wifiConfiguration.oemPrivate = mIsNetworkOemPrivate; wifiConfiguration.macRandomizationSetting = mIsEnhancedMacRandomizationEnabled ? WifiConfiguration.RANDOMIZATION_ENHANCED : WifiConfiguration.RANDOMIZATION_PERSISTENT; @@ -819,6 +880,7 @@ public final class WifiNetworkSuggestion implements Parcelable { wifiConfiguration.meteredOverride = mMeteredOverride; wifiConfiguration.trusted = !mIsNetworkUntrusted; wifiConfiguration.oemPaid = mIsNetworkOemPaid; + wifiConfiguration.oemPrivate = mIsNetworkOemPrivate; wifiConfiguration.subscriptionId = mSubscriptionId; mPasspointConfiguration.setCarrierId(mCarrierId); mPasspointConfiguration.setSubscriptionId(mSubscriptionId); @@ -938,6 +1000,14 @@ public final class WifiNetworkSuggestion implements Parcelable { } mIsSharedWithUser = false; } + if (mIsNetworkOemPrivate) { + if (mIsSharedWithUserSet && mIsSharedWithUser) { + throw new IllegalStateException("Should not be both" + + "setCredentialSharedWithUser and +" + + "setOemPrivate to true"); + } + mIsSharedWithUser = false; + } return new WifiNetworkSuggestion( wifiConfiguration, mPasspointConfiguration, @@ -1105,6 +1175,7 @@ public final class WifiNetworkSuggestion implements Parcelable { .append(", isInitialAutoJoinEnabled=").append(isInitialAutoJoinEnabled) .append(", isUnTrusted=").append(!wifiConfiguration.trusted) .append(", isOemPaid=").append(wifiConfiguration.oemPaid) + .append(", isOemPrivate=").append(wifiConfiguration.oemPrivate) .append(", priorityGroup=").append(priorityGroup) .append(" ]"); return sb.toString(); @@ -1212,6 +1283,18 @@ public final class WifiNetworkSuggestion implements Parcelable { } /** + * @see Builder#setOemPrivate(boolean) + * @hide + */ + @SystemApi + public boolean isOemPrivate() { + if (!SdkLevel.isAtLeastS()) { + throw new UnsupportedOperationException(); + } + return wifiConfiguration.oemPrivate; + } + + /** * Get the WifiEnterpriseConfig, or null if unset. * @see Builder#setWapiEnterpriseConfig(WifiEnterpriseConfig) * @see Builder#setWpa2EnterpriseConfig(WifiEnterpriseConfig) diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java index b82c67b658aa..f09c37d811f9 100644 --- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java @@ -69,6 +69,7 @@ public class WifiConfigurationTest { config.setPasspointManagementObjectTree(cookie); config.trusted = false; config.oemPaid = true; + config.oemPrivate = true; config.updateIdentifier = "1234"; config.fromWifiNetworkSpecifier = true; config.fromWifiNetworkSuggestion = true; @@ -91,8 +92,10 @@ public class WifiConfigurationTest { assertEquals(macBeforeParcel, reconfig.getRandomizedMacAddress()); assertEquals(config.updateIdentifier, reconfig.updateIdentifier); assertFalse(reconfig.trusted); - assertTrue(config.fromWifiNetworkSpecifier); - assertTrue(config.fromWifiNetworkSuggestion); + assertTrue(reconfig.fromWifiNetworkSpecifier); + assertTrue(reconfig.fromWifiNetworkSuggestion); + assertTrue(reconfig.oemPaid); + assertTrue(reconfig.oemPrivate); Parcel parcelWW = Parcel.obtain(); reconfig.writeToParcel(parcelWW, 0); @@ -103,6 +106,32 @@ public class WifiConfigurationTest { } @Test + public void testWifiConfigurationCopyConstructor() { + WifiConfiguration config = new WifiConfiguration(); + config.trusted = false; + config.oemPaid = true; + config.oemPrivate = true; + config.updateIdentifier = "1234"; + config.fromWifiNetworkSpecifier = true; + config.fromWifiNetworkSuggestion = true; + config.setRandomizedMacAddress(MacAddressUtils.createRandomUnicastAddress()); + MacAddress macBeforeParcel = config.getRandomizedMacAddress(); + config.subscriptionId = 1; + config.carrierId = 1189; + + WifiConfiguration reconfig = new WifiConfiguration(config); + + // lacking a useful config.equals, check two fields near the end. + assertEquals(macBeforeParcel, reconfig.getRandomizedMacAddress()); + assertEquals(config.updateIdentifier, reconfig.updateIdentifier); + assertFalse(reconfig.trusted); + assertTrue(reconfig.fromWifiNetworkSpecifier); + assertTrue(reconfig.fromWifiNetworkSuggestion); + assertTrue(reconfig.oemPaid); + assertTrue(reconfig.oemPrivate); + } + + @Test public void testIsOpenNetwork_IsOpen_NullWepKeys() { WifiConfiguration config = new WifiConfiguration(); config.allowedKeyManagement.clear(); diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java index 06ae13a21e38..c6faf66140e2 100644 --- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java +++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java @@ -17,6 +17,7 @@ package android.net.wifi; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; @@ -62,6 +63,7 @@ public class WifiInfoTest { writeWifiInfo.rxSuccess = TEST_RX_SUCCESS; writeWifiInfo.setTrusted(true); writeWifiInfo.setOemPaid(true); + writeWifiInfo.setOemPrivate(true); writeWifiInfo.setOsuAp(true); writeWifiInfo.setFQDN(TEST_FQDN); writeWifiInfo.setProviderFriendlyName(TEST_PROVIDER_NAME); @@ -83,6 +85,46 @@ public class WifiInfoTest { assertEquals(TEST_RX_SUCCESS, readWifiInfo.rxSuccess); assertTrue(readWifiInfo.isTrusted()); assertTrue(readWifiInfo.isOemPaid()); + assertTrue(readWifiInfo.isOemPrivate()); + assertTrue(readWifiInfo.isOsuAp()); + assertTrue(readWifiInfo.isPasspointAp()); + assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getRequestingPackageName()); + assertEquals(TEST_FQDN, readWifiInfo.getPasspointFqdn()); + assertEquals(TEST_PROVIDER_NAME, readWifiInfo.getPasspointProviderFriendlyName()); + assertEquals(TEST_WIFI_STANDARD, readWifiInfo.getWifiStandard()); + assertEquals(TEST_MAX_SUPPORTED_TX_LINK_SPEED_MBPS, + readWifiInfo.getMaxSupportedTxLinkSpeedMbps()); + assertEquals(TEST_MAX_SUPPORTED_RX_LINK_SPEED_MBPS, + readWifiInfo.getMaxSupportedRxLinkSpeedMbps()); + } + + @Test + public void testWifiInfoCopyConstructor() throws Exception { + WifiInfo writeWifiInfo = new WifiInfo(); + writeWifiInfo.txSuccess = TEST_TX_SUCCESS; + writeWifiInfo.txRetries = TEST_TX_RETRIES; + writeWifiInfo.txBad = TEST_TX_BAD; + writeWifiInfo.rxSuccess = TEST_RX_SUCCESS; + writeWifiInfo.setTrusted(true); + writeWifiInfo.setOemPaid(true); + writeWifiInfo.setOemPrivate(true); + writeWifiInfo.setOsuAp(true); + writeWifiInfo.setFQDN(TEST_FQDN); + writeWifiInfo.setProviderFriendlyName(TEST_PROVIDER_NAME); + writeWifiInfo.setRequestingPackageName(TEST_PACKAGE_NAME); + writeWifiInfo.setWifiStandard(TEST_WIFI_STANDARD); + writeWifiInfo.setMaxSupportedTxLinkSpeedMbps(TEST_MAX_SUPPORTED_TX_LINK_SPEED_MBPS); + writeWifiInfo.setMaxSupportedRxLinkSpeedMbps(TEST_MAX_SUPPORTED_RX_LINK_SPEED_MBPS); + + WifiInfo readWifiInfo = new WifiInfo(writeWifiInfo); + + assertEquals(TEST_TX_SUCCESS, readWifiInfo.txSuccess); + assertEquals(TEST_TX_RETRIES, readWifiInfo.txRetries); + assertEquals(TEST_TX_BAD, readWifiInfo.txBad); + assertEquals(TEST_RX_SUCCESS, readWifiInfo.rxSuccess); + assertTrue(readWifiInfo.isTrusted()); + assertTrue(readWifiInfo.isOemPaid()); + assertTrue(readWifiInfo.isOemPrivate()); assertTrue(readWifiInfo.isOsuAp()); assertTrue(readWifiInfo.isPasspointAp()); assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getRequestingPackageName()); @@ -110,6 +152,8 @@ public class WifiInfoTest { assertEquals(WifiManager.UNKNOWN_SSID, wifiInfo.getSSID()); assertEquals(null, wifiInfo.getBSSID()); assertEquals(-1, wifiInfo.getNetworkId()); + assertFalse(wifiInfo.isOemPaid()); + assertFalse(wifiInfo.isOemPrivate()); } /** diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java index 56e79983817f..870ff0a26d58 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java @@ -223,6 +223,34 @@ public class WifiNetworkSuggestionTest { /** * Validate correctness of WifiNetworkSuggestion object created by + * {@link WifiNetworkSuggestion.Builder#build()} for OWE network. + */ + @Test + public void testWifiNetworkSuggestionBuilderForOemPrivateEnhancedOpenNetworkWithBssid() { + assumeTrue(SdkLevel.isAtLeastS()); + + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setBssid(MacAddress.fromString(TEST_BSSID)) + .setOemPrivate(true) + .setIsEnhancedOpen(true) + .build(); + + assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); + assertEquals(TEST_BSSID, suggestion.wifiConfiguration.BSSID); + assertTrue(suggestion.wifiConfiguration.allowedKeyManagement + .get(WifiConfiguration.KeyMgmt.OWE)); + assertNull(suggestion.wifiConfiguration.preSharedKey); + assertTrue(suggestion.wifiConfiguration.requirePmf); + assertTrue(suggestion.wifiConfiguration.oemPrivate); + assertTrue(suggestion.isOemPrivate()); + assertFalse(suggestion.isUserAllowedToManuallyConnect); + assertTrue(suggestion.isInitialAutoJoinEnabled); + assertNull(suggestion.getEnterpriseConfig()); + } + + /** + * Validate correctness of WifiNetworkSuggestion object created by * {@link WifiNetworkSuggestion.Builder#build()} for SAE network. */ @Test @@ -1285,6 +1313,41 @@ public class WifiNetworkSuggestionTest { } /** + * Validate {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} set the + * correct value to the WifiConfiguration. + */ + @Test + public void testSetIsNetworkAsOemPrivate() { + assumeTrue(SdkLevel.isAtLeastS()); + + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setWpa2Passphrase(TEST_PRESHARED_KEY) + .setOemPrivate(true) + .build(); + assertTrue(suggestion.isOemPrivate()); + assertFalse(suggestion.isUserAllowedToManuallyConnect); + } + + /** + * Validate {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} set the + * correct value to the WifiConfiguration. + * Also the {@link WifiNetworkSuggestion#isUserAllowedToManuallyConnect} should be false; + */ + @Test + public void testSetIsNetworkAsOemPrivateOnPasspointNetwork() { + assumeTrue(SdkLevel.isAtLeastS()); + + PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig(); + WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() + .setPasspointConfig(passpointConfiguration) + .setOemPrivate(true) + .build(); + assertTrue(suggestion.isOemPrivate()); + assertFalse(suggestion.isUserAllowedToManuallyConnect); + } + + /** * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception * when set {@link WifiNetworkSuggestion.Builder#setUntrusted(boolean)} to true and * set {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} to true @@ -1320,6 +1383,24 @@ public class WifiNetworkSuggestionTest { /** * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception + * when set {@link WifiNetworkSuggestion.Builder#setOemPrivate(boolean)} to true and + * set {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} to true + * together. + */ + @Test(expected = IllegalStateException.class) + public void testSetCredentialSharedWithUserWithSetIsNetworkAsOemPrivate() { + assumeTrue(SdkLevel.isAtLeastS()); + + new WifiNetworkSuggestion.Builder() + .setSsid(TEST_SSID) + .setWpa2Passphrase(TEST_PRESHARED_KEY) + .setCredentialSharedWithUser(true) + .setOemPrivate(true) + .build(); + } + + /** + * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception * when set both {@link WifiNetworkSuggestion.Builder#setIsInitialAutojoinEnabled(boolean)} * and {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} (boolean)} * to false on a passpoint suggestion. |