diff options
306 files changed, 12359 insertions, 5763 deletions
diff --git a/Android.bp b/Android.bp index b3faef1050f6..5fcefa64a92f 100644 --- a/Android.bp +++ b/Android.bp @@ -306,7 +306,6 @@ java_defaults { "rs/java", "sax/java", "telecomm/java", - "wifi/java", ], }, @@ -586,7 +585,7 @@ java_library { genrule { name: "framework-statslog-gen", tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --java $(out)", + cmd: "$(location stats-log-api-gen) --java $(out) --worksource", out: ["android/util/StatsLogInternal.java"], } @@ -1126,9 +1125,10 @@ genrule { } // Avoid including Parcelable classes as we don't want to have two copies of -// Parcelable cross the process. +// Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony) +// and TeleService app (packages/services/Telephony). filegroup { - name: "framework-telephony-stack-shared-srcs", + name: "framework-telephony-common-shared-srcs", srcs: [ "core/java/android/os/BasicShellCommandHandler.java", "core/java/android/os/RegistrantList.java", @@ -1151,6 +1151,21 @@ filegroup { } // Avoid including Parcelable classes as we don't want to have two copies of +// Parcelable cross the process. This is used by framework-telephony (frameworks/base/telephony). +filegroup { + name: "framework-telephony-shared-srcs", + srcs: [ + "core/java/android/util/RecurrenceRule.java", + "core/java/com/android/internal/os/SomeArgs.java", + "core/java/com/android/internal/util/BitwiseInputStream.java", + "core/java/com/android/internal/util/BitwiseOutputStream.java", + "core/java/com/android/internal/util/HexDump.java", + "core/java/com/android/internal/util/IndentingPrintWriter.java", + "core/java/com/android/internal/util/Preconditions.java", + ], +} + +// Avoid including Parcelable classes as we don't want to have two copies of // Parcelable cross the process. filegroup { name: "framework-cellbroadcast-shared-srcs", @@ -1189,6 +1204,7 @@ java_library { "core/java/com/android/internal/util/Protocol.java", "core/java/com/android/internal/util/Preconditions.java", "telephony/java/android/telephony/Annotation.java", + ":net-utils-framework-wifi-common-srcs", ], libs: [ "framework-annotations-lib", @@ -1265,13 +1281,15 @@ java_library { aidl: { export_include_dirs: ["telephony/java"], }, - sdk_version: "system_current", + sdk_version: "core_current", + libs: ["android_system_stubs_current"], } java_library { name: "framework-telephony", srcs: [ ":framework-telephony-sources", + ":framework-telephony-shared-srcs", ], // TODO: change to framework-system-stub to build against system APIs. libs: [ @@ -1290,7 +1308,7 @@ java_library { "frameworks/native/aidl/gui", ] }, - jarjar_rules: ":telephony-framework-jarjar-rules", + jarjar_rules: ":framework-telephony-jarjar-rules", dxflags: [ "--core-library", "--multi-dex", @@ -1309,6 +1327,6 @@ filegroup { } filegroup { - name: "telephony-framework-jarjar-rules", + name: "framework-telephony-jarjar-rules", srcs: ["telephony/framework-telephony-jarjar-rules.txt"], } diff --git a/StubLibraries.bp b/StubLibraries.bp index cdc0d322eedb..6927f4449054 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -181,7 +181,7 @@ droidstubs { // They however are NOT used for building the API stub. droidstubs { name: "module-app-api", - defaults: ["metalava-non-updatable-api-stubs-default"], + defaults: ["metalava-api-stubs-default"], libs: ["framework-all"], arg_files: ["core/res/AndroidManifest.xml"], args: metalava_framework_docs_args + @@ -211,7 +211,7 @@ droidstubs { droidstubs { name: "module-lib-api", - defaults: ["metalava-non-updatable-api-stubs-default"], + defaults: ["metalava-api-stubs-default"], libs: ["framework-all"], arg_files: ["core/res/AndroidManifest.xml"], args: metalava_framework_docs_args + @@ -245,7 +245,7 @@ droidstubs { // the ones with 'client=MODULE_LIBRARIES'. droidstubs { name: "module-app-api-stubs-docs", - defaults: ["metalava-non-updatable-api-stubs-default"], + defaults: ["metalava-api-stubs-default"], libs: ["framework-all"], arg_files: ["core/res/AndroidManifest.xml"], args: metalava_framework_docs_args + @@ -259,7 +259,7 @@ droidstubs { droidstubs { name: "module-lib-api-stubs-docs", - defaults: ["metalava-non-updatable-api-stubs-default"], + defaults: ["metalava-api-stubs-default"], libs: ["framework-all"], arg_files: ["core/res/AndroidManifest.xml"], args: metalava_framework_docs_args + diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp index 2e8811506e0b..24309d7c4e0d 100644 --- a/apex/appsearch/framework/Android.bp +++ b/apex/appsearch/framework/Android.bp @@ -30,14 +30,13 @@ java_library { libs: [ "framework-minus-apex", // TODO(b/146218515) should be framework-system-stubs ], - static_libs: [ - "icing-java-proto-lite", - ], + static_libs: ["icing-java-proto-lite"], visibility: [ - "//frameworks/base/apex/appsearch:__subpackages__", // TODO(b/146218515) remove this when framework is built with the stub of appsearch "//frameworks/base", + "//frameworks/base/apex/appsearch:__subpackages__", ], + permitted_packages: ["android.app.appsearch"], apex_available: ["com.android.appsearch"], } diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearch.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearch.java index e779b69750c2..5b412499ee15 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearch.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearch.java @@ -16,7 +16,7 @@ package android.app.appsearch; -import android.annotation.CurrentTimeSecondsLong; +import android.annotation.CurrentTimeMillisLong; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -32,7 +32,6 @@ import com.google.android.icing.proto.PropertyProto; import java.util.ArrayList; import java.util.Collections; import java.util.Objects; -import java.util.concurrent.TimeUnit; /** * Collection of all AppSearch Document Types. @@ -150,14 +149,14 @@ public final class AppSearch { } /** - * Get the creation timestamp in seconds of the {@link Document}. + * Get the creation timestamp in milliseconds of the {@link Document}. Value will be in the + * {@link System#currentTimeMillis()} time base. * * @hide */ - // TODO(b/143789408) Change seconds to millis with Icing library. - @CurrentTimeSecondsLong - public long getCreationTimestampSecs() { - return mProto.getCreationTimestampSecs(); + @CurrentTimeMillisLong + public long getCreationTimestampMillis() { + return mProto.getCreationTimestampMs(); } /** @@ -381,8 +380,7 @@ public final class AppSearch { mBuilderTypeInstance = (BuilderType) this; mProtoBuilder.setUri(uri).setSchema(schemaType); // Set current timestamp for creation timestamp by default. - setCreationTimestampSecs( - TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())); + setCreationTimestampMillis(System.currentTimeMillis()); } /** @@ -404,14 +402,15 @@ public final class AppSearch { } /** - * Set the creation timestamp in seconds of the {@link Document}. + * Set the creation timestamp in milliseconds of the {@link Document}. Should be set + * using a value obtained from the {@link System#currentTimeMillis()} time base. * * @hide */ @NonNull - public BuilderType setCreationTimestampSecs( - @CurrentTimeSecondsLong long creationTimestampSecs) { - mProtoBuilder.setCreationTimestampSecs(creationTimestampSecs); + public BuilderType setCreationTimestampMillis( + @CurrentTimeMillisLong long creationTimestampMillis) { + mProtoBuilder.setCreationTimestampMs(creationTimestampMillis); return mBuilderTypeInstance; } diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index 83195dc73db6..2ef4893d5918 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -25,9 +25,13 @@ import android.os.RemoteException; import com.android.internal.infra.AndroidFuture; import com.google.android.icing.proto.SchemaProto; +import com.google.android.icing.proto.SearchResultProto; +import com.google.android.icing.proto.StatusProto; +import com.google.android.icing.protobuf.InvalidProtocolBufferException; import java.util.List; import java.util.concurrent.Executor; +import java.util.function.BiConsumer; import java.util.function.Consumer; /** @@ -50,6 +54,47 @@ public class AppSearchManager { /** * Sets the schema being used by documents provided to the #put method. * + * <p>The schema provided here is compared to the stored copy of the schema previously supplied + * to {@link #setSchema}, if any, to determine how to treat existing documents. The following + * types of schema modifications are always safe and are made without deleting any existing + * documents: + * <ul> + * <li>Addition of new types + * <li>Addition of new + * {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL + * OPTIONAL} or + * {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED + * REPEATED} properties to a type + * <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an + * {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL + * OPTIONAL} property into a + * {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED + * REPEATED} property. + * </ul> + * + * <p>The following types of schema changes are not backwards-compatible. Supplying a schema + * with such changes will result in the provided callback being called with a {@link Throwable} + * describing the incompatibility, and the previously set schema will remain active: + * <ul> + * <li>Removal of an existing type + * <li>Removal of a property from a type + * <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property + * <li>For properties of {@code Document} type, changing the schema type of + * {@code Document Documents} of that property + * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an + * {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL + * OPTIONAL} property into a + * {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED + * REQUIRED} property). + * <li>Adding a + * {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED + * REQUIRED} property. + * </ul> + * + * <p>If you need to make non-backwards-compatible changes as described above, you may set the + * {@code force} parameter to {@code true}. In this case, all documents which are not compatible + * with the new schema will be deleted. + * * <p>This operation is performed asynchronously. On success, the provided callback will be * called with {@code null}. On failure, the provided callback will be called with a * {@link Throwable} describing the failure. @@ -57,21 +102,10 @@ public class AppSearchManager { * <p>It is a no-op to set the same schema as has been previously set; this is handled * efficiently. * - * <p>AppSearch automatically handles the following types of schema changes: - * <ul> - * <li>Addition of new types (No changes to storage or index) - * <li>Removal of an existing type (All documents of the removed type are deleted) - * <li>Addition of new 'optional' property to a type (No changes to storage or index) - * <li>Removal of existing property of any cardinality (All documents reindexed) - * </ul> - * - * <p>This method will return an error when attempting to make the following types of changes: - * <ul> - * <li>Changing the type of an existing property - * <li>Adding a 'required' property - * </ul> - * - * @param schema The schema config for this app. + * @param schemas The schema configs for the types used by the calling app. + * @param force Whether to force the new schema to be applied even if there are incompatible + * changes versus the previously set schema. Documents which are incompatible with the new + * schema will be deleted. * @param executor Executor on which to invoke the callback. * @param callback Callback to receive errors resulting from setting the schema. If the * operation succeeds, the callback will be invoked with {@code null}. @@ -79,19 +113,24 @@ public class AppSearchManager { * @hide */ // TODO(b/143789408): linkify #put after that API is created - // TODO(b/145635424): add a 'force' param to setSchema after the corresponding API is finalized - // in Icing Library - // TODO(b/145635424): Update the documentation above once the Schema mutation APIs of Icing - // Library are finalized public void setSchema( - @NonNull AppSearchSchema schema, + List<AppSearchSchema> schemas, + boolean force, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<? super Throwable> callback) { - SchemaProto schemaProto = schema.getProto(); - byte[] schemaBytes = schemaProto.toByteArray(); + // Prepare the merged schema for transmission. + SchemaProto.Builder schemaProtoBuilder = SchemaProto.newBuilder(); + for (AppSearchSchema schema : schemas) { + schemaProtoBuilder.addTypes(schema.getProto()); + } + + // Serialize and send the schema. + // TODO: This should use com.android.internal.infra.RemoteStream or another mechanism to + // avoid binder limits. + byte[] schemaBytes = schemaProtoBuilder.build().toByteArray(); AndroidFuture<Void> future = new AndroidFuture<>(); try { - mService.setSchema(schemaBytes, future); + mService.setSchema(schemaBytes, force, future); } catch (RemoteException e) { future.completeExceptionally(e); } @@ -127,4 +166,94 @@ public class AppSearchManager { // TODO(b/147614371) Fix error report for multiple documents. future.whenCompleteAsync((noop, err) -> callback.accept(err), executor); } + + /** + * This method searches for documents based on a given query string. It also accepts + * specifications regarding how to search and format the results. + * + *<p>Currently we support following features in the raw query format: + * <ul> + * <li>AND + * AND joins (e.g. “match documents that have both the terms ‘dog’ and + * ‘cat’”). + * Example: hello world matches documents that have both ‘hello’ and ‘world’ + * <li>OR + * OR joins (e.g. “match documents that have either the term ‘dog’ or + * ‘cat’”). + * Example: dog OR puppy + * <li>Exclusion + * Exclude a term (e.g. “match documents that do + * not have the term ‘dog’”). + * Example: -dog excludes the term ‘dog’ + * <li>Grouping terms + * Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g. + * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”). + * Example: (dog puppy) (cat kitten) two one group containing two terms. + * <li>Property restricts + * which properties of a document to specifically match terms in (e.g. + * “match documents where the ‘subject’ property contains ‘important’”). + * Example: subject:important matches documents with the term ‘important’ in the + * ‘subject’ property + * <li>Schema type restricts + * This is similar to property restricts, but allows for restricts on top-level document + * fields, such as schema_type. Clients should be able to limit their query to documents of + * a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”). + * Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents + * that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the + * ‘Video’ schema type. + * </ul> + * + * <p> It is strongly recommended to use Jetpack APIs. + * + * @param queryExpression Query String to search. + * @param searchSpec Spec for setting filters, raw query etc. + * @param executor Executor on which to invoke the callback. + * @param callback Callback to receive errors resulting from the query operation. If the + * operation succeeds, the callback will be invoked with {@code null}. + * @hide + */ + @NonNull + public void query( + @NonNull String queryExpression, + @NonNull SearchSpec searchSpec, + @NonNull @CallbackExecutor Executor executor, + @NonNull BiConsumer<? super SearchResults, ? super Throwable> callback) { + AndroidFuture<byte[]> future = new AndroidFuture<>(); + future.whenCompleteAsync((searchResultBytes, err) -> { + if (err != null) { + callback.accept(null, err); + return; + } + + if (searchResultBytes != null) { + SearchResultProto searchResultProto; + try { + searchResultProto = SearchResultProto.parseFrom(searchResultBytes); + } catch (InvalidProtocolBufferException e) { + callback.accept(null, e); + return; + } + if (searchResultProto.getStatus().getCode() != StatusProto.Code.OK) { + // TODO(sidchhabra): Add better exception handling. + callback.accept( + null, + new RuntimeException(searchResultProto.getStatus().getMessage())); + return; + } + SearchResults searchResults = new SearchResults(searchResultProto); + callback.accept(searchResults, null); + return; + } + + // Nothing was supplied in the future at all + callback.accept( + null, new IllegalStateException("Unknown failure occurred while querying")); + }, executor); + + try { + mService.query(queryExpression, searchSpec.getProto().toByteArray(), future); + } catch (RemoteException e) { + future.completeExceptionally(e); + } + } } diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java index 7e5f187b88c9..1d54dc4971a6 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java @@ -18,41 +18,38 @@ package android.app.appsearch; import android.annotation.IntDef; import android.annotation.NonNull; +import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.google.android.icing.proto.PropertyConfigProto; -import com.google.android.icing.proto.SchemaProto; import com.google.android.icing.proto.SchemaTypeConfigProto; +import com.google.android.icing.proto.TermMatchType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Set; /** - * Representation of the AppSearch Schema. + * The AppSearch Schema for a particular type of document. * - * <p>The schema is the set of document types, properties, and config (like tokenization type) - * understood by AppSearch for this app. + * <p>For example, an e-mail message or a music recording could be a schema type. + * + * <p>The schema consists of type information, properties, and config (like tokenization type). * * @hide */ public final class AppSearchSchema { - private final SchemaProto mProto; + private final SchemaTypeConfigProto mProto; - private AppSearchSchema(SchemaProto proto) { + private AppSearchSchema(SchemaTypeConfigProto proto) { mProto = proto; } /** Creates a new {@link AppSearchSchema.Builder}. */ @NonNull - public static AppSearchSchema.Builder newBuilder() { - return new AppSearchSchema.Builder(); - } - - /** Creates a new {@link SchemaType.Builder}. */ - @NonNull - public static SchemaType.Builder newSchemaTypeBuilder(@NonNull String typeName) { - return new SchemaType.Builder(typeName); + public static AppSearchSchema.Builder newBuilder(@NonNull String typeName) { + return new AppSearchSchema.Builder(typeName); } /** Creates a new {@link PropertyConfig.Builder}. */ @@ -61,32 +58,34 @@ public final class AppSearchSchema { return new PropertyConfig.Builder(propertyName); } - /** Creates a new {@link IndexingConfig.Builder}. */ - @NonNull - public static IndexingConfig.Builder newIndexingConfigBuilder() { - return new IndexingConfig.Builder(); - } - /** - * Returns the schema proto populated by the {@link AppSearchSchema} builders. + * Returns the {@link SchemaTypeConfigProto} populated by this builder. * @hide */ @NonNull @VisibleForTesting - public SchemaProto getProto() { + public SchemaTypeConfigProto getProto() { return mProto; } + @Override + public String toString() { + return mProto.toString(); + } + /** Builder for {@link AppSearchSchema objects}. */ public static final class Builder { - private final SchemaProto.Builder mProtoBuilder = SchemaProto.newBuilder(); + private final SchemaTypeConfigProto.Builder mProtoBuilder = + SchemaTypeConfigProto.newBuilder(); - private Builder() {} + private Builder(@NonNull String typeName) { + mProtoBuilder.setSchemaType(typeName); + } - /** Adds a supported type to this app's AppSearch schema. */ + /** Adds a property to the given type. */ @NonNull - public AppSearchSchema.Builder addType(@NonNull SchemaType schemaType) { - mProtoBuilder.addTypes(schemaType.mProto); + public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) { + mProtoBuilder.addProperties(propertyConfig.mProto); return this; } @@ -97,47 +96,15 @@ public final class AppSearchSchema { */ @NonNull public AppSearchSchema build() { - return new AppSearchSchema(mProtoBuilder.build()); - } - } - - /** - * Represents a type of a document. - * - * <p>For example, an e-mail message or a music recording could be a schema type. - */ - public static final class SchemaType { - private final SchemaTypeConfigProto mProto; - - private SchemaType(SchemaTypeConfigProto proto) { - mProto = proto; - } - - /** Builder for {@link SchemaType} objects. */ - public static final class Builder { - private final SchemaTypeConfigProto.Builder mProtoBuilder = - SchemaTypeConfigProto.newBuilder(); - - private Builder(@NonNull String typeName) { - mProtoBuilder.setSchemaType(typeName); - } - - /** Adds a property to the given type. */ - @NonNull - public SchemaType.Builder addProperty(@NonNull PropertyConfig propertyConfig) { - mProtoBuilder.addProperties(propertyConfig.mProto); - return this; - } - - /** - * Constructs a new {@link SchemaType} from the contents of this builder. - * - * <p>After calling this method, the builder must no longer be used. - */ - @NonNull - public SchemaType build() { - return new SchemaType(mProtoBuilder.build()); + Set<String> propertyNames = new ArraySet<>(); + for (PropertyConfigProto propertyConfigProto : mProtoBuilder.getPropertiesList()) { + if (!propertyNames.add(propertyConfigProto.getPropertyName())) { + throw new IllegalSchemaException( + "Property defined more than once: " + + propertyConfigProto.getPropertyName()); + } } + return new AppSearchSchema(mProtoBuilder.build()); } } @@ -197,12 +164,71 @@ public final class AppSearchSchema { /** Exactly one value [1]. */ public static final int CARDINALITY_REQUIRED = 3; + /** Encapsulates the configurations on how AppSearch should query/index these terms. */ + @IntDef(prefix = {"INDEXING_TYPE_"}, value = { + INDEXING_TYPE_NONE, + INDEXING_TYPE_EXACT_TERMS, + INDEXING_TYPE_PREFIXES, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface IndexingType {} + + /** + * Content in this property will not be tokenized or indexed. + * + * <p>Useful if the data type is not made up of terms (e.g. + * {@link PropertyConfig#DATA_TYPE_DOCUMENT} or {@link PropertyConfig#DATA_TYPE_BYTES} + * type). All the properties inside the nested property won't be indexed regardless of the + * value of {@code indexingType} for the nested properties. + */ + public static final int INDEXING_TYPE_NONE = 0; + + /** + * Content in this property should only be returned for queries matching the exact tokens + * appearing in this property. + * + * <p>Ex. A property with "fool" should NOT match a query for "foo". + */ + public static final int INDEXING_TYPE_EXACT_TERMS = 1; + + /** + * Content in this property should be returned for queries that are either exact matches or + * query matches of the tokens appearing in this property. + * + * <p>Ex. A property with "fool" <b>should</b> match a query for "foo". + */ + public static final int INDEXING_TYPE_PREFIXES = 2; + + /** Configures how tokens should be extracted from this property. */ + // NOTE: The integer values of these constants must match the proto enum constants in + // com.google.android.icing.proto.IndexingConfig.TokenizerType.Code. + @IntDef(prefix = {"TOKENIZER_TYPE_"}, value = { + TOKENIZER_TYPE_NONE, + TOKENIZER_TYPE_PLAIN, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TokenizerType {} + + /** + * It is only valid for tokenizer_type to be 'NONE' if the data type is + * {@link PropertyConfig#DATA_TYPE_DOCUMENT}. + */ + public static final int TOKENIZER_TYPE_NONE = 0; + + /** Tokenization for plain text. */ + public static final int TOKENIZER_TYPE_PLAIN = 1; + private final PropertyConfigProto mProto; private PropertyConfig(PropertyConfigProto proto) { mProto = proto; } + @Override + public String toString() { + return mProto.toString(); + } + /** * Builder for {@link PropertyConfig}. * @@ -217,11 +243,14 @@ public final class AppSearchSchema { * is also required. */ public static final class Builder { - private final PropertyConfigProto.Builder mProtoBuilder = + private final PropertyConfigProto.Builder mPropertyConfigProto = PropertyConfigProto.newBuilder(); + private final com.google.android.icing.proto.IndexingConfig.Builder + mIndexingConfigProto = + com.google.android.icing.proto.IndexingConfig.newBuilder(); private Builder(String propertyName) { - mProtoBuilder.setPropertyName(propertyName); + mPropertyConfigProto.setPropertyName(propertyName); } /** @@ -236,7 +265,7 @@ public final class AppSearchSchema { if (dataTypeProto == null) { throw new IllegalArgumentException("Invalid dataType: " + dataType); } - mProtoBuilder.setDataType(dataTypeProto); + mPropertyConfigProto.setDataType(dataTypeProto); return this; } @@ -248,7 +277,7 @@ public final class AppSearchSchema { */ @NonNull public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) { - mProtoBuilder.setSchemaType(schemaType); + mPropertyConfigProto.setSchemaType(schemaType); return this; } @@ -264,19 +293,44 @@ public final class AppSearchSchema { if (cardinalityProto == null) { throw new IllegalArgumentException("Invalid cardinality: " + cardinality); } - mProtoBuilder.setCardinality(cardinalityProto); + mPropertyConfigProto.setCardinality(cardinalityProto); return this; } /** - * Configures how this property should be indexed. - * - * <p>If this is not supplied, the property will not be indexed at all. + * Configures how a property should be indexed so that it can be retrieved by queries. */ @NonNull - public PropertyConfig.Builder setIndexingConfig( - @NonNull IndexingConfig indexingConfig) { - mProtoBuilder.setIndexingConfig(indexingConfig.mProto); + public PropertyConfig.Builder setIndexingType(@IndexingType int indexingType) { + TermMatchType.Code termMatchTypeProto; + switch (indexingType) { + case INDEXING_TYPE_NONE: + termMatchTypeProto = TermMatchType.Code.UNKNOWN; + break; + case INDEXING_TYPE_EXACT_TERMS: + termMatchTypeProto = TermMatchType.Code.EXACT_ONLY; + break; + case INDEXING_TYPE_PREFIXES: + termMatchTypeProto = TermMatchType.Code.PREFIX; + break; + default: + throw new IllegalArgumentException("Invalid indexingType: " + indexingType); + } + mIndexingConfigProto.setTermMatchType(termMatchTypeProto); + return this; + } + + /** Configures how this property should be tokenized (split into words). */ + @NonNull + public PropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) { + com.google.android.icing.proto.IndexingConfig.TokenizerType.Code + tokenizerTypeProto = + com.google.android.icing.proto.IndexingConfig + .TokenizerType.Code.forNumber(tokenizerType); + if (tokenizerTypeProto == null) { + throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType); + } + mIndexingConfigProto.setTokenizerType(tokenizerTypeProto); return this; } @@ -290,136 +344,25 @@ public final class AppSearchSchema { */ @NonNull public PropertyConfig build() { - if (mProtoBuilder.getDataType() == PropertyConfigProto.DataType.Code.UNKNOWN) { + mPropertyConfigProto.setIndexingConfig(mIndexingConfigProto); + // TODO(b/147692920): Send the schema to Icing Lib for official validation, instead + // of partially reimplementing some of the validation Icing does here. + if (mPropertyConfigProto.getDataType() + == PropertyConfigProto.DataType.Code.UNKNOWN) { throw new IllegalSchemaException("Missing field: dataType"); } - if (mProtoBuilder.getSchemaType().isEmpty() - && mProtoBuilder.getDataType() - == PropertyConfigProto.DataType.Code.DOCUMENT) { + if (mPropertyConfigProto.getSchemaType().isEmpty() + && mPropertyConfigProto.getDataType() + == PropertyConfigProto.DataType.Code.DOCUMENT) { throw new IllegalSchemaException( "Missing field: schemaType (required for configs with " + "dataType = DOCUMENT)"); } - if (mProtoBuilder.getCardinality() + if (mPropertyConfigProto.getCardinality() == PropertyConfigProto.Cardinality.Code.UNKNOWN) { throw new IllegalSchemaException("Missing field: cardinality"); } - return new PropertyConfig(mProtoBuilder.build()); - } - } - } - - /** Configures how a property should be indexed so that it can be retrieved by queries. */ - public static final class IndexingConfig { - /** Encapsulates the configurations on how AppSearch should query/index these terms. */ - // NOTE: The integer values of these constants must match the proto enum constants in - // com.google.android.icing.proto.TermMatchType.Code. - @IntDef(prefix = {"TERM_MATCH_TYPE_"}, value = { - TERM_MATCH_TYPE_UNKNOWN, - TERM_MATCH_TYPE_EXACT_ONLY, - TERM_MATCH_TYPE_PREFIX, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TermMatchType {} - - /** - * Content in this property will not be tokenized or indexed. - * - * <p>Useful if the data type is not made up of terms (e.g. - * {@link PropertyConfig#DATA_TYPE_DOCUMENT} or {@link PropertyConfig#DATA_TYPE_BYTES} - * type). All the properties inside the nested property won't be indexed regardless of the - * value of {@code termMatchType} for the nested properties. - */ - public static final int TERM_MATCH_TYPE_UNKNOWN = 0; - - /** - * Content in this property should only be returned for queries matching the exact tokens - * appearing in this property. - * - * <p>Ex. A property with "fool" should NOT match a query for "foo". - */ - public static final int TERM_MATCH_TYPE_EXACT_ONLY = 1; - - /** - * Content in this property should be returned for queries that are either exact matches or - * query matches of the tokens appearing in this property. - * - * <p>Ex. A property with "fool" <b>should</b> match a query for "foo". - */ - public static final int TERM_MATCH_TYPE_PREFIX = 2; - - /** Configures how tokens should be extracted from this property. */ - // NOTE: The integer values of these constants must match the proto enum constants in - // com.google.android.icing.proto.IndexingConfig.TokenizerType.Code. - @IntDef(prefix = {"TOKENIZER_TYPE_"}, value = { - TOKENIZER_TYPE_NONE, - TOKENIZER_TYPE_PLAIN, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TokenizerType {} - - /** - * It is only valid for tokenizer_type to be 'NONE' if the data type is - * {@link PropertyConfig#DATA_TYPE_DOCUMENT}. - */ - public static final int TOKENIZER_TYPE_NONE = 0; - - /** Tokenization for plain text. */ - public static final int TOKENIZER_TYPE_PLAIN = 1; - - private final com.google.android.icing.proto.IndexingConfig mProto; - - private IndexingConfig(com.google.android.icing.proto.IndexingConfig proto) { - mProto = proto; - } - - /** - * Builder for {@link IndexingConfig} objects. - * - * <p>You may skip adding an {@link IndexingConfig} for a property, which is equivalent to - * an {@link IndexingConfig} having {@code termMatchType} equal to - * {@link #TERM_MATCH_TYPE_UNKNOWN}. In this case the property will not be indexed. - */ - public static final class Builder { - private final com.google.android.icing.proto.IndexingConfig.Builder mProtoBuilder = - com.google.android.icing.proto.IndexingConfig.newBuilder(); - - private Builder() {} - - /** Configures how the content of this property should be matched in the index. */ - @NonNull - public IndexingConfig.Builder setTermMatchType(@TermMatchType int termMatchType) { - com.google.android.icing.proto.TermMatchType.Code termMatchTypeProto = - com.google.android.icing.proto.TermMatchType.Code.forNumber(termMatchType); - if (termMatchTypeProto == null) { - throw new IllegalArgumentException("Invalid termMatchType: " + termMatchType); - } - mProtoBuilder.setTermMatchType(termMatchTypeProto); - return this; - } - - /** Configures how this property should be tokenized (split into words). */ - @NonNull - public IndexingConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) { - com.google.android.icing.proto.IndexingConfig.TokenizerType.Code - tokenizerTypeProto = - com.google.android.icing.proto.IndexingConfig - .TokenizerType.Code.forNumber(tokenizerType); - if (tokenizerTypeProto == null) { - throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType); - } - mProtoBuilder.setTokenizerType(tokenizerTypeProto); - return this; - } - - /** - * Constructs a new {@link IndexingConfig} from the contents of this builder. - * - * <p>After calling this method, the builder must no longer be used. - */ - @NonNull - public IndexingConfig build() { - return new IndexingConfig(mProtoBuilder.build()); + return new PropertyConfig(mPropertyConfigProto.build()); } } } diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl index fc83d8ccbd4a..6db65a4299a4 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl @@ -1,5 +1,5 @@ /** - * Copyright 2019, 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. @@ -22,11 +22,22 @@ interface IAppSearchManager { /** * Sets the schema. * - * @param schemaProto serialized SchemaProto + * @param schemaProto Serialized SchemaProto. + * @param force Whether to apply the new schema even if it is incompatible. All incompatible + documents will be deleted. * @param callback {@link AndroidFuture}<{@link Void}>. Will be completed with * {@code null} upon successful completion of the setSchema call, or completed exceptionally * if setSchema fails. */ - void setSchema(in byte[] schemaProto, in AndroidFuture callback); + void setSchema(in byte[] schemaProto, boolean force, in AndroidFuture callback); void put(in byte[] documentBytes, in AndroidFuture callback); + /** + * Searches a document based on a given query string. + * + * @param queryExpression Query String to search. + * @param searchSpec Serialized SearchSpecProto. + * @param callback {@link AndroidFuture}. Will be completed with a serialized + * {@link SearchResultsProto}, or completed exceptionally if query fails. + */ + void query(in String queryExpression, in byte[] searchSpecBytes, in AndroidFuture callback); } diff --git a/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java b/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java new file mode 100644 index 000000000000..0d029f029ee5 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java @@ -0,0 +1,36 @@ +/* + * 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.app.appsearch; + +import android.annotation.NonNull; + +/** + * Indicates that a {@link android.app.appsearch.SearchResults} has logical inconsistencies such + * as unpopulated mandatory fields or illegal combinations of parameters. + * + * @hide + */ +public class IllegalSearchSpecException extends IllegalArgumentException { + /** + * Constructs a new {@link IllegalSearchSpecException}. + * + * @param message A developer-readable description of the issue with the bundle. + */ + public IllegalSearchSpecException(@NonNull String message) { + super(message); + } +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java new file mode 100644 index 000000000000..d763103f1217 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java @@ -0,0 +1,89 @@ +/* + * 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.app.appsearch; + +import android.annotation.NonNull; + +import com.google.android.icing.proto.SearchResultProto; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * SearchResults are a list of results that are returned from a query. Each result from this + * list contains a document and may contain other fields like snippets based on request. + * @hide + */ +public final class SearchResults { + + private final SearchResultProto mSearchResultProto; + + /** @hide */ + public SearchResults(SearchResultProto searchResultProto) { + mSearchResultProto = searchResultProto; + } + + /** + * This class represents the result obtained from the query. It will contain the document which + * which matched the specified query string and specifications. + * @hide + */ + public static final class Result { + private final SearchResultProto.ResultProto mResultProto; + + private Result(SearchResultProto.ResultProto resultProto) { + mResultProto = resultProto; + } + + /** + * Contains the matching {@link AppSearch.Document}. + * @return Document object which matched the query. + * @hide + */ + // TODO(sidchhabra): Switch to Document constructor that takes proto. + @NonNull + public AppSearch.Document getDocument() { + return AppSearch.Document.newBuilder(mResultProto.getDocument().getUri(), + mResultProto.getDocument().getSchema()) + .setCreationTimestampMillis(mResultProto.getDocument().getCreationTimestampMs()) + .setScore(mResultProto.getDocument().getScore()) + .build(); + } + + // TODO(sidchhabra): Add Getter for ResultReader for Snippet. + } + + @Override + public String toString() { + return mSearchResultProto.toString(); + } + + /** + * Returns a {@link Result} iterator. Returns Empty Iterator if there are no matching results. + * @hide + */ + @NonNull + public Iterator<Result> getResults() { + List<Result> results = new ArrayList<>(); + // TODO(sidchhabra): Pass results using a RemoteStream. + for (SearchResultProto.ResultProto resultProto : mSearchResultProto.getResultsList()) { + results.add(new Result(resultProto)); + } + return results.iterator(); + } +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java new file mode 100644 index 000000000000..5df7108fec09 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java @@ -0,0 +1,127 @@ +/* + * 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.app.appsearch; + +import android.annotation.IntDef; +import android.annotation.NonNull; + +import com.google.android.icing.proto.SearchSpecProto; +import com.google.android.icing.proto.TermMatchType; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This class represents the specification logic for AppSearch. It can be used to set the type of + * search, like prefix or exact only or apply filters to search for a specific schema type only etc. + * @hide + * + */ +// TODO(sidchhabra) : AddResultSpec fields for Snippets etc. +public final class SearchSpec { + + private final SearchSpecProto mSearchSpecProto; + + private SearchSpec(SearchSpecProto searchSpecProto) { + mSearchSpecProto = searchSpecProto; + } + + /** Creates a new {@link SearchSpec.Builder}. */ + @NonNull + public static SearchSpec.Builder newBuilder() { + return new SearchSpec.Builder(); + } + + /** @hide */ + @NonNull + SearchSpecProto getProto() { + return mSearchSpecProto; + } + + /** Term Match Type for the query. */ + // NOTE: The integer values of these constants must match the proto enum constants in + // {@link com.google.android.icing.proto.SearchSpecProto.termMatchType} + @IntDef(prefix = {"TERM_MATCH_TYPE_"}, value = { + TERM_MATCH_TYPE_EXACT_ONLY, + TERM_MATCH_TYPE_PREFIX + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TermMatchTypeCode {} + + public static final int TERM_MATCH_TYPE_EXACT_ONLY = 1; + public static final int TERM_MATCH_TYPE_PREFIX = 2; + + /** Builder for {@link SearchSpec objects}. */ + public static final class Builder { + + private final SearchSpecProto.Builder mBuilder = SearchSpecProto.newBuilder(); + + private Builder(){} + + /** + * Indicates how the query terms should match {@link TermMatchTypeCode} in the index. + * + * TermMatchType.Code=EXACT_ONLY + * Query terms will only match exact tokens in the index. + * Ex. A query term "foo" will only match indexed token "foo", and not "foot" + * or "football" + * + * TermMatchType.Code=PREFIX + * Query terms will match indexed tokens when the query term is a prefix of + * the token. + * Ex. A query term "foo" will match indexed tokens like "foo", "foot", and + * "football". + */ + @NonNull + public Builder setTermMatchType(@TermMatchTypeCode int termMatchTypeCode) { + TermMatchType.Code termMatchTypeCodeProto = + TermMatchType.Code.forNumber(termMatchTypeCode); + if (termMatchTypeCodeProto == null) { + throw new IllegalArgumentException("Invalid term match type: " + termMatchTypeCode); + } + mBuilder.setTermMatchType(termMatchTypeCodeProto); + return this; + } + + /** + * Adds a Schema type filter to {@link SearchSpec} Entry. + * Only search for documents that have the specified schema types. + * If unset, the query will search over all schema types. + */ + @NonNull + public Builder setSchemaTypes(@NonNull String... schemaTypes) { + for (String schemaType : schemaTypes) { + mBuilder.addSchemaTypeFilters(schemaType); + } + return this; + } + + /** + * Constructs a new {@link SearchSpec} from the contents of this builder. + * + * <p>After calling this method, the builder must no longer be used. + */ + @NonNull + public SearchSpec build() { + if (mBuilder.getTermMatchType() == TermMatchType.Code.UNKNOWN) { + throw new IllegalSearchSpecException("Missing termMatchType field."); + } + return new SearchSpec(mBuilder.build()); + } + } + +} 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 ce7e04c8ce04..f8e010ddb86f 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -15,6 +15,7 @@ */ package com.android.server.appsearch; +import android.annotation.NonNull; import android.app.appsearch.IAppSearchManager; import android.content.Context; import android.os.Binder; @@ -24,9 +25,13 @@ import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import com.android.server.SystemService; import com.android.server.appsearch.impl.AppSearchImpl; +import com.android.server.appsearch.impl.FakeIcing; import com.android.server.appsearch.impl.ImplInstanceManager; import com.google.android.icing.proto.SchemaProto; +import com.google.android.icing.proto.SearchResultProto; +import com.google.android.icing.proto.SearchSpecProto; +import com.google.android.icing.protobuf.InvalidProtocolBufferException; /** * TODO(b/142567528): add comments when implement this class @@ -35,8 +40,11 @@ public class AppSearchManagerService extends SystemService { public AppSearchManagerService(Context context) { super(context); + mFakeIcing = new FakeIcing(); } + private final FakeIcing mFakeIcing; + @Override public void onStart() { publishBinderService(Context.APP_SEARCH_SERVICE, new Stub()); @@ -44,7 +52,7 @@ public class AppSearchManagerService extends SystemService { private class Stub extends IAppSearchManager.Stub { @Override - public void setSchema(byte[] schemaBytes, AndroidFuture callback) { + public void setSchema(byte[] schemaBytes, boolean force, AndroidFuture callback) { Preconditions.checkNotNull(schemaBytes); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); @@ -53,7 +61,7 @@ public class AppSearchManagerService extends SystemService { try { SchemaProto schema = SchemaProto.parseFrom(schemaBytes); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - impl.setSchema(callingUid, schema); + impl.setSchema(callingUid, schema, force); callback.complete(null); } catch (Throwable t) { callback.completeExceptionally(t); @@ -70,5 +78,22 @@ public class AppSearchManagerService extends SystemService { callback.completeExceptionally(t); } } + // TODO(sidchhabra):Init FakeIcing properly. + // TODO(sidchhabra): Do this in a threadpool. + @Override + public void query(@NonNull String queryExpression, @NonNull byte[] searchSpec, + AndroidFuture callback) { + Preconditions.checkNotNull(queryExpression); + Preconditions.checkNotNull(searchSpec); + SearchSpecProto searchSpecProto = null; + try { + searchSpecProto = SearchSpecProto.parseFrom(searchSpec); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + SearchResultProto searchResults = + mFakeIcing.query(queryExpression); + callback.complete(searchResults.toByteArray()); + } } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java index 7c97b0b8cf30..e69fc8a2af63 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java @@ -45,8 +45,10 @@ public final class AppSearchImpl { * * @param callingUid The uid of the app calling AppSearch. * @param origSchema The schema to set for this app. + * @param force Whether to force-apply the schema even if it is incompatible. Documents which do + * not comply with the new schema will be deleted. */ - public void setSchema(int callingUid, @NonNull SchemaProto origSchema) { + public void setSchema(int callingUid, @NonNull SchemaProto origSchema, boolean force) { // Rewrite schema type names to include the calling app's package and uid. String typePrefix = getTypePrefix(callingUid); SchemaProto.Builder schemaBuilder = origSchema.toBuilder(); diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.aidl b/apex/blobstore/framework/java/android/app/blob/BlobHandle.aidl new file mode 100644 index 000000000000..02d0740a2ce0 --- /dev/null +++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.aidl @@ -0,0 +1,19 @@ +/* + * 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.blob; + +/** {@hide} */ +parcelable BlobHandle;
\ No newline at end of file diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java new file mode 100644 index 000000000000..6aca4a1be1e0 --- /dev/null +++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java @@ -0,0 +1,187 @@ +/* + * 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.blob; + +import android.annotation.CurrentTimeMillisLong; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +/** + * An identifier to represent a blob. + */ +// TODO: use datagen tool? +public final class BlobHandle implements Parcelable { + private static final String ALGO_SHA_256 = "SHA-256"; + + private static final int LIMIT_BLOB_TAG_LENGTH = 128; // characters + + /** + * Cyrptographically secure hash algorithm used to generate hash of the blob this handle is + * representing. + * + * @hide + */ + @NonNull public final String algorithm; + /** + * Hash of the blob this handle is representing using {@link #algorithm}. + * + * @hide + */ + @NonNull public final byte[] digest; + /** + * Label of the blob that can be surfaced to the user. + * @hide + */ + @NonNull public final CharSequence label; + /** + * Time in milliseconds after which the blob should be invalidated and not + * allowed to be accessed by any other app, in {@link System#currentTimeMillis()} timebase. + * + * @hide + */ + @CurrentTimeMillisLong public final long expiryTimeMillis; + /** + * An opaque {@link String} associated with the blob. + * + * @hide + */ + @NonNull public final String tag; + + private BlobHandle(String algorithm, byte[] digest, CharSequence label, long expiryTimeMillis, + String tag) { + this.algorithm = algorithm; + this.digest = digest; + this.label = label; + this.expiryTimeMillis = expiryTimeMillis; + this.tag = tag; + } + + private BlobHandle(Parcel in) { + this.algorithm = in.readString(); + this.digest = in.createByteArray(); + this.label = in.readCharSequence(); + this.expiryTimeMillis = in.readLong(); + this.tag = in.readString(); + } + + /** @hide */ + public static @NonNull BlobHandle create(@NonNull String algorithm, @NonNull byte[] digest, + @NonNull CharSequence label, @CurrentTimeMillisLong long expiryTimeMillis, + @NonNull String tag) { + Preconditions.checkNotNull(algorithm, "algorithm must not be null"); + Preconditions.checkNotNull(digest, "digest must not be null"); + Preconditions.checkNotNull(label, "label must not be null"); + Preconditions.checkArgumentNonnegative(expiryTimeMillis, + "expiryTimeMillis must not be negative"); + Preconditions.checkNotNull(tag, "tag must not be null"); + Preconditions.checkArgument(tag.length() <= LIMIT_BLOB_TAG_LENGTH, "tag too long"); + return new BlobHandle(algorithm, digest, label, expiryTimeMillis, tag); + } + + /** + * Create a new blob identifier. + * + * <p> For two objects of {@link BlobHandle} to be considered equal, the following arguments + * must be equal: + * <ul> + * <li> {@code digest} + * <li> {@code label} + * <li> {@code expiryTimeMillis} + * <li> {@code tag} + * </ul> + * + * @param digest the SHA-256 hash of the blob this is representing. + * @param label a label indicating what the blob is, that can be surfaced to the user. + * @param expiryTimeMillis the time in secs after which the blob should be invalidated and not + * allowed to be accessed by any other app, + * in {@link System#currentTimeMillis()} timebase. + * @param tag an opaque {@link String} associated with the blob. The length of the tag + * cannot be more than 128 characters. + * + * @return a new instance of {@link BlobHandle} object. + */ + public static @NonNull BlobHandle createWithSha256(@NonNull byte[] digest, + @NonNull CharSequence label, @CurrentTimeMillisLong long expiryTimeMillis, + @NonNull String tag) { + return create(ALGO_SHA_256, digest, label, expiryTimeMillis, tag); + } + + /** + * Returns the SHA-256 hash of the blob that this object is representing. + * + * @see #createWithSha256(byte[], CharSequence, long, String) + */ + public @NonNull byte[] getSha256Digest() { + return digest; + } + + /** + * Returns the label associated with the blob that this object is representing. + * + * @see #createWithSha256(byte[], CharSequence, long, String) + */ + public @NonNull CharSequence getLabel() { + return label; + } + + /** + * Returns the expiry time in milliseconds of the blob that this object is representing, in + * {@link System#currentTimeMillis()} timebase. + * + * @see #createWithSha256(byte[], CharSequence, long, String) + */ + public @CurrentTimeMillisLong long getExpiryTimeMillis() { + return expiryTimeMillis; + } + + /** + * Returns the opaque {@link String} associated with the blob this object is representing. + * + * @see #createWithSha256(byte[], CharSequence, long, String) + */ + public @NonNull String getTag() { + return tag; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(algorithm); + dest.writeByteArray(digest); + dest.writeCharSequence(label); + dest.writeLong(expiryTimeMillis); + dest.writeString(tag); + } + + public static final @NonNull Creator<BlobHandle> CREATOR = new Creator<BlobHandle>() { + @Override + public @NonNull BlobHandle createFromParcel(@NonNull Parcel source) { + return new BlobHandle(source); + } + + @Override + public @NonNull BlobHandle[] newArray(int size) { + return new BlobHandle[size]; + } + }; +} diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java index 1ed188e69881..4395e5a0776f 100644 --- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java +++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java @@ -15,18 +15,33 @@ */ package android.app.blob; +import android.annotation.BytesLong; +import android.annotation.CallbackExecutor; +import android.annotation.CurrentTimeMillisLong; +import android.annotation.IdRes; +import android.annotation.IntRange; +import android.annotation.NonNull; import android.annotation.SystemService; import android.content.Context; +import android.os.ParcelFileDescriptor; +import android.os.ParcelableException; +import android.os.RemoteException; + +import com.android.internal.util.function.pooled.PooledLambda; + +import java.io.Closeable; +import java.io.IOException; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** - * This class provides access to the blob store maintained by the system. + * This class provides access to the blob store managed by the system. * * Apps can publish data blobs which might be useful for other apps on the device to be - * maintained by the system and apps that would like to access these data blobs can do so + * managed by the system and apps that would like to access these data blobs can do so * by addressing them via their cryptographically secure hashes. * - * TODO: make this public once the APIs are added. - * @hide + * TODO: More documentation. */ @SystemService(Context.BLOB_STORE_SERVICE) public class BlobStoreManager { @@ -34,8 +49,421 @@ public class BlobStoreManager { private final IBlobStoreManager mService; /** @hide */ - public BlobStoreManager(Context context, IBlobStoreManager service) { + public BlobStoreManager(@NonNull Context context, @NonNull IBlobStoreManager service) { mContext = context; mService = service; } + + /** + * Create a new session using the given {@link BlobHandle}, returning a unique id + * that represents the session. Once created, the session can be opened + * multiple times across multiple device boots. + * + * <p> The system may automatically destroy sessions that have not been + * finalized (either committed or abandoned) within a reasonable period of + * time, typically about a week. + * + * @param blobHandle the {@link BlobHandle} identifier for which a new session + * needs to be created. + * @return positive, non-zero unique id that represents the created session. + * This id remains consistent across device reboots until the + * session is finalized. IDs are not reused during a given boot. + * + * @throws IOException when there is an I/O error while creating the session. + * @throws SecurityException when the caller is not allowed to create a session, such + * as when called from an Instant app. + * @throws IllegalArgumentException when {@code blobHandle} is invalid. + * @throws IllegalStateException when a new session could not be created, such as when the + * caller is trying to create too many sessions or when the + * device is running low on space. + */ + public @IntRange(from = 1) long createSession(@NonNull BlobHandle blobHandle) + throws IOException { + try { + return mService.createSession(blobHandle, mContext.getOpPackageName()); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Open an existing session to actively perform work. + * + * @param sessionId a unique id obtained via {@link #createSession(BlobHandle)} that + * represents a particular session. + * @return the {@link Session} object corresponding to the {@code sessionId}. + * + * @throws IOException when there is an I/O error while opening the session. + * @throws SecurityException when the caller does not own the session, or + * the session does not exist or is invalid. + */ + public @NonNull Session openSession(@IntRange(from = 1) long sessionId) throws IOException { + try { + return new Session(mService.openSession(sessionId)); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Opens an existing blob for reading from the blob store managed by the system. + * + * @param blobHandle the {@link BlobHandle} representing the blob that the caller + * wants to access. + * @return a {@link ParcelFileDescriptor} that can be used to read the blob content. + * + * @throws IOException when there is an I/O while opening the blob for read. + * @throws IllegalArgumentException when {@code blobHandle} is invalid. + * @throws SecurityException when the blob represented by the {@code blobHandle} does not + * exist or the caller does not have access to it. + */ + public @NonNull ParcelFileDescriptor openBlob(@NonNull BlobHandle blobHandle) + throws IOException { + try { + return mService.openBlob(blobHandle, mContext.getOpPackageName()); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the + * system that the caller wants the blob to be kept around. + * + * <p> Any active leases will be automatically released when the blob's expiry time + * ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed. + * + * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to + * acquire a lease for. + * @param descriptionResId the resource id for a short description string that can be surfaced + * to the user explaining what the blob is used for. + * @param leaseExpiryTimeMillis the time in milliseconds after which the lease can be + * automatically released, in {@link System#currentTimeMillis()} + * timebase. If its value is {@code 0}, then the behavior of this + * API is identical to {@link #acquireLease(BlobHandle, int)} + * where clients have to explicitly call + * {@link #releaseLease(BlobHandle)} when they don't + * need the blob anymore. + * + * @throws IOException when there is an I/O error while acquiring a lease to the blob. + * @throws SecurityException when the blob represented by the {@code blobHandle} does not + * exist or the caller does not have access to it. + * @throws IllegalArgumentException when {@code blobHandle} is invalid or + * if the {@code leaseExpiryTimeMillis} is greater than the + * {@link BlobHandle#getExpiryTimeMillis()}. + * @throws IllegalStateException when a lease could not be acquired, such as when the + * caller is trying to acquire too many leases. + * + * @see {@link #acquireLease(BlobHandle, int)} + */ + public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId, + @CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException { + try { + mService.acquireLease(blobHandle, descriptionResId, leaseExpiryTimeMillis, + mContext.getOpPackageName()); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the + * system that the caller wants the blob to be kept around. + * + * <p> This is similar to {@link #acquireLease(BlobHandle, int, long)} except clients don't + * have to specify the lease expiry time upfront using this API and need to explicitly + * release the lease using {@link #releaseLease(BlobHandle)} when they no longer like to keep + * a blob around. + * + * <p> Any active leases will be automatically released when the blob's expiry time + * ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed. + * + * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to + * acquire a lease for. + * @param descriptionResId the resource id for a short description string that can be surfaced + * to the user explaining what the blob is used for. + * + * @throws IOException when there is an I/O error while acquiring a lease to the blob. + * @throws SecurityException when the blob represented by the {@code blobHandle} does not + * exist or the caller does not have access to it. + * @throws IllegalArgumentException when {@code blobHandle} is invalid. + * @throws IllegalStateException when a lease could not be acquired, such as when the + * caller is trying to acquire too many leases. + * + * @see {@link #acquireLease(BlobHandle, int, long)} + */ + public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId) + throws IOException { + acquireLease(blobHandle, descriptionResId, 0); + } + + /** + * Release all active leases to the blob represented by {@code blobHandle} which are + * currently held by the caller. + * + * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to + * release the leases for. + * + * @throws IOException when there is an I/O error while releasing the releases to the blob. + * @throws SecurityException when the blob represented by the {@code blobHandle} does not + * exist or the caller does not have access to it. + * @throws IllegalArgumentException when {@code blobHandle} is invalid. + */ + public void releaseLease(@NonNull BlobHandle blobHandle) throws IOException { + try { + mService.releaseLease(blobHandle, mContext.getOpPackageName()); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Represents an ongoing session of a blob's contribution to the blob store managed by the + * system. + * + * <p> Clients that want to contribute a blob need to first create a {@link Session} using + * {@link #createSession(BlobHandle)} and once the session is created, clients can open and + * close this session multiple times using {@link #openSession(long)} and + * {@link Session#close()} before committing it using + * {@link Session#commit(Executor, Consumer)}, at which point system will take + * ownership of the blob and the client can no longer make any modifications to the blob's + * content. + */ + public static class Session implements Closeable { + private final IBlobStoreSession mSession; + + private Session(@NonNull IBlobStoreSession session) { + mSession = session; + } + + /** + * Opens a file descriptor to write a blob into the session. + * + * <p> The returned file descriptor will start writing data at the requested offset + * in the underlying file, which can be used to resume a partially + * written file. If a valid file length is specified, the system will + * preallocate the underlying disk space to optimize placement on disk. + * It is strongly recommended to provide a valid file length when known. + * + * @param offsetBytes offset into the file to begin writing at, or 0 to + * start at the beginning of the file. + * @param lengthBytes total size of the file being written, used to + * preallocate the underlying disk space, or -1 if unknown. + * The system may clear various caches as needed to allocate + * this space. + * + * @return a {@link ParcelFileDescriptor} for writing to the blob file. + * + * @throws IOException when there is an I/O error while opening the file to write. + * @throws SecurityException when the caller is not the owner of the session. + * @throws IllegalStateException when the caller tries to write to the file after it is + * abandoned (using {@link #abandon()}) + * or committed (using {@link #commit}) + * or closed (using {@link #close()}). + */ + public @NonNull ParcelFileDescriptor openWrite(@BytesLong long offsetBytes, + @BytesLong long lengthBytes) throws IOException { + try { + return mSession.openWrite(offsetBytes, lengthBytes); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets the size of the blob file that was written to the session so far. + * + * @return the size of the blob file so far. + * + * @throws IOException when there is an I/O error while opening the file to read. + * @throws SecurityException when the caller is not the owner of the session. + * @throws IllegalStateException when the caller tries to get the file size after it is + * abandoned (using {@link #abandon()}) + * or closed (using {@link #close()}). + */ + public @BytesLong long getSize() throws IOException { + try { + return mSession.getSize(); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Close this session. It can be re-opened for writing/reading if it has not been + * abandoned (using {@link #abandon}) or closed (using {@link #commit}). + * + * @throws IOException when there is an I/O error while closing the session. + * @throws SecurityException when the caller is not the owner of the session. + */ + public void close() throws IOException { + try { + mSession.close(); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Abandon this session and delete any data that was written to this session so far. + * + * @throws IOException when there is an I/O error while abandoning the session. + * @throws SecurityException when the caller is not the owner of the session. + * @throws IllegalStateException when the caller tries to abandon a session which was + * already finalized. + */ + public void abandon() throws IOException { + try { + mSession.abandon(); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Allow {@code packageName} with a particular signing certificate to access this blob + * data once it is committed using a {@link BlobHandle} representing the blob. + * + * <p> This needs to be called before committing the blob using + * {@link #commit(Executor, Consumer)}. + * + * @param packageName the name of the package which should be allowed to access the blob. + * @param certificate the input bytes representing a certificate of type + * {@link android.content.pm.PackageManager#CERT_INPUT_SHA256}. + * + * @throws IOException when there is an I/O error while changing the access. + * @throws SecurityException when the caller is not the owner of the session. + * @throws IllegalStateException when the caller tries to change access for a blob which is + * already committed. + */ + public void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate) + throws IOException { + try { + mSession.allowPackageAccess(packageName, certificate); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Allow packages which are signed with the same certificate as the caller to access this + * blob data once it is committed using a {@link BlobHandle} representing the blob. + * + * <p> This needs to be called before committing the blob using + * {@link #commit(Executor, Consumer)}. + * + * @throws IOException when there is an I/O error while changing the access. + * @throws SecurityException when the caller is not the owner of the session. + * @throws IllegalStateException when the caller tries to change access for a blob which is + * already committed. + */ + public void allowSameSignatureAccess() throws IOException { + try { + mSession.allowSameSignatureAccess(); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Allow any app on the device to access this blob data once it is committed using + * a {@link BlobHandle} representing the blob. + * + * <p><strong>Note:</strong> This is only meant to be used from libraries and SDKs where + * the apps which we want to allow access is not known ahead of time. + * If a blob is being committed to be shared with a particular set of apps, it is highly + * recommended to use {@link #allowPackageAccess(String, byte[])} instead. + * + * <p> This needs to be called before committing the blob using + * {@link #commit(Executor, Consumer)}. + * + * @throws IOException when there is an I/O error while changing the access. + * @throws SecurityException when the caller is not the owner of the session. + * @throws IllegalStateException when the caller tries to change access for a blob which is + * already committed. + */ + public void allowPublicAccess() throws IOException { + try { + mSession.allowPublicAccess(); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Commit the file that was written so far to this session to the blob store maintained by + * the system. + * + * <p> Once this method is called, the session is finalized and no additional + * mutations can be performed on the session. If the device reboots + * before the session has been finalized, you may commit the session again. + * + * <p> Note that this commit operation will fail if the hash of the data written so far + * to this session does not match with the one used for + * {@link BlobHandle#createWithSha256(byte[], CharSequence, long, String)} BlobHandle} + * associated with this session. + * + * @param executor the executor on which result callback will be invoked. + * @param resultCallback a callback to receive the commit result. when the result is + * {@code 0}, it indicates success. Otherwise, failure. + * + * @throws IOException when there is an I/O error while committing the session. + * @throws SecurityException when the caller is not the owner of the session. + * @throws IllegalArgumentException when the passed parameters are not valid. + * @throws IllegalStateException when the caller tries to commit a session which was + * already finalized. + */ + public void commit(@NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<Integer> resultCallback) throws IOException { + try { + mSession.commit(new IBlobCommitCallback.Stub() { + public void onResult(int result) { + executor.execute(PooledLambda.obtainRunnable( + Consumer::accept, resultCallback, result)); + } + }); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } } diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobCommitCallback.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobCommitCallback.aidl new file mode 100644 index 000000000000..a9b30e20f5bd --- /dev/null +++ b/apex/blobstore/framework/java/android/app/blob/IBlobCommitCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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.blob; + +/** {@hide} */ +oneway interface IBlobCommitCallback { + void onResult(int result); +}
\ No newline at end of file diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl index 00c1ed4daa27..b7a2f1a58108 100644 --- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl +++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl @@ -15,6 +15,16 @@ */ package android.app.blob; +import android.app.blob.BlobHandle; +import android.app.blob.IBlobStoreSession; + /** {@hide} */ interface IBlobStoreManager { + long createSession(in BlobHandle handle, in String packageName); + IBlobStoreSession openSession(long sessionId); + ParcelFileDescriptor openBlob(in BlobHandle handle, in String packageName); + + void acquireLease(in BlobHandle handle, int descriptionResId, long leaseTimeout, + in String packageName); + void releaseLease(in BlobHandle handle, in String packageName); }
\ No newline at end of file diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl new file mode 100644 index 000000000000..bb5ef3b0d4b2 --- /dev/null +++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl @@ -0,0 +1,34 @@ +/* + * 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.blob; + +import android.app.blob.IBlobCommitCallback; +import android.os.ParcelFileDescriptor; + +/** {@hide} */ +interface IBlobStoreSession { + ParcelFileDescriptor openWrite(long offsetBytes, long lengthBytes); + + void allowPackageAccess(in String packageName, in byte[] certificate); + void allowSameSignatureAccess(); + void allowPublicAccess(); + + long getSize(); + void close(); + void abandon(); + + void commit(in IBlobCommitCallback callback); +}
\ No newline at end of file diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java index d7cab5998881..b204fee4a7c4 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -15,8 +15,14 @@ */ package com.android.server.blob; +import android.annotation.CurrentTimeSecondsLong; +import android.annotation.IdRes; +import android.annotation.NonNull; +import android.app.blob.BlobHandle; import android.app.blob.IBlobStoreManager; +import android.app.blob.IBlobStoreSession; import android.content.Context; +import android.os.ParcelFileDescriptor; import com.android.server.SystemService; @@ -25,8 +31,11 @@ import com.android.server.SystemService; */ public class BlobStoreManagerService extends SystemService { + private final Context mContext; + public BlobStoreManagerService(Context context) { super(context); + mContext = context; } @Override @@ -35,5 +44,29 @@ public class BlobStoreManagerService extends SystemService { } private class Stub extends IBlobStoreManager.Stub { + @Override + public long createSession(@NonNull BlobHandle blobHandle, @NonNull String packageName) { + return 0; + } + + @Override + public IBlobStoreSession openSession(long sessionId) { + return null; + } + + @Override + public ParcelFileDescriptor openBlob(@NonNull BlobHandle blobHandle, + @NonNull String packageName) { + return null; + } + + @Override + public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId, + @CurrentTimeSecondsLong long leaseTimeout, @NonNull String packageName) { + } + + @Override + public void releaseLease(@NonNull BlobHandle blobHandle, @NonNull String packageName) { + } } } diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java new file mode 100644 index 000000000000..2c38e769c520 --- /dev/null +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java @@ -0,0 +1,64 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.blob; + +import android.annotation.BytesLong; +import android.annotation.NonNull; +import android.app.blob.IBlobCommitCallback; +import android.app.blob.IBlobStoreSession; +import android.os.ParcelFileDescriptor; + +/** TODO: add doc */ +public class BlobStoreSession extends IBlobStoreSession.Stub { + + @Override + @NonNull + public ParcelFileDescriptor openWrite(@BytesLong long offsetBytes, + @BytesLong long lengthBytes) { + return null; + } + + @Override + @BytesLong + public long getSize() { + return 0; + } + + @Override + public void allowPackageAccess(@NonNull String packageName, + @NonNull byte[] certificate) { + } + + @Override + public void allowSameSignatureAccess() { + } + + @Override + public void allowPublicAccess() { + } + + @Override + public void close() { + } + + @Override + public void abandon() { + } + + @Override + public void commit(IBlobCommitCallback callback) { + } +} diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp index f8325d47e268..cc5172c6018a 100644 --- a/apex/statsd/aidl/Android.bp +++ b/apex/statsd/aidl/Android.bp @@ -23,7 +23,6 @@ filegroup { "android/os/IPullAtomResultReceiver.aidl", "android/os/IStatsCompanionService.aidl", "android/os/IStatsd.aidl", - "android/os/IStatsPullerCallback.aidl", "android/util/StatsEventParcel.aidl", ], } diff --git a/apex/statsd/aidl/android/os/IStatsPullerCallback.aidl b/apex/statsd/aidl/android/os/IStatsPullerCallback.aidl deleted file mode 100644 index c3e1e55dde06..000000000000 --- a/apex/statsd/aidl/android/os/IStatsPullerCallback.aidl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.os; - -import android.os.StatsLogEventWrapper; - -/** - * DEPRECATED - * Binder interface to pull atoms for the stats service. - * {@hide} - */ -interface IStatsPullerCallback { - /** - * Pull data for the specified atom tag. Returns an array of StatsLogEventWrapper containing - * the data. - * - * Note: These pulled atoms should not have uid/attribution chain. Additionally, the event - * timestamps will be truncated to the nearest 5 minutes. - */ - StatsLogEventWrapper[] pullData(int atomTag, long elapsedNanos, long wallClocknanos); - -} diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl index 0ecf2f0b4851..a2564212366f 100644 --- a/apex/statsd/aidl/android/os/IStatsd.aidl +++ b/apex/statsd/aidl/android/os/IStatsd.aidl @@ -16,7 +16,6 @@ package android.os; -import android.os.IStatsPullerCallback; import android.os.IPendingIntentRef; import android.os.IPullAtomCallback; import android.os.ParcelFileDescriptor; @@ -183,16 +182,6 @@ interface IStatsd { */ void sendAppBreadcrumbAtom(int label, int state); - /** - * Registers a puller callback function that, when invoked, pulls the data - * for the specified vendor atom tag. - * - * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS - * @deprecated please use registerPullAtomCallback. - */ - oneway void registerPullerCallback(int atomTag, IStatsPullerCallback pullerCallback, - String packageName); - /** * Registers a puller callback function that, when invoked, pulls the data * for the specified atom tag. @@ -207,13 +196,6 @@ interface IStatsd { oneway void registerNativePullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, in int[] additiveFields, IPullAtomCallback pullerCallback); - /** - * Unregisters a puller callback function for the given vendor atom. - * - * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS - */ - oneway void unregisterPullerCallback(int atomTag, String packageName); - /** * Unregisters any pullAtomCallback for the given uid/atom. */ diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index 8bc14d70ff87..e5d1182903f4 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -24,11 +24,11 @@ import static android.os.storage.VolumeInfo.TYPE_PRIVATE; import static android.os.storage.VolumeInfo.TYPE_PUBLIC; import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; -import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs; -import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs; -import static com.android.server.stats.ProcfsMemoryUtil.forEachPid; -import static com.android.server.stats.ProcfsMemoryUtil.readCmdlineFromProcfs; -import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs; +import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs; +import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs; +import static com.android.server.stats.pull.ProcfsMemoryUtil.forEachPid; +import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs; +import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs; import android.annotation.NonNull; import android.annotation.Nullable; @@ -135,8 +135,8 @@ import com.android.server.SystemServiceManager; import com.android.server.am.MemoryStatUtil.MemoryStat; import com.android.server.notification.NotificationManagerService; import com.android.server.role.RoleManagerInternal; -import com.android.server.stats.IonMemoryUtil.IonAllocations; -import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot; +import com.android.server.stats.pull.IonMemoryUtil.IonAllocations; +import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot; import com.android.server.storage.DiskStatsFileLogger; import com.android.server.storage.DiskStatsLoggingService; @@ -714,466 +714,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - private void pullSystemElapsedRealtime( - int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeLong(SystemClock.elapsedRealtime()); - pulledData.add(e); - } - - private void pullProcessMemoryState( - int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - List<ProcessMemoryState> processMemoryStates = - LocalServices.getService( - ActivityManagerInternal.class).getMemoryStateForProcesses(); - for (ProcessMemoryState processMemoryState : processMemoryStates) { - final MemoryStat memoryStat = readMemoryStatFromFilesystem(processMemoryState.uid, - processMemoryState.pid); - if (memoryStat == null) { - continue; - } - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(processMemoryState.uid); - e.writeString(processMemoryState.processName); - e.writeInt(processMemoryState.oomScore); - e.writeLong(memoryStat.pgfault); - e.writeLong(memoryStat.pgmajfault); - e.writeLong(memoryStat.rssInBytes); - e.writeLong(memoryStat.cacheInBytes); - e.writeLong(memoryStat.swapInBytes); - e.writeLong(-1); // unused - e.writeLong(-1); // unused - e.writeInt(-1); // unsed - pulledData.add(e); - } - } - - private void pullProcessMemoryHighWaterMark( - int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - List<ProcessMemoryState> managedProcessList = - LocalServices.getService( - ActivityManagerInternal.class).getMemoryStateForProcesses(); - for (ProcessMemoryState managedProcess : managedProcessList) { - final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid); - if (snapshot == null) { - continue; - } - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(managedProcess.uid); - e.writeString(managedProcess.processName); - // RSS high-water mark in bytes. - e.writeLong((long) snapshot.rssHighWaterMarkInKilobytes * 1024L); - e.writeInt(snapshot.rssHighWaterMarkInKilobytes); - pulledData.add(e); - } - forEachPid((pid, cmdLine) -> { - if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) { - return; - } - final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid); - if (snapshot == null) { - return; - } - // Sometimes we get here a process that is not included in the whitelist. It comes - // from forking the zygote for an app. We can ignore that sample because this process - // is collected by ProcessMemoryState. - if (isAppUid(snapshot.uid)) { - return; - } - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(snapshot.uid); - e.writeString(cmdLine); - // RSS high-water mark in bytes. - e.writeLong((long) snapshot.rssHighWaterMarkInKilobytes * 1024L); - e.writeInt(snapshot.rssHighWaterMarkInKilobytes); - pulledData.add(e); - }); - // Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes. - SystemProperties.set("sys.rss_hwm_reset.on", "1"); - } - - private void pullProcessMemorySnapshot( - int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - List<ProcessMemoryState> managedProcessList = - LocalServices.getService( - ActivityManagerInternal.class).getMemoryStateForProcesses(); - for (ProcessMemoryState managedProcess : managedProcessList) { - final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid); - if (snapshot == null) { - continue; - } - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(managedProcess.uid); - e.writeString(managedProcess.processName); - e.writeInt(managedProcess.pid); - e.writeInt(managedProcess.oomScore); - e.writeInt(snapshot.rssInKilobytes); - e.writeInt(snapshot.anonRssInKilobytes); - e.writeInt(snapshot.swapInKilobytes); - e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes); - pulledData.add(e); - } - forEachPid((pid, cmdLine) -> { - if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) { - return; - } - final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid); - if (snapshot == null) { - return; - } - // Sometimes we get here a process that is not included in the whitelist. It comes - // from forking the zygote for an app. We can ignore that sample because this process - // is collected by ProcessMemoryState. - if (isAppUid(snapshot.uid)) { - return; - } - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(snapshot.uid); - e.writeString(cmdLine); - e.writeInt(pid); - e.writeInt(-1001); // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1. - e.writeInt(snapshot.rssInKilobytes); - e.writeInt(snapshot.anonRssInKilobytes); - e.writeInt(snapshot.swapInKilobytes); - e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes); - pulledData.add(e); - }); - } - - private static boolean isAppUid(int uid) { - return uid >= MIN_APP_UID; - } - - private void pullSystemIonHeapSize( - int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs(); - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeLong(systemIonHeapSizeInBytes); - pulledData.add(e); - } - - private void pullProcessSystemIonHeapSize( - int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs(); - for (IonAllocations allocations : result) { - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(getUidForPid(allocations.pid)); - e.writeString(readCmdlineFromProcfs(allocations.pid)); - e.writeInt((int) (allocations.totalSizeInBytes / 1024)); - e.writeInt(allocations.count); - e.writeInt((int) (allocations.maxSizeInBytes / 1024)); - pulledData.add(e); - } - } - - private void pullBinderCallsStats( - int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - BinderCallsStatsService.Internal binderStats = - LocalServices.getService(BinderCallsStatsService.Internal.class); - if (binderStats == null) { - throw new IllegalStateException("binderStats is null"); - } - - List<ExportedCallStat> callStats = binderStats.getExportedCallStats(); - binderStats.reset(); - for (ExportedCallStat callStat : callStats) { - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(callStat.workSourceUid); - e.writeString(callStat.className); - e.writeString(callStat.methodName); - e.writeLong(callStat.callCount); - e.writeLong(callStat.exceptionCount); - e.writeLong(callStat.latencyMicros); - e.writeLong(callStat.maxLatencyMicros); - e.writeLong(callStat.cpuTimeMicros); - e.writeLong(callStat.maxCpuTimeMicros); - e.writeLong(callStat.maxReplySizeBytes); - e.writeLong(callStat.maxRequestSizeBytes); - e.writeLong(callStat.recordedCallCount); - e.writeInt(callStat.screenInteractive ? 1 : 0); - e.writeInt(callStat.callingUid); - pulledData.add(e); - } - } - - private void pullBinderCallsStatsExceptions( - int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - BinderCallsStatsService.Internal binderStats = - LocalServices.getService(BinderCallsStatsService.Internal.class); - if (binderStats == null) { - throw new IllegalStateException("binderStats is null"); - } - - ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats(); - // TODO: decouple binder calls exceptions with the rest of the binder calls data so that we - // can reset the exception stats. - for (Entry<String, Integer> entry : exceptionStats.entrySet()) { - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeString(entry.getKey()); - e.writeInt(entry.getValue()); - pulledData.add(e); - } - } - - private void pullLooperStats(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - LooperStats looperStats = LocalServices.getService(LooperStats.class); - if (looperStats == null) { - throw new IllegalStateException("looperStats null"); - } - - List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); - looperStats.reset(); - for (LooperStats.ExportedEntry entry : entries) { - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(entry.workSourceUid); - e.writeString(entry.handlerClassName); - e.writeString(entry.threadName); - e.writeString(entry.messageName); - e.writeLong(entry.messageCount); - e.writeLong(entry.exceptionCount); - e.writeLong(entry.recordedMessageCount); - e.writeLong(entry.totalLatencyMicros); - e.writeLong(entry.cpuUsageMicros); - e.writeBoolean(entry.isInteractive); - e.writeLong(entry.maxCpuUsageMicros); - e.writeLong(entry.maxLatencyMicros); - e.writeLong(entry.recordedDelayMessageCount); - e.writeLong(entry.delayMillis); - e.writeLong(entry.maxDelayMillis); - pulledData.add(e); - } - } - - private void pullDiskStats(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - // Run a quick-and-dirty performance test: write 512 bytes - byte[] junk = new byte[512]; - for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes - - File tmp = new File(Environment.getDataDirectory(), "system/statsdperftest.tmp"); - FileOutputStream fos = null; - IOException error = null; - - long before = SystemClock.elapsedRealtime(); - try { - fos = new FileOutputStream(tmp); - fos.write(junk); - } catch (IOException e) { - error = e; - } finally { - try { - if (fos != null) fos.close(); - } catch (IOException e) { - // Do nothing. - } - } - - long latency = SystemClock.elapsedRealtime() - before; - if (tmp.exists()) tmp.delete(); - - if (error != null) { - Slog.e(TAG, "Error performing diskstats latency test"); - latency = -1; - } - // File based encryption. - boolean fileBased = StorageManager.isFileEncryptedNativeOnly(); - - //Recent disk write speed. Binder call to storaged. - int writeSpeed = -1; - try { - IBinder binder = ServiceManager.getService("storaged"); - if (binder == null) { - Slog.e(TAG, "storaged not found"); - } - IStoraged storaged = IStoraged.Stub.asInterface(binder); - writeSpeed = storaged.getRecentPerf(); - } catch (RemoteException e) { - Slog.e(TAG, "storaged not found"); - } - - // Add info pulledData. - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeLong(latency); - e.writeBoolean(fileBased); - e.writeInt(writeSpeed); - pulledData.add(e); - } - - private void pullDirectoryUsage(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath()); - StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath()); - StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath()); - - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__DATA); - e.writeLong(statFsData.getAvailableBytes()); - e.writeLong(statFsData.getTotalBytes()); - pulledData.add(e); - - e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE); - e.writeLong(statFsCache.getAvailableBytes()); - e.writeLong(statFsCache.getTotalBytes()); - pulledData.add(e); - - e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM); - e.writeLong(statFsSystem.getAvailableBytes()); - e.writeLong(statFsSystem.getTotalBytes()); - pulledData.add(e); - } - - private void pullAppSize(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - try { - String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH); - JSONObject json = new JSONObject(jsonStr); - long cache_time = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L); - JSONArray pkg_names = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY); - JSONArray app_sizes = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY); - JSONArray app_data_sizes = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY); - JSONArray app_cache_sizes = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY); - // Sanity check: Ensure all 4 lists have the same length. - int length = pkg_names.length(); - if (app_sizes.length() != length || app_data_sizes.length() != length - || app_cache_sizes.length() != length) { - Slog.e(TAG, "formatting error in diskstats cache file!"); - return; - } - for (int i = 0; i < length; i++) { - StatsLogEventWrapper e = - new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeString(pkg_names.getString(i)); - e.writeLong(app_sizes.optLong(i, -1L)); - e.writeLong(app_data_sizes.optLong(i, -1L)); - e.writeLong(app_cache_sizes.optLong(i, -1L)); - e.writeLong(cache_time); - pulledData.add(e); - } - } catch (IOException | JSONException e) { - Slog.e(TAG, "exception reading diskstats cache file", e); - } - } - - private void pullCategorySize(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - try { - String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH); - JSONObject json = new JSONObject(jsonStr); - long cacheTime = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L); - - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE); - e.writeLong(json.optLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY, -1L)); - e.writeLong(cacheTime); - pulledData.add(e); - - e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE); - e.writeLong(json.optLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, -1L)); - e.writeLong(cacheTime); - pulledData.add(e); - - e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE); - e.writeLong(json.optLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY, -1L)); - e.writeLong(cacheTime); - pulledData.add(e); - - e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS); - e.writeLong(json.optLong(DiskStatsFileLogger.PHOTOS_KEY, -1L)); - e.writeLong(cacheTime); - pulledData.add(e); - - e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS); - e.writeLong(json.optLong(DiskStatsFileLogger.VIDEOS_KEY, -1L)); - e.writeLong(cacheTime); - pulledData.add(e); - - e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__AUDIO); - e.writeLong(json.optLong(DiskStatsFileLogger.AUDIO_KEY, -1L)); - e.writeLong(cacheTime); - pulledData.add(e); - - e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS); - e.writeLong(json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, -1L)); - e.writeLong(cacheTime); - pulledData.add(e); - - e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM); - e.writeLong(json.optLong(DiskStatsFileLogger.SYSTEM_KEY, -1L)); - e.writeLong(cacheTime); - pulledData.add(e); - - e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__OTHER); - e.writeLong(json.optLong(DiskStatsFileLogger.MISC_KEY, -1L)); - e.writeLong(cacheTime); - pulledData.add(e); - } catch (IOException | JSONException e) { - Slog.e(TAG, "exception reading diskstats cache file", e); - } - } - - private void pullNumBiometricsEnrolled(int modality, int tagId, long elapsedNanos, - long wallClockNanos, List<StatsLogEventWrapper> pulledData) { - final PackageManager pm = mContext.getPackageManager(); - FingerprintManager fingerprintManager = null; - FaceManager faceManager = null; - - if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { - fingerprintManager = mContext.getSystemService( - FingerprintManager.class); - } - if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) { - faceManager = mContext.getSystemService(FaceManager.class); - } - - if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT && fingerprintManager == null) { - return; - } - if (modality == BiometricsProtoEnums.MODALITY_FACE && faceManager == null) { - return; - } - UserManager userManager = mContext.getSystemService(UserManager.class); - if (userManager == null) { - return; - } - - final long token = Binder.clearCallingIdentity(); - for (UserInfo user : userManager.getUsers()) { - final int userId = user.getUserHandle().getIdentifier(); - int numEnrolled = 0; - if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) { - numEnrolled = fingerprintManager.getEnrolledFingerprints(userId).size(); - } else if (modality == BiometricsProtoEnums.MODALITY_FACE) { - numEnrolled = faceManager.getEnrolledFaces(userId).size(); - } else { - return; - } - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(userId); - e.writeInt(numEnrolled); - pulledData.add(e); - } - Binder.restoreCallingIdentity(token); - } - // read high watermark for section private long readProcStatsHighWaterMark(int section) { try { @@ -1225,39 +765,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - private INotificationManager mNotificationManager = - INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)); - - private void pullNotificationStats(int reportId, int tagId, long elapsedNanos, - long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - final long callingToken = Binder.clearCallingIdentity(); - try { - // determine last pull tine. Copy file trick from pullProcessStats? - long lastNotificationStatsNs = wallClockNanos - - TimeUnit.NANOSECONDS.convert(1, TimeUnit.DAYS); - - List<ParcelFileDescriptor> statsFiles = new ArrayList<>(); - long notificationStatsNs = mNotificationManager.pullStats( - lastNotificationStatsNs, reportId, true, statsFiles); - if (statsFiles.size() != 1) { - return; - } - unpackStreamedData(tagId, elapsedNanos, wallClockNanos, pulledData, statsFiles); - } catch (IOException e) { - Log.e(TAG, "Getting notistats failed: ", e); - - } catch (RemoteException e) { - Log.e(TAG, "Getting notistats failed: ", e); - } catch (SecurityException e) { - Log.e(TAG, "Getting notistats failed: ", e); - } finally { - Binder.restoreCallingIdentity(callingToken); - } - - } - static void unpackStreamedData(int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData, List<ParcelFileDescriptor> statsFiles) throws IOException { @@ -1299,147 +806,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead, - fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite, - fgFsync, bgFsync) -> { - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, - wallClockNanos); - e.writeInt(uid); - e.writeLong(fgCharsRead); - e.writeLong(fgCharsWrite); - e.writeLong(fgBytesRead); - e.writeLong(fgBytesWrite); - e.writeLong(bgCharsRead); - e.writeLong(bgCharsWrite); - e.writeLong(bgBytesRead); - e.writeLong(bgBytesWrite); - e.writeLong(fgFsync); - e.writeLong(bgFsync); - pulledData.add(e); - }); - } - - private void pullProcessCpuTime(int tagId, long elapsedNanos, final long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - synchronized (this) { - if (mProcessCpuTracker == null) { - mProcessCpuTracker = new ProcessCpuTracker(false); - mProcessCpuTracker.init(); - } - mProcessCpuTracker.update(); - for (int i = 0; i < mProcessCpuTracker.countStats(); i++) { - ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, - wallClockNanos); - e.writeInt(st.uid); - e.writeString(st.name); - e.writeLong(st.base_utime); - e.writeLong(st.base_stime); - pulledData.add(e); - } - } - } - - private void pullCpuTimePerThreadFreq(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - if (this.mKernelCpuThreadReader == null) { - throw new IllegalStateException("mKernelCpuThreadReader is null"); - } - ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages = - this.mKernelCpuThreadReader.getProcessCpuUsageDiffed(); - if (processCpuUsages == null) { - throw new IllegalStateException("processCpuUsages is null"); - } - int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz(); - if (cpuFrequencies.length > CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES) { - String message = "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES - + " frequencies, but got " + cpuFrequencies.length; - Slog.w(TAG, message); - throw new IllegalStateException(message); - } - for (int i = 0; i < processCpuUsages.size(); i++) { - KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i); - ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = - processCpuUsage.threadCpuUsages; - for (int j = 0; j < threadCpuUsages.size(); j++) { - KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j); - if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) { - String message = "Unexpected number of usage times," - + " expected " + cpuFrequencies.length - + " but got " + threadCpuUsage.usageTimesMillis.length; - Slog.w(TAG, message); - throw new IllegalStateException(message); - } - - StatsLogEventWrapper e = - new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(processCpuUsage.uid); - e.writeInt(processCpuUsage.processId); - e.writeInt(threadCpuUsage.threadId); - e.writeString(processCpuUsage.processName); - e.writeString(threadCpuUsage.threadName); - for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES; k++) { - if (k < cpuFrequencies.length) { - e.writeInt(cpuFrequencies[k]); - e.writeInt(threadCpuUsage.usageTimesMillis[k]); - } else { - // If we have no more frequencies to write, we still must write empty data. - // We know that this data is empty (and not just zero) because all - // frequencies are expected to be greater than zero - e.writeInt(0); - e.writeInt(0); - } - } - pulledData.add(e); - } - } - } - - private void pullTemperature(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - long callingToken = Binder.clearCallingIdentity(); - try { - List<Temperature> temperatures = sThermalService.getCurrentTemperatures(); - for (Temperature temp : temperatures) { - StatsLogEventWrapper e = - new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(temp.getType()); - e.writeString(temp.getName()); - e.writeInt((int) (temp.getValue() * 10)); - e.writeInt(temp.getStatus()); - pulledData.add(e); - } - } catch (RemoteException e) { - // Should not happen. - Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures."); - } finally { - Binder.restoreCallingIdentity(callingToken); - } - } - - private void pullCoolingDevices(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - long callingToken = Binder.clearCallingIdentity(); - try { - List<CoolingDevice> devices = sThermalService.getCurrentCoolingDevices(); - for (CoolingDevice device : devices) { - StatsLogEventWrapper e = - new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(device.getType()); - e.writeString(device.getName()); - e.writeInt((int) (device.getValue())); - pulledData.add(e); - } - } catch (RemoteException e) { - // Should not happen. - Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures."); - } finally { - Binder.restoreCallingIdentity(callingToken); - } - } - private void pullDebugElapsedClock(int tagId, long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) { final long elapsedMillis = SystemClock.elapsedRealtime(); @@ -1490,283 +856,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pulledData.add(e); } - private void pullDangerousPermissionState(int atomId, long elapsedNanos, - final long wallClockNanos, List<StatsLogEventWrapper> pulledData) { - long token = Binder.clearCallingIdentity(); - Set<Integer> reportedUids = new HashSet<>(); - try { - PackageManager pm = mContext.getPackageManager(); - - List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers(); - - int numUsers = users.size(); - for (int userNum = 0; userNum < numUsers; userNum++) { - UserHandle user = users.get(userNum).getUserHandle(); - - List<PackageInfo> pkgs = pm.getInstalledPackagesAsUser( - PackageManager.GET_PERMISSIONS, user.getIdentifier()); - - int numPkgs = pkgs.size(); - for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) { - PackageInfo pkg = pkgs.get(pkgNum); - - if (pkg.requestedPermissions == null) { - continue; - } - - if (reportedUids.contains(pkg.applicationInfo.uid)) { - // do not report same uid twice - continue; - } - reportedUids.add(pkg.applicationInfo.uid); - - if (atomId == StatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED - && ThreadLocalRandom.current().nextFloat() > 0.2f) { - continue; - } - - int numPerms = pkg.requestedPermissions.length; - for (int permNum = 0; permNum < numPerms; permNum++) { - String permName = pkg.requestedPermissions[permNum]; - - PermissionInfo permissionInfo; - int permissionFlags = 0; - try { - permissionInfo = pm.getPermissionInfo(permName, 0); - permissionFlags = - pm.getPermissionFlags(permName, pkg.packageName, user); - - } catch (PackageManager.NameNotFoundException ignored) { - continue; - } - - if (permissionInfo.getProtection() != PROTECTION_DANGEROUS) { - continue; - } - - StatsLogEventWrapper e = new StatsLogEventWrapper( - atomId, elapsedNanos, wallClockNanos); - - e.writeString(permName); - e.writeInt(pkg.applicationInfo.uid); - if (atomId == StatsLog.DANGEROUS_PERMISSION_STATE) { - e.writeString(null); - } - e.writeBoolean((pkg.requestedPermissionsFlags[permNum] - & REQUESTED_PERMISSION_GRANTED) != 0); - e.writeInt(permissionFlags); - - pulledData.add(e); - } - } - } - } catch (Throwable t) { - Log.e(TAG, "Could not read permissions", t); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - private void pullAppOps(long elapsedNanos, final long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - long token = Binder.clearCallingIdentity(); - try { - AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); - - CompletableFuture<HistoricalOps> ops = new CompletableFuture<>(); - HistoricalOpsRequest histOpsRequest = - new HistoricalOpsRequest.Builder(0, Long.MAX_VALUE).build(); - appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete); - - HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, - TimeUnit.MILLISECONDS); - - for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) { - final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx); - final int uid = uidOps.getUid(); - for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) { - final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx); - for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) { - final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx); - StatsLogEventWrapper e = new StatsLogEventWrapper(StatsLog.APP_OPS, - elapsedNanos, wallClockNanos); - - e.writeInt(uid); - e.writeString(packageOps.getPackageName()); - e.writeInt(op.getOpCode()); - e.writeLong(op.getForegroundAccessCount(OP_FLAGS_ALL_TRUSTED)); - e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_ALL_TRUSTED)); - e.writeLong(op.getForegroundRejectCount(OP_FLAGS_ALL_TRUSTED)); - e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_ALL_TRUSTED)); - e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_ALL_TRUSTED)); - e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_ALL_TRUSTED)); - - String perm = AppOpsManager.opToPermission(op.getOpCode()); - if (perm == null) { - e.writeBoolean(false); - } else { - PermissionInfo permInfo; - try { - permInfo = mContext.getPackageManager().getPermissionInfo(perm, 0); - e.writeBoolean(permInfo.getProtection() == PROTECTION_DANGEROUS); - } catch (PackageManager.NameNotFoundException exception) { - e.writeBoolean(false); - } - } - - pulledData.add(e); - } - } - } - } catch (Throwable t) { - Log.e(TAG, "Could not read appops", t); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - - /** - * Add a RoleHolder atom for each package that holds a role. - * - * @param elapsedNanos the time since boot - * @param wallClockNanos the time on the clock - * @param pulledData the data sink to write to - */ - private void pullRoleHolders(long elapsedNanos, final long wallClockNanos, - @NonNull List<StatsLogEventWrapper> pulledData) { - long callingToken = Binder.clearCallingIdentity(); - try { - PackageManager pm = mContext.getPackageManager(); - RoleManagerInternal rmi = LocalServices.getService(RoleManagerInternal.class); - - List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers(); - - int numUsers = users.size(); - for (int userNum = 0; userNum < numUsers; userNum++) { - int userId = users.get(userNum).getUserHandle().getIdentifier(); - - ArrayMap<String, ArraySet<String>> roles = rmi.getRolesAndHolders( - userId); - - int numRoles = roles.size(); - for (int roleNum = 0; roleNum < numRoles; roleNum++) { - String roleName = roles.keyAt(roleNum); - ArraySet<String> holders = roles.valueAt(roleNum); - - int numHolders = holders.size(); - for (int holderNum = 0; holderNum < numHolders; holderNum++) { - String holderName = holders.valueAt(holderNum); - - PackageInfo pkg; - try { - pkg = pm.getPackageInfoAsUser(holderName, 0, userId); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Role holder " + holderName + " not found"); - return; - } - - StatsLogEventWrapper e = new StatsLogEventWrapper(StatsLog.ROLE_HOLDER, - elapsedNanos, wallClockNanos); - e.writeInt(pkg.applicationInfo.uid); - e.writeString(holderName); - e.writeString(roleName); - pulledData.add(e); - } - } - } - } finally { - Binder.restoreCallingIdentity(callingToken); - } - } - - private void pullTimeZoneDataInfo(int tagId, - long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { - String tzDbVersion = "Unknown"; - try { - tzDbVersion = android.icu.util.TimeZone.getTZDataVersion(); - } catch (Exception e) { - Log.e(TAG, "Getting tzdb version failed: ", e); - } - - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeString(tzDbVersion); - pulledData.add(e); - } - - private void pullExternalStorageInfo(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - StorageManager storageManager = mContext.getSystemService(StorageManager.class); - if (storageManager != null) { - List<VolumeInfo> volumes = storageManager.getVolumes(); - for (VolumeInfo vol : volumes) { - final String envState = VolumeInfo.getEnvironmentForState(vol.getState()); - final DiskInfo diskInfo = vol.getDisk(); - if (diskInfo != null) { - if (envState.equals(Environment.MEDIA_MOUNTED)) { - // Get the type of the volume, if it is adoptable or portable. - int volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__OTHER; - if (vol.getType() == TYPE_PUBLIC) { - volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PUBLIC; - } else if (vol.getType() == TYPE_PRIVATE) { - volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PRIVATE; - } - // Get the type of external storage inserted in the device (sd cards, - // usb, etc) - int externalStorageType; - if (diskInfo.isSd()) { - externalStorageType = StorageEnums.SD_CARD; - } else if (diskInfo.isUsb()) { - externalStorageType = StorageEnums.USB; - } else { - externalStorageType = StorageEnums.OTHER; - } - StatsLogEventWrapper e = - new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(externalStorageType); - e.writeInt(volumeType); - e.writeLong(diskInfo.size); - pulledData.add(e); - } - } - } - } - } - - private void pullAppsOnExternalStorageInfo(int tagId, long elapsedNanos, long wallClockNanos, - List<StatsLogEventWrapper> pulledData) { - PackageManager pm = mContext.getPackageManager(); - StorageManager storage = mContext.getSystemService(StorageManager.class); - List<ApplicationInfo> apps = pm.getInstalledApplications(/* flags = */ 0); - for (ApplicationInfo appInfo : apps) { - UUID storageUuid = appInfo.storageUuid; - if (storageUuid != null) { - VolumeInfo volumeInfo = storage.findVolumeByUuid(appInfo.storageUuid.toString()); - if (volumeInfo != null) { - DiskInfo diskInfo = volumeInfo.getDisk(); - if (diskInfo != null) { - int externalStorageType = -1; - if (diskInfo.isSd()) { - externalStorageType = StorageEnums.SD_CARD; - } else if (diskInfo.isUsb()) { - externalStorageType = StorageEnums.USB; - } else if (appInfo.isExternal()) { - externalStorageType = StorageEnums.OTHER; - } - // App is installed on external storage. - if (externalStorageType != -1) { - StatsLogEventWrapper e = - new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeInt(externalStorageType); - e.writeString(appInfo.packageName); - pulledData.add(e); - } - } - } - } - } - } - private void pullFaceSettings(int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { long callingToken = Binder.clearCallingIdentity(); @@ -1818,83 +907,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { long wallClockNanos = SystemClock.currentTimeMicro() * 1000L; switch (tagId) { - case StatsLog.SYSTEM_ELAPSED_REALTIME: { - pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.PROCESS_MEMORY_STATE: { - pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK: { - pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.PROCESS_MEMORY_SNAPSHOT: { - pullProcessMemorySnapshot(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.SYSTEM_ION_HEAP_SIZE: { - pullSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE: { - pullProcessSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.BINDER_CALLS: { - pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.BINDER_CALLS_EXCEPTIONS: { - pullBinderCallsStatsExceptions(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.LOOPER_STATS: { - pullLooperStats(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.DISK_STATS: { - pullDiskStats(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.DIRECTORY_USAGE: { - pullDirectoryUsage(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.APP_SIZE: { - pullAppSize(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.CATEGORY_SIZE: { - pullCategorySize(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.NUM_FINGERPRINTS_ENROLLED: { - pullNumBiometricsEnrolled(BiometricsProtoEnums.MODALITY_FINGERPRINT, tagId, - elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.NUM_FACES_ENROLLED: { - pullNumBiometricsEnrolled(BiometricsProtoEnums.MODALITY_FACE, tagId, elapsedNanos, - wallClockNanos, ret); - break; - } - case StatsLog.PROC_STATS: { pullProcessStats(ProcessStats.REPORT_ALL, tagId, elapsedNanos, wallClockNanos, ret); break; @@ -1906,30 +918,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { break; } - case StatsLog.DISK_IO: { - pullDiskIo(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.PROCESS_CPU_TIME: { - pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - case StatsLog.CPU_TIME_PER_THREAD_FREQ: { - pullCpuTimePerThreadFreq(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.TEMPERATURE: { - pullTemperature(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.COOLING_DEVICE: { - pullCoolingDevices(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - case StatsLog.DEBUG_ELAPSED_CLOCK: { pullDebugElapsedClock(tagId, elapsedNanos, wallClockNanos, ret); break; @@ -1940,54 +928,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { break; } - case StatsLog.ROLE_HOLDER: { - pullRoleHolders(elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.DANGEROUS_PERMISSION_STATE: { - pullDangerousPermissionState(StatsLog.DANGEROUS_PERMISSION_STATE, elapsedNanos, - wallClockNanos, ret); - break; - } - - case StatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED: { - pullDangerousPermissionState(StatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED, - elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.TIME_ZONE_DATA_INFO: { - pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.EXTERNAL_STORAGE_INFO: { - pullExternalStorageInfo(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.APPS_ON_EXTERNAL_STORAGE_INFO: { - pullAppsOnExternalStorageInfo(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - case StatsLog.FACE_SETTINGS: { pullFaceSettings(tagId, elapsedNanos, wallClockNanos, ret); break; } - case StatsLog.APP_OPS: { - pullAppOps(elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.NOTIFICATION_REMOTE_VIEWS: { - pullNotificationStats(NotificationManagerService.REPORT_REMOTE_VIEWS, - tagId, elapsedNanos, wallClockNanos, ret); - break; - } - default: Slog.w(TAG, "No such tagId data as " + tagId); return null; diff --git a/api/current.txt b/api/current.txt index e27c318c09b9..1210d627c99e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2854,6 +2854,7 @@ package android.accessibilityservice { method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow(); method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo(); method @NonNull public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController(); + method @NonNull public final java.util.List<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> getSystemActions(); method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows(); method @NonNull public final android.util.SparseArray<java.util.List<android.view.accessibility.AccessibilityWindowInfo>> getWindowsOnAllDisplays(); method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent); @@ -2863,6 +2864,7 @@ package android.accessibilityservice { method public abstract void onInterrupt(); method protected boolean onKeyEvent(android.view.KeyEvent); method protected void onServiceConnected(); + method public void onSystemActionsChanged(); method public final boolean performGlobalAction(int); method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>); @@ -6831,6 +6833,7 @@ package android.app.admin { method public boolean isApplicationHidden(@NonNull android.content.ComponentName, String); method public boolean isBackupServiceEnabled(@NonNull android.content.ComponentName); method @Deprecated public boolean isCallerApplicationRestrictionsManagingPackage(); + method public boolean isCommonCriteriaModeEnabled(@NonNull android.content.ComponentName); method public boolean isDeviceIdAttestationSupported(); method public boolean isDeviceOwnerApp(String); method public boolean isEphemeralUser(@NonNull android.content.ComponentName); @@ -6879,6 +6882,7 @@ package android.app.admin { method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean); method public void setCameraDisabled(@NonNull android.content.ComponentName, boolean); method @Deprecated public void setCertInstallerPackage(@NonNull android.content.ComponentName, @Nullable String) throws java.lang.SecurityException; + method public void setCommonCriteriaModeEnabled(@NonNull android.content.ComponentName, boolean); method public void setCrossProfileCalendarPackages(@NonNull android.content.ComponentName, @Nullable java.util.Set<java.lang.String>); method public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean); method public void setCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName, boolean); @@ -7447,6 +7451,41 @@ package android.app.backup { } +package android.app.blob { + + public final class BlobHandle implements android.os.Parcelable { + method @NonNull public static android.app.blob.BlobHandle createWithSha256(@NonNull byte[], @NonNull CharSequence, long, @NonNull String); + method public int describeContents(); + method public long getExpiryTimeMillis(); + method @NonNull public CharSequence getLabel(); + method @NonNull public byte[] getSha256Digest(); + method @NonNull public String getTag(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.blob.BlobHandle> CREATOR; + } + + public class BlobStoreManager { + method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int, long) throws java.io.IOException; + method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int) throws java.io.IOException; + method @IntRange(from=1) public long createSession(@NonNull android.app.blob.BlobHandle) throws java.io.IOException; + method @NonNull public android.os.ParcelFileDescriptor openBlob(@NonNull android.app.blob.BlobHandle) throws java.io.IOException; + method @NonNull public android.app.blob.BlobStoreManager.Session openSession(@IntRange(from=1) long) throws java.io.IOException; + method public void releaseLease(@NonNull android.app.blob.BlobHandle) throws java.io.IOException; + } + + public static class BlobStoreManager.Session implements java.io.Closeable { + method public void abandon() throws java.io.IOException; + method public void allowPackageAccess(@NonNull String, @NonNull byte[]) throws java.io.IOException; + method public void allowPublicAccess() throws java.io.IOException; + method public void allowSameSignatureAccess() throws java.io.IOException; + method public void close() throws java.io.IOException; + method public void commit(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws java.io.IOException; + method public long getSize() throws java.io.IOException; + method @NonNull public android.os.ParcelFileDescriptor openWrite(long, long) throws java.io.IOException; + } + +} + package android.app.job { public class JobInfo implements android.os.Parcelable { @@ -11395,6 +11434,7 @@ package android.content.pm { method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle); method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles(); method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle); + field public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED"; } public final class FeatureGroupInfo implements android.os.Parcelable { @@ -13211,6 +13251,8 @@ package android.database.sqlite { method public static int releaseMemory(); method public long replace(String, String, android.content.ContentValues); method public long replaceOrThrow(String, String, android.content.ContentValues) throws android.database.SQLException; + method public void setCustomAggregateFunction(@NonNull String, @NonNull java.util.function.BinaryOperator<java.lang.String>) throws android.database.sqlite.SQLiteException; + method public void setCustomScalarFunction(@NonNull String, @NonNull java.util.function.UnaryOperator<java.lang.String>) throws android.database.sqlite.SQLiteException; method public void setForeignKeyConstraintsEnabled(boolean); method public void setLocale(java.util.Locale); method @Deprecated public void setLockingEnabled(boolean); @@ -18261,6 +18303,7 @@ package android.icu.lang { field public static final int RIGHT = 7; // 0x7 field public static final int TOP = 8; // 0x8 field public static final int TOP_AND_BOTTOM = 9; // 0x9 + field public static final int TOP_AND_BOTTOM_AND_LEFT = 15; // 0xf field public static final int TOP_AND_BOTTOM_AND_RIGHT = 10; // 0xa field public static final int TOP_AND_LEFT = 11; // 0xb field public static final int TOP_AND_LEFT_AND_RIGHT = 12; // 0xc @@ -18583,6 +18626,7 @@ package android.icu.lang { field public static final int CHEROKEE_SUPPLEMENT_ID = 255; // 0xff field public static final android.icu.lang.UCharacter.UnicodeBlock CHESS_SYMBOLS; field public static final int CHESS_SYMBOLS_ID = 281; // 0x119 + field public static final int CHORASMIAN_ID = 301; // 0x12d field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_COMPATIBILITY; field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_COMPATIBILITY_FORMS; field public static final int CJK_COMPATIBILITY_FORMS_ID = 83; // 0x53 @@ -18610,6 +18654,7 @@ package android.icu.lang { field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E_ID = 256; // 0x100 field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F; field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F_ID = 274; // 0x112 + field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G_ID = 302; // 0x12e field public static final int CJK_UNIFIED_IDEOGRAPHS_ID = 71; // 0x47 field public static final android.icu.lang.UCharacter.UnicodeBlock COMBINING_DIACRITICAL_MARKS; field public static final android.icu.lang.UCharacter.UnicodeBlock COMBINING_DIACRITICAL_MARKS_EXTENDED; @@ -18659,6 +18704,7 @@ package android.icu.lang { field public static final int DEVANAGARI_ID = 15; // 0xf field public static final android.icu.lang.UCharacter.UnicodeBlock DINGBATS; field public static final int DINGBATS_ID = 56; // 0x38 + field public static final int DIVES_AKURU_ID = 303; // 0x12f field public static final android.icu.lang.UCharacter.UnicodeBlock DOGRA; field public static final int DOGRA_ID = 282; // 0x11a field public static final android.icu.lang.UCharacter.UnicodeBlock DOMINO_TILES; @@ -18787,6 +18833,8 @@ package android.icu.lang { field public static final int KAYAH_LI_ID = 162; // 0xa2 field public static final android.icu.lang.UCharacter.UnicodeBlock KHAROSHTHI; field public static final int KHAROSHTHI_ID = 137; // 0x89 + field public static final android.icu.lang.UCharacter.UnicodeBlock KHITAN_SMALL_SCRIPT; + field public static final int KHITAN_SMALL_SCRIPT_ID = 304; // 0x130 field public static final android.icu.lang.UCharacter.UnicodeBlock KHMER; field public static final int KHMER_ID = 36; // 0x24 field public static final android.icu.lang.UCharacter.UnicodeBlock KHMER_SYMBOLS; @@ -18825,6 +18873,8 @@ package android.icu.lang { field public static final int LINEAR_B_SYLLABARY_ID = 117; // 0x75 field public static final android.icu.lang.UCharacter.UnicodeBlock LISU; field public static final int LISU_ID = 176; // 0xb0 + field public static final android.icu.lang.UCharacter.UnicodeBlock LISU_SUPPLEMENT; + field public static final int LISU_SUPPLEMENT_ID = 305; // 0x131 field public static final android.icu.lang.UCharacter.UnicodeBlock LOW_SURROGATES; field public static final int LOW_SURROGATES_ID = 77; // 0x4d field public static final android.icu.lang.UCharacter.UnicodeBlock LYCIAN; @@ -19036,6 +19086,8 @@ package android.icu.lang { field public static final int SYLOTI_NAGRI_ID = 143; // 0x8f field public static final android.icu.lang.UCharacter.UnicodeBlock SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A; field public static final int SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A_ID = 298; // 0x12a + field public static final android.icu.lang.UCharacter.UnicodeBlock SYMBOLS_FOR_LEGACY_COMPUTING; + field public static final int SYMBOLS_FOR_LEGACY_COMPUTING_ID = 306; // 0x132 field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC; field public static final int SYRIAC_ID = 13; // 0xd field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC_SUPPLEMENT; @@ -19064,6 +19116,8 @@ package android.icu.lang { field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT_COMPONENTS; field public static final int TANGUT_COMPONENTS_ID = 273; // 0x111 field public static final int TANGUT_ID = 272; // 0x110 + field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT_SUPPLEMENT; + field public static final int TANGUT_SUPPLEMENT_ID = 307; // 0x133 field public static final android.icu.lang.UCharacter.UnicodeBlock TELUGU; field public static final int TELUGU_ID = 21; // 0x15 field public static final android.icu.lang.UCharacter.UnicodeBlock THAANA; @@ -19098,6 +19152,8 @@ package android.icu.lang { field public static final int WANCHO_ID = 300; // 0x12c field public static final android.icu.lang.UCharacter.UnicodeBlock WARANG_CITI; field public static final int WARANG_CITI_ID = 252; // 0xfc + field public static final android.icu.lang.UCharacter.UnicodeBlock YEZIDI; + field public static final int YEZIDI_ID = 308; // 0x134 field public static final android.icu.lang.UCharacter.UnicodeBlock YIJING_HEXAGRAM_SYMBOLS; field public static final int YIJING_HEXAGRAM_SYMBOLS_ID = 116; // 0x74 field public static final android.icu.lang.UCharacter.UnicodeBlock YI_RADICALS; @@ -19394,6 +19450,7 @@ package android.icu.lang { field public static final int CHAKMA = 118; // 0x76 field public static final int CHAM = 66; // 0x42 field public static final int CHEROKEE = 6; // 0x6 + field public static final int CHORASMIAN = 189; // 0xbd field public static final int CIRTH = 67; // 0x43 field public static final int COMMON = 0; // 0x0 field public static final int COPTIC = 7; // 0x7 @@ -19403,6 +19460,7 @@ package android.icu.lang { field public static final int DEMOTIC_EGYPTIAN = 69; // 0x45 field public static final int DESERET = 9; // 0x9 field public static final int DEVANAGARI = 10; // 0xa + field public static final int DIVES_AKURU = 190; // 0xbe field public static final int DOGRA = 178; // 0xb2 field public static final int DUPLOYAN = 135; // 0x87 field public static final int EASTERN_SYRIAC = 97; // 0x61 @@ -19444,6 +19502,7 @@ package android.icu.lang { field public static final int KATAKANA_OR_HIRAGANA = 54; // 0x36 field public static final int KAYAH_LI = 79; // 0x4f field public static final int KHAROSHTHI = 57; // 0x39 + field public static final int KHITAN_SMALL_SCRIPT = 191; // 0xbf field public static final int KHMER = 23; // 0x17 field public static final int KHOJKI = 157; // 0x9d field public static final int KHUDAWADI = 145; // 0x91 @@ -19561,6 +19620,7 @@ package android.icu.lang { field public static final int WARANG_CITI = 146; // 0x92 field public static final int WESTERN_SYRIAC = 96; // 0x60 field public static final int WOLEAI = 155; // 0x9b + field public static final int YEZIDI = 192; // 0xc0 field public static final int YI = 41; // 0x29 field public static final int ZANABAZAR_SQUARE = 177; // 0xb1 } @@ -24260,6 +24320,9 @@ package android.media { method public int write(@NonNull float[], int, int, int); method public int write(@NonNull java.nio.ByteBuffer, int, int); method public int write(@NonNull java.nio.ByteBuffer, int, int, long); + field public static final int ENCAPSULATION_MODE_ELEMENTARY_STREAM = 1; // 0x1 + field public static final int ENCAPSULATION_MODE_HANDLE = 2; // 0x2 + field public static final int ENCAPSULATION_MODE_NONE = 0; // 0x0 field public static final int ERROR = -1; // 0xffffffff field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa @@ -24286,10 +24349,12 @@ package android.media { method @NonNull public android.media.AudioTrack.Builder setAudioAttributes(@NonNull android.media.AudioAttributes) throws java.lang.IllegalArgumentException; method @NonNull public android.media.AudioTrack.Builder setAudioFormat(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException; method @NonNull public android.media.AudioTrack.Builder setBufferSizeInBytes(@IntRange(from=0) int) throws java.lang.IllegalArgumentException; + method @NonNull public android.media.AudioTrack.Builder setEncapsulationMode(int); method @NonNull public android.media.AudioTrack.Builder setOffloadedPlayback(boolean); method @NonNull public android.media.AudioTrack.Builder setPerformanceMode(int); method @NonNull public android.media.AudioTrack.Builder setSessionId(@IntRange(from=1) int) throws java.lang.IllegalArgumentException; method @NonNull public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException; + method @NonNull public android.media.AudioTrack.Builder setTunerConfiguration(@NonNull android.media.AudioTrack.TunerConfiguration); } public static final class AudioTrack.MetricsConstants { @@ -24317,6 +24382,18 @@ package android.media { method public void onTearDown(@NonNull android.media.AudioTrack); } + public static class AudioTrack.TunerConfiguration { + method public int getContentId(); + method public int getSyncId(); + } + + public static class AudioTrack.TunerConfiguration.Builder { + ctor public AudioTrack.TunerConfiguration.Builder(); + method @NonNull public android.media.AudioTrack.TunerConfiguration build(); + method @NonNull public android.media.AudioTrack.TunerConfiguration.Builder setContentId(@IntRange(from=1) int); + method @NonNull public android.media.AudioTrack.TunerConfiguration.Builder setSyncId(@IntRange(from=1) int); + } + public class CamcorderProfile { method public static android.media.CamcorderProfile get(int); method public static android.media.CamcorderProfile get(int, int); @@ -24324,28 +24401,39 @@ package android.media { method public static boolean hasProfile(int, int); field public static final int QUALITY_1080P = 6; // 0x6 field public static final int QUALITY_2160P = 8; // 0x8 + field public static final int QUALITY_2K = 12; // 0xc field public static final int QUALITY_480P = 4; // 0x4 + field public static final int QUALITY_4KDCI = 10; // 0xa field public static final int QUALITY_720P = 5; // 0x5 field public static final int QUALITY_CIF = 3; // 0x3 field public static final int QUALITY_HIGH = 1; // 0x1 field public static final int QUALITY_HIGH_SPEED_1080P = 2004; // 0x7d4 field public static final int QUALITY_HIGH_SPEED_2160P = 2005; // 0x7d5 field public static final int QUALITY_HIGH_SPEED_480P = 2002; // 0x7d2 + field public static final int QUALITY_HIGH_SPEED_4KDCI = 2008; // 0x7d8 field public static final int QUALITY_HIGH_SPEED_720P = 2003; // 0x7d3 + field public static final int QUALITY_HIGH_SPEED_CIF = 2006; // 0x7d6 field public static final int QUALITY_HIGH_SPEED_HIGH = 2001; // 0x7d1 field public static final int QUALITY_HIGH_SPEED_LOW = 2000; // 0x7d0 + field public static final int QUALITY_HIGH_SPEED_VGA = 2007; // 0x7d7 field public static final int QUALITY_LOW = 0; // 0x0 field public static final int QUALITY_QCIF = 2; // 0x2 + field public static final int QUALITY_QHD = 11; // 0xb field public static final int QUALITY_QVGA = 7; // 0x7 field public static final int QUALITY_TIME_LAPSE_1080P = 1006; // 0x3ee field public static final int QUALITY_TIME_LAPSE_2160P = 1008; // 0x3f0 + field public static final int QUALITY_TIME_LAPSE_2K = 1012; // 0x3f4 field public static final int QUALITY_TIME_LAPSE_480P = 1004; // 0x3ec + field public static final int QUALITY_TIME_LAPSE_4KDCI = 1010; // 0x3f2 field public static final int QUALITY_TIME_LAPSE_720P = 1005; // 0x3ed field public static final int QUALITY_TIME_LAPSE_CIF = 1003; // 0x3eb field public static final int QUALITY_TIME_LAPSE_HIGH = 1001; // 0x3e9 field public static final int QUALITY_TIME_LAPSE_LOW = 1000; // 0x3e8 field public static final int QUALITY_TIME_LAPSE_QCIF = 1002; // 0x3ea + field public static final int QUALITY_TIME_LAPSE_QHD = 1011; // 0x3f3 field public static final int QUALITY_TIME_LAPSE_QVGA = 1007; // 0x3ef + field public static final int QUALITY_TIME_LAPSE_VGA = 1009; // 0x3f1 + field public static final int QUALITY_VGA = 9; // 0x9 field public int audioBitRate; field public int audioChannels; field public int audioCodec; @@ -30181,6 +30269,7 @@ package android.net.sip { method public void close(); method public void continueCall(int) throws android.net.sip.SipException; method public void endCall() throws android.net.sip.SipException; + method @Nullable public android.net.rtp.AudioGroup getAudioGroup(); method public android.net.sip.SipProfile getLocalProfile(); method public android.net.sip.SipProfile getPeerProfile(); method public int getState(); @@ -30191,6 +30280,7 @@ package android.net.sip { method public void makeCall(android.net.sip.SipProfile, android.net.sip.SipSession, int) throws android.net.sip.SipException; method public void sendDtmf(int); method public void sendDtmf(int, android.os.Message); + method public void setAudioGroup(@NonNull android.net.rtp.AudioGroup); method public void setListener(android.net.sip.SipAudioCall.Listener); method public void setListener(android.net.sip.SipAudioCall.Listener, boolean); method public void setSpeakerMode(boolean); @@ -30239,6 +30329,7 @@ package android.net.sip { method public void close(String) throws android.net.sip.SipException; method public android.net.sip.SipSession createSipSession(android.net.sip.SipProfile, android.net.sip.SipSession.Listener) throws android.net.sip.SipException; method public static String getCallId(android.content.Intent); + method @NonNull public java.util.List<android.net.sip.SipProfile> getListOfProfiles() throws android.net.sip.SipException; method public static String getOfferSessionDescription(android.content.Intent); method public android.net.sip.SipSession getSessionFor(android.content.Intent) throws android.net.sip.SipException; method public static boolean isApiSupported(android.content.Context); @@ -30256,6 +30347,11 @@ package android.net.sip { method public void setRegistrationListener(String, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException; method public android.net.sip.SipAudioCall takeAudioCall(android.content.Intent, android.net.sip.SipAudioCall.Listener) throws android.net.sip.SipException; method public void unregister(android.net.sip.SipProfile, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException; + field public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED"; + field public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL"; + field public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE"; + field public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP"; + field public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP"; field public static final String EXTRA_CALL_ID = "android:sipCallID"; field public static final String EXTRA_OFFER_SD = "android:sipOfferSD"; field public static final int INCOMING_CALL_RESULT_CODE = 101; // 0x65 @@ -30265,6 +30361,7 @@ package android.net.sip { method public int describeContents(); method public String getAuthUserName(); method public boolean getAutoRegistration(); + method public int getCallingUid(); method public String getDisplayName(); method public String getPassword(); method public int getPort(); @@ -30275,6 +30372,7 @@ package android.net.sip { method public String getSipDomain(); method public String getUriString(); method public String getUserName(); + method public void setCallingUid(int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.net.sip.SipProfile> CREATOR; } @@ -30607,6 +30705,8 @@ package android.net.wifi { method public int getIpAddress(); method public int getLinkSpeed(); method public String getMacAddress(); + method public int getMaxSupportedRxLinkSpeedMbps(); + method public int getMaxSupportedTxLinkSpeedMbps(); method public int getNetworkId(); method @Nullable public String getPasspointFqdn(); method @Nullable public String getPasspointProviderFriendlyName(); @@ -36226,6 +36326,7 @@ package android.os { method public boolean isUserUnlocked(); method public boolean isUserUnlocked(android.os.UserHandle); method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle); + method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle, int); method @Deprecated public boolean setRestrictionsChallenge(String); method @Deprecated public void setUserRestriction(String, boolean); method @Deprecated public void setUserRestrictions(android.os.Bundle); @@ -36287,6 +36388,7 @@ package android.os { field public static final String DISALLOW_USER_SWITCH = "no_user_switch"; field public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps"; field public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending"; + field public static final int QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED = 1; // 0x1 field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1 field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2 field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4 @@ -39750,8 +39852,8 @@ package android.provider { field public static final String EXTRA_INPUT_METHOD_ID = "input_method_id"; field public static final String EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME = "android.provider.extra.NOTIFICATION_LISTENER_COMPONENT_NAME"; field public static final String EXTRA_SUB_ID = "android.provider.extra.SUB_ID"; - field public static final String EXTRA_WIFI_CONFIGURATION_LIST = "android.provider.extra.WIFI_CONFIGURATION_LIST"; - field public static final String EXTRA_WIFI_CONFIGURATION_RESULT_LIST = "android.provider.extra.WIFI_CONFIGURATION_RESULT_LIST"; + field public static final String EXTRA_WIFI_NETWORK_LIST = "android.provider.extra.WIFI_NETWORK_LIST"; + field public static final String EXTRA_WIFI_NETWORK_RESULT_LIST = "android.provider.extra.WIFI_NETWORK_RESULT_LIST"; field public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = "android.intent.category.USAGE_ACCESS_CONFIG"; field public static final String METADATA_USAGE_ACCESS_REASON = "android.settings.metadata.USAGE_ACCESS_REASON"; } @@ -41620,6 +41722,7 @@ package android.se.omapi { public final class SEService { ctor public SEService(@NonNull android.content.Context, @NonNull java.util.concurrent.Executor, @NonNull android.se.omapi.SEService.OnConnectedListener); method @NonNull public android.se.omapi.Reader[] getReaders(); + method @NonNull public android.se.omapi.Reader getUiccReader(int); method @NonNull public String getVersion(); method public boolean isConnected(); method public void shutdown(); @@ -45017,6 +45120,8 @@ package android.telecom { package android.telephony { public final class AccessNetworkConstants { + field public static final int TRANSPORT_TYPE_WLAN = 2; // 0x2 + field public static final int TRANSPORT_TYPE_WWAN = 1; // 0x1 } public static final class AccessNetworkConstants.AccessNetworkType { @@ -45365,6 +45470,7 @@ package android.telephony { field public static final String KEY_MMS_ALIAS_MIN_CHARS_INT = "aliasMinChars"; field public static final String KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL = "allowAttachAudio"; field public static final String KEY_MMS_APPEND_TRANSACTION_ID_BOOL = "enabledTransID"; + field public static final String KEY_MMS_CLOSE_CONNECTION_BOOL = "mmsCloseConnection"; field public static final String KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING = "emailGatewayNumber"; field public static final String KEY_MMS_GROUP_MMS_ENABLED_BOOL = "enableGroupMms"; field public static final String KEY_MMS_HTTP_PARAMS_STRING = "httpParams"; @@ -45562,7 +45668,8 @@ package android.telephony { method public int getCellConnectionStatus(); method @NonNull public abstract android.telephony.CellIdentity getCellIdentity(); method @NonNull public abstract android.telephony.CellSignalStrength getCellSignalStrength(); - method public long getTimeStamp(); + method @Deprecated public long getTimeStamp(); + method public long getTimestampNanos(); method public boolean isRegistered(); field public static final int CONNECTION_NONE = 0; // 0x0 field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1 @@ -45802,6 +45909,35 @@ package android.telephony { field @Deprecated public static final int UNKNOWN_RSSI = 99; // 0x63 } + public final class NetworkRegistrationInfo implements android.os.Parcelable { + method public int describeContents(); + method public int getAccessNetworkTechnology(); + method @NonNull public java.util.List<java.lang.Integer> getAvailableServices(); + method @Nullable public android.telephony.CellIdentity getCellIdentity(); + method public int getDomain(); + method public int getNrState(); + method public int getTransportType(); + method public boolean isRegistered(); + method public boolean isRoaming(); + method public boolean isSearching(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationInfo> CREATOR; + field public static final int DOMAIN_CS = 1; // 0x1 + field public static final int DOMAIN_CS_PS = 3; // 0x3 + field public static final int DOMAIN_PS = 2; // 0x2 + field public static final int DOMAIN_UNKNOWN = 0; // 0x0 + field public static final int NR_STATE_CONNECTED = 3; // 0x3 + field public static final int NR_STATE_NONE = 0; // 0x0 + field public static final int NR_STATE_NOT_RESTRICTED = 2; // 0x2 + field public static final int NR_STATE_RESTRICTED = 1; // 0x1 + field public static final int SERVICE_TYPE_DATA = 2; // 0x2 + field public static final int SERVICE_TYPE_EMERGENCY = 5; // 0x5 + field public static final int SERVICE_TYPE_SMS = 3; // 0x3 + field public static final int SERVICE_TYPE_UNKNOWN = 0; // 0x0 + field public static final int SERVICE_TYPE_VIDEO = 4; // 0x4 + field public static final int SERVICE_TYPE_VOICE = 1; // 0x1 + } + public class NetworkScan { method public void stopScan(); field public static final int ERROR_INTERRUPTED = 10002; // 0x2712 @@ -45976,6 +46112,7 @@ package android.telephony { method public int getChannelNumber(); method public int getDuplexMode(); method public boolean getIsManualSelection(); + method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoList(); method public String getOperatorAlphaLong(); method public String getOperatorAlphaShort(); method public String getOperatorNumeric(); @@ -46012,6 +46149,7 @@ package android.telephony { method @Deprecated public int getGsmBitErrorRate(); method @Deprecated public int getGsmSignalStrength(); method public int getLevel(); + method public long getTimestampNanos(); method @Deprecated public boolean isGsm(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalStrength> CREATOR; @@ -46034,7 +46172,9 @@ package android.telephony { method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent); method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>); + method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, long); method public void sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent); + method public void sendTextMessage(@NonNull String, @Nullable String, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent, long); method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.SEND_SMS}) public void sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSmscAddress(@NonNull String); field public static final String EXTRA_MMS_DATA = "android.telephony.extra.MMS_DATA"; @@ -46406,6 +46546,7 @@ package android.telephony { method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String); method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String); method public boolean isConcurrentVoiceAndDataSupported(); + method public boolean isDataCapable(); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled(); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled(); method public boolean isEmergencyNumber(@NonNull String); @@ -55697,7 +55838,7 @@ package android.view.textclassifier { method public int describeContents(); method @NonNull public android.os.Bundle getExtras(); method @NonNull public java.util.Collection<android.view.textclassifier.TextLinks.TextLink> getLinks(); - method @NonNull public String getText(); + method @NonNull public CharSequence getText(); method public void writeToParcel(android.os.Parcel, int); field public static final int APPLY_STRATEGY_IGNORE = 0; // 0x0 field public static final int APPLY_STRATEGY_REPLACE = 1; // 0x1 @@ -71884,6 +72025,18 @@ package java.util { method public int lastIndexOf(@Nullable Object); method @NonNull public java.util.ListIterator<E> listIterator(); method @NonNull public java.util.ListIterator<E> listIterator(int); + method @NonNull public static <E> java.util.List<E> of(); + method @NonNull public static <E> java.util.List<E> of(@NonNull E); + method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E); + method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull @java.lang.SafeVarargs public static <E> java.util.List<E> of(@NonNull E...); method public E remove(int); method public default void replaceAll(@NonNull java.util.function.UnaryOperator<E>); method public E set(int, E); @@ -72038,6 +72191,7 @@ package java.util { method @Nullable public default V computeIfPresent(K, @NonNull java.util.function.BiFunction<? super K,? super V,? extends V>); method public boolean containsKey(@Nullable Object); method public boolean containsValue(@Nullable Object); + method @NonNull public static <K, V> java.util.Map.Entry<K,V> entry(@NonNull K, @NonNull V); method @NonNull public java.util.Set<java.util.Map.Entry<K,V>> entrySet(); method public boolean equals(@Nullable Object); method public default void forEach(@NonNull java.util.function.BiConsumer<? super K,? super V>); @@ -72047,6 +72201,18 @@ package java.util { method public boolean isEmpty(); method @NonNull public java.util.Set<K> keySet(); method @Nullable public default V merge(K, @NonNull V, @NonNull java.util.function.BiFunction<? super V,? super V,? extends V>); + method @NonNull public static <K, V> java.util.Map<K,V> of(); + method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V); + method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V); + method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V); + method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V); + method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V); + method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V); + method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V); + method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V); + method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V); + method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V); + method @NonNull @java.lang.SafeVarargs public static <K, V> java.util.Map<K,V> ofEntries(@NonNull java.util.Map.Entry<? extends K,? extends V>...); method @Nullable public V put(K, V); method public void putAll(@NonNull java.util.Map<? extends K,? extends V>); method @Nullable public default V putIfAbsent(K, V); @@ -72128,6 +72294,9 @@ package java.util { } public final class Objects { + method public static int checkFromIndexSize(int, int, int); + method public static int checkFromToIndex(int, int, int); + method public static int checkIndex(int, int); method public static <T> int compare(T, T, @NonNull java.util.Comparator<? super T>); method public static boolean deepEquals(@Nullable Object, @Nullable Object); method public static boolean equals(@Nullable Object, @Nullable Object); @@ -72138,6 +72307,8 @@ package java.util { method @NonNull public static <T> T requireNonNull(@Nullable T); method @NonNull public static <T> T requireNonNull(@Nullable T, @NonNull String); method @NonNull public static <T> T requireNonNull(@Nullable T, @NonNull java.util.function.Supplier<java.lang.String>); + method @NonNull public static <T> T requireNonNullElse(@Nullable T, @NonNull T); + method @NonNull public static <T> T requireNonNullElseGet(@Nullable T, @NonNull java.util.function.Supplier<? extends T>); method @NonNull public static String toString(@Nullable Object); method @NonNull public static String toString(@Nullable Object, @NonNull String); } @@ -72442,6 +72613,18 @@ package java.util { } public interface Set<E> extends java.util.Collection<E> { + method @NonNull public static <E> java.util.Set<E> of(); + method @NonNull public static <E> java.util.Set<E> of(@NonNull E); + method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E); + method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E); + method @NonNull @java.lang.SafeVarargs public static <E> java.util.Set<E> of(@NonNull E...); } public class SimpleTimeZone extends java.util.TimeZone { @@ -73380,6 +73563,29 @@ package java.util.concurrent { method public static java.util.concurrent.ScheduledExecutorService unconfigurableScheduledExecutorService(java.util.concurrent.ScheduledExecutorService); } + public final class Flow { + method public static int defaultBufferSize(); + } + + public static interface Flow.Processor<T, R> extends java.util.concurrent.Flow.Subscriber<T> java.util.concurrent.Flow.Publisher<R> { + } + + @java.lang.FunctionalInterface public static interface Flow.Publisher<T> { + method public void subscribe(java.util.concurrent.Flow.Subscriber<? super T>); + } + + public static interface Flow.Subscriber<T> { + method public void onComplete(); + method public void onError(Throwable); + method public void onNext(T); + method public void onSubscribe(java.util.concurrent.Flow.Subscription); + } + + public static interface Flow.Subscription { + method public void cancel(); + method public void request(long); + } + public class ForkJoinPool extends java.util.concurrent.AbstractExecutorService { ctor public ForkJoinPool(); ctor public ForkJoinPool(int); diff --git a/api/lint-baseline.txt b/api/lint-baseline.txt index 508718e10bd9..a3318c7e6745 100644 --- a/api/lint-baseline.txt +++ b/api/lint-baseline.txt @@ -509,6 +509,10 @@ MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#EGYPTIAN_HIEROGLYPH MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#ELYMAIC: +MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#KHITAN_SMALL_SCRIPT: + +MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#LISU_SUPPLEMENT: + MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#NANDINAGARI: MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#NYIAKENG_PUACHUE_HMONG: @@ -519,22 +523,28 @@ MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#SMALL_KANA_EXTENSIO MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A: +MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#SYMBOLS_FOR_LEGACY_COMPUTING: + MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#TAMIL_SUPPLEMENT: +MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#TANGUT_SUPPLEMENT: + MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#WANCHO: +MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#YEZIDI: + MissingNullability: android.icu.text.DateTimePatternGenerator#getFieldDisplayName(int, android.icu.text.DateTimePatternGenerator.DisplayWidth): MissingNullability: android.icu.text.DateTimePatternGenerator#getFieldDisplayName(int, android.icu.text.DateTimePatternGenerator.DisplayWidth) parameter #1: MissingNullability: android.icu.util.MeasureUnit#ATMOSPHERE: - Missing nullability on field `ATMOSPHERE` in class `class android.icu.util.MeasureUnit` + MissingNullability: android.icu.util.MeasureUnit#PERCENT: - Missing nullability on field `PERCENT` in class `class android.icu.util.MeasureUnit` + MissingNullability: android.icu.util.MeasureUnit#PERMILLE: - Missing nullability on field `PERMILLE` in class `class android.icu.util.MeasureUnit` + MissingNullability: android.icu.util.MeasureUnit#PETABYTE: - Missing nullability on field `PETABYTE` in class `class android.icu.util.MeasureUnit` + MissingNullability: android.icu.util.VersionInfo#UNICODE_12_0: MissingNullability: android.icu.util.VersionInfo#UNICODE_12_1: diff --git a/api/module-app-current.txt b/api/module-app-current.txt index d802177e249b..db774ef8ea2e 100644 --- a/api/module-app-current.txt +++ b/api/module-app-current.txt @@ -1 +1,17 @@ // Signature format: 2.0 +package android.app { + + public final class NotificationChannel implements android.os.Parcelable { + method public void setBlockableSystem(boolean); + } + +} + +package android.provider { + + public static final class Settings.Global extends android.provider.Settings.NameValueTable { + field public static final String COMMON_CRITERIA_MODE = "common_criteria_mode"; + } + +} + diff --git a/api/system-current.txt b/api/system-current.txt index e532a3afab8a..9c53f806aa7a 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -326,6 +326,7 @@ package android.app { method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String); method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales(); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.CREATE_USERS"}) public boolean isProfileForeground(@NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.KILL_UID) public void killUid(int, String); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener); method public void setDeviceLocales(@NonNull android.os.LocaleList); @@ -661,6 +662,7 @@ package android.app { method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] getRegisteredExperimentIds() throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException; + method public void registerPullAtomCallback(int, @Nullable android.app.StatsManager.PullAtomMetadata, @NonNull java.util.concurrent.Executor, @NonNull android.app.StatsManager.StatsPullAtomCallback); method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException; method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long); method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException; @@ -668,6 +670,7 @@ package android.app { method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent); method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent); method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException; + method public void unregisterPullAtomCallback(int); field public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED"; field public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = "android.app.extra.STATS_ACTIVE_CONFIG_KEYS"; field public static final String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES"; @@ -676,6 +679,23 @@ package android.app { field public static final String EXTRA_STATS_DIMENSIONS_VALUE = "android.app.extra.STATS_DIMENSIONS_VALUE"; field public static final String EXTRA_STATS_SUBSCRIPTION_ID = "android.app.extra.STATS_SUBSCRIPTION_ID"; field public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID = "android.app.extra.STATS_SUBSCRIPTION_RULE_ID"; + field public static final int PULL_SKIP = 1; // 0x1 + field public static final int PULL_SUCCESS = 0; // 0x0 + } + + public static class StatsManager.PullAtomMetadata { + } + + public static class StatsManager.PullAtomMetadata.Builder { + ctor public StatsManager.PullAtomMetadata.Builder(); + method @NonNull public android.app.StatsManager.PullAtomMetadata build(); + method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setAdditiveFields(@NonNull int[]); + method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setCoolDownNs(long); + method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setTimeoutNs(long); + } + + public static interface StatsManager.StatsPullAtomCallback { + method public int onPullAtom(int, @NonNull java.util.List<android.util.StatsEvent>); } public static class StatsManager.StatsUnavailableException extends android.util.AndroidException { @@ -1800,6 +1820,7 @@ package android.content { field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME"; field public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME"; field public static final String EXTRA_REASON = "android.intent.extra.REASON"; + field @Deprecated public static final String EXTRA_REBROADCAST_ON_UNLOCK = "rebroadcastOnUnlock"; field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK"; field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED"; field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME"; @@ -4631,9 +4652,19 @@ package android.media.tv { package android.media.tv.tuner { - public abstract class FrontendSettings { - method public final int getFrequency(); - method public abstract int getType(); + public class DemuxCapabilities { + method public int getAudioFilterCount(); + method public int getDemuxCount(); + method public int getFilterCapabilities(); + method @Nullable @Size(5) public int[] getLinkCapabilities(); + method public int getPcrFilterCount(); + method public int getPesFilterCount(); + method public int getPlaybackCount(); + method public int getRecordCount(); + method public int getSectionFilterCount(); + method public long getSectionFilterLength(); + method public int getTsFilterCount(); + method public int getVideoFilterCount(); } public class Lnb implements java.lang.AutoCloseable { @@ -4660,8 +4691,11 @@ package android.media.tv.tuner { public final class Tuner implements java.lang.AutoCloseable { ctor public Tuner(@NonNull android.content.Context); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearOnTuneEventListener(); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Tuner.Descrambler openDescrambler(); - method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.FrontendSettings); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopTune(); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings); } public class Tuner.Descrambler { @@ -4742,6 +4776,71 @@ package android.media.tv.tuner.filter { } +package android.media.tv.tuner.frontend { + + public class AnalogFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder builder(@NonNull android.content.Context); + method public int getSifStandard(); + method public int getSignalType(); + method public int getType(); + field public static final int SIF_BG = 1; // 0x1 + field public static final int SIF_BG_A2 = 2; // 0x2 + field public static final int SIF_BG_NICAM = 4; // 0x4 + field public static final int SIF_DK = 16; // 0x10 + field public static final int SIF_DK1 = 32; // 0x20 + field public static final int SIF_DK2 = 64; // 0x40 + field public static final int SIF_DK3 = 128; // 0x80 + field public static final int SIF_DK_NICAM = 256; // 0x100 + field public static final int SIF_I = 8; // 0x8 + field public static final int SIF_I_NICAM = 16384; // 0x4000 + field public static final int SIF_L = 512; // 0x200 + field public static final int SIF_L_NICAM = 32768; // 0x8000 + field public static final int SIF_L_PRIME = 65536; // 0x10000 + field public static final int SIF_M = 1024; // 0x400 + field public static final int SIF_M_A2 = 4096; // 0x1000 + field public static final int SIF_M_BTSC = 2048; // 0x800 + field public static final int SIF_M_EIA_J = 8192; // 0x2000 + field public static final int SIF_UNDEFINED = 0; // 0x0 + field public static final int SIGNAL_TYPE_NTSC = 4; // 0x4 + field public static final int SIGNAL_TYPE_PAL = 1; // 0x1 + field public static final int SIGNAL_TYPE_SECAM = 2; // 0x2 + field public static final int SIGNAL_TYPE_UNDEFINED = 0; // 0x0 + } + + public static class AnalogFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder> { + method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setASignalType(int); + method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int); + } + + public abstract class FrontendSettings { + method public int getFrequency(); + method public abstract int getType(); + field public static final int TYPE_ANALOG = 1; // 0x1 + field public static final int TYPE_ATSC = 2; // 0x2 + field public static final int TYPE_ATSC3 = 3; // 0x3 + field public static final int TYPE_DVBC = 4; // 0x4 + field public static final int TYPE_DVBS = 5; // 0x5 + field public static final int TYPE_DVBT = 6; // 0x6 + field public static final int TYPE_ISDBS = 7; // 0x7 + field public static final int TYPE_ISDBS3 = 8; // 0x8 + field public static final int TYPE_ISDBT = 9; // 0x9 + field public static final int TYPE_UNDEFINED = 0; // 0x0 + } + + public abstract static class FrontendSettings.Builder<T extends android.media.tv.tuner.frontend.FrontendSettings.Builder<T>> { + method @IntRange(from=1) @NonNull public T setFrequency(int); + } + + public interface OnTuneEventListener { + method public void onTuneEvent(int); + field public static final int SIGNAL_LOCKED = 0; // 0x0 + field public static final int SIGNAL_LOST_LOCK = 2; // 0x2 + field public static final int SIGNAL_NO_SIGNAL = 1; // 0x1 + } + +} + package android.metrics { public class LogMaker { @@ -4974,6 +5073,7 @@ package android.net { } public abstract class NetworkAgent { + ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider); method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData); method public void onAutomaticReconnectDisabled(); method public void onBandwidthUpdateRequested(); @@ -4996,6 +5096,8 @@ package android.net { public final class NetworkAgentConfig implements android.os.Parcelable { method public int describeContents(); + method public int getLegacyType(); + method @NonNull public String getLegacyTypeName(); method @Nullable public String getSubscriberId(); method public boolean isNat64DetectionEnabled(); method public boolean isProvisioningNotificationEnabled(); @@ -5008,6 +5110,8 @@ package android.net { method @NonNull public android.net.NetworkAgentConfig build(); method @NonNull public android.net.NetworkAgentConfig.Builder disableNat64Detection(); method @NonNull public android.net.NetworkAgentConfig.Builder disableProvisioningNotification(); + method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int); + method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String); method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String); } @@ -5325,9 +5429,11 @@ package android.net.ipsec.ike { } public abstract class ChildSessionParams { + method public long getHardLifetime(); method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getLocalTrafficSelectors(); method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getRemoteTrafficSelectors(); method @NonNull public java.util.List<android.net.ipsec.ike.ChildSaProposal> getSaProposals(); + method public long getSoftLifetime(); } public class IkeFqdnIdentification extends android.net.ipsec.ike.IkeIdentification { @@ -5396,12 +5502,14 @@ package android.net.ipsec.ike { } public final class IkeSessionParams { + method public long getHardLifetime(); method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getLocalAuthConfig(); method @NonNull public android.net.ipsec.ike.IkeIdentification getLocalIdentification(); method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getRemoteAuthConfig(); method @NonNull public android.net.ipsec.ike.IkeIdentification getRemoteIdentification(); method @NonNull public java.util.List<android.net.ipsec.ike.IkeSaProposal> getSaProposals(); method @NonNull public java.net.InetAddress getServerAddress(); + method public long getSoftLifetime(); method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket getUdpEncapsulationSocket(); } @@ -5413,6 +5521,7 @@ package android.net.ipsec.ike { method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey); method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthEap(@Nullable java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig); method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthPsk(@NonNull byte[]); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLifetime(long, long); method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLocalIdentification(@NonNull android.net.ipsec.ike.IkeIdentification); method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setRemoteIdentification(@NonNull android.net.ipsec.ike.IkeIdentification); method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setServerAddress(@NonNull java.net.InetAddress); @@ -5483,6 +5592,7 @@ package android.net.ipsec.ike { method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal); method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams build(); + method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder setLifetime(long, long); } public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams { @@ -5500,6 +5610,7 @@ package android.net.ipsec.ike { method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector); method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal); method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams build(); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder setLifetime(long, long); } public static interface TunnelModeChildSessionParams.ConfigRequest { @@ -6205,6 +6316,7 @@ package android.net.wifi { method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinPasspoint(@NonNull String, boolean); + method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void clearWifiConnectedNetworkScorer(); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener); @@ -6249,6 +6361,7 @@ package android.net.wifi { method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration); method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); + method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback); @@ -6338,6 +6451,11 @@ package android.net.wifi { method public void onWifiUsabilityStats(int, boolean, @NonNull android.net.wifi.WifiUsabilityStatsEntry); } + public static interface WifiManager.ScoreChangeCallback { + method public void onStatusChange(int, boolean); + method public void onTriggerUpdateOfWifiUsabilityStats(int); + } + public static interface WifiManager.SoftApCallback { method public default void onBlockedClientConnecting(@NonNull android.net.wifi.WifiClient, int); method public default void onCapabilityChanged(@NonNull android.net.wifi.SoftApCapability); @@ -6354,6 +6472,12 @@ package android.net.wifi { field public static final int DATA_ACTIVITY_OUT = 2; // 0x2 } + public static interface WifiManager.WifiConnectedNetworkScorer { + method public void setScoreChangeCallback(@NonNull android.net.wifi.WifiManager.ScoreChangeCallback); + method public void start(int); + method public void stop(int); + } + public class WifiNetworkConnectionStatistics implements android.os.Parcelable { ctor public WifiNetworkConnectionStatistics(int, int); ctor public WifiNetworkConnectionStatistics(); @@ -6730,6 +6854,15 @@ package android.net.wifi.rtt { package android.net.wifi.wificond { + public final class DeviceWiphyCapabilities implements android.os.Parcelable { + ctor public DeviceWiphyCapabilities(); + method public int describeContents(); + method public boolean isWifiStandardSupported(int); + method public void setWifiStandardSupport(int, boolean); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.DeviceWiphyCapabilities> CREATOR; + } + public final class NativeScanResult implements android.os.Parcelable { method public int describeContents(); method @NonNull public byte[] getBssid(); @@ -6794,6 +6927,7 @@ package android.net.wifi.wificond { method public void abortScan(@NonNull String); method public void enableVerboseLogging(boolean); method @NonNull public int[] getChannelsMhzForBand(int); + method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String); method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int); method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String); method public boolean initialize(@NonNull Runnable); @@ -9486,8 +9620,6 @@ package android.telephony { public final class AccessNetworkConstants { field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff - field public static final int TRANSPORT_TYPE_WLAN = 2; // 0x2 - field public static final int TRANSPORT_TYPE_WWAN = 1; // 0x1 } public final class BarringInfo implements android.os.Parcelable { @@ -10132,37 +10264,18 @@ package android.telephony { } public final class NetworkRegistrationInfo implements android.os.Parcelable { - method public int describeContents(); - method public int getAccessNetworkTechnology(); - method @NonNull public java.util.List<java.lang.Integer> getAvailableServices(); - method @Nullable public android.telephony.CellIdentity getCellIdentity(); method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo(); - method public int getDomain(); - method public int getNrState(); method public int getRegistrationState(); method public int getRejectCause(); method public int getRoamingType(); - method public int getTransportType(); method public boolean isEmergencyEnabled(); - method public boolean isRoaming(); method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationInfo> CREATOR; - field public static final int DOMAIN_CS = 1; // 0x1 - field public static final int DOMAIN_CS_PS = 3; // 0x3 - field public static final int DOMAIN_PS = 2; // 0x2 - field public static final int DOMAIN_UNKNOWN = 0; // 0x0 field public static final int REGISTRATION_STATE_DENIED = 3; // 0x3 field public static final int REGISTRATION_STATE_HOME = 1; // 0x1 field public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; // 0x0 field public static final int REGISTRATION_STATE_NOT_REGISTERED_SEARCHING = 2; // 0x2 field public static final int REGISTRATION_STATE_ROAMING = 5; // 0x5 field public static final int REGISTRATION_STATE_UNKNOWN = 4; // 0x4 - field public static final int SERVICE_TYPE_DATA = 2; // 0x2 - field public static final int SERVICE_TYPE_EMERGENCY = 5; // 0x5 - field public static final int SERVICE_TYPE_SMS = 3; // 0x3 - field public static final int SERVICE_TYPE_UNKNOWN = 0; // 0x0 - field public static final int SERVICE_TYPE_VIDEO = 4; // 0x4 - field public static final int SERVICE_TYPE_VOICE = 1; // 0x1 } public static final class NetworkRegistrationInfo.Builder { @@ -10380,7 +10493,6 @@ package android.telephony { method public int getDataRegistrationState(); method public boolean getDataRoamingFromRegistration(); method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int); - method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoList(); method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int); method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int); method public int getNrFrequencyRange(); @@ -10526,7 +10638,7 @@ package android.telephony { method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public java.util.List<android.telephony.SmsMessage> getMessagesFromIcc(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getPremiumSmsConsent(@NonNull String); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSmsCapacityOnIcc(); - method public void sendMultipartTextMessage(@NonNull String, @NonNull String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String); + method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPremiumSmsConsent(@NonNull String, int); field public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3; // 0x3 @@ -10677,10 +10789,12 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int); method public boolean isCurrentSimOperator(@NonNull String, int, @Nullable String); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataAllowedInVoiceCall(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionEnabled(); method public boolean isDataConnectivityPossible(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isGlobalModeEnabled(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isManualNetworkSelectionAllowed(); @@ -10712,6 +10826,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaRoamingMode(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaSubscriptionMode(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setDataAllowedDuringVoiceCall(boolean); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean); @@ -10750,6 +10865,9 @@ package android.telephony { field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED"; field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED"; field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED"; + field public static final int CARD_POWER_DOWN = 0; // 0x0 + field public static final int CARD_POWER_UP = 1; // 0x1 + field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2 field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1 field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0 @@ -10902,6 +11020,23 @@ package android.telephony.cdma { package android.telephony.data { + public class ApnSetting implements android.os.Parcelable { + method @NonNull public static String getApnTypesStringFromBitmask(int); + field public static final String TYPE_ALL_STRING = "*"; + field public static final String TYPE_CBS_STRING = "cbs"; + field public static final String TYPE_DEFAULT_STRING = "default"; + field public static final String TYPE_DUN_STRING = "dun"; + field public static final String TYPE_EMERGENCY_STRING = "emergency"; + field public static final String TYPE_FOTA_STRING = "fota"; + field public static final String TYPE_HIPRI_STRING = "hipri"; + field public static final String TYPE_IA_STRING = "ia"; + field public static final String TYPE_IMS_STRING = "ims"; + field public static final String TYPE_MCX_STRING = "mcx"; + field public static final String TYPE_MMS_STRING = "mms"; + field public static final String TYPE_SUPL_STRING = "supl"; + field public static final String TYPE_XCAP_STRING = "xcap"; + } + public final class DataCallResponse implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.List<android.net.LinkAddress> getAddresses(); @@ -11214,6 +11349,7 @@ package android.telephony.ims { method public int getEmergencyServiceCategories(); method @NonNull public java.util.List<java.lang.String> getEmergencyUrns(); method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile(); + method @Nullable public android.os.Bundle getProprietaryCallExtras(); method public int getRestrictCause(); method public int getServiceType(); method public static int getVideoStateFromCallType(int); @@ -11716,6 +11852,9 @@ package android.telephony.ims { method public boolean isCapable(int); method public boolean isCapable(@NonNull String); method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000 + field public static final int CAPABILITY_CHAT_BOT = 67108864; // 0x4000000 + field public static final int CAPABILITY_CHAT_BOT_ROLE = 134217728; // 0x8000000 field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2 field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4 field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1 @@ -11732,9 +11871,13 @@ package android.telephony.ims { field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100 field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000 field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000 + field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000 + field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000 field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000 field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000 field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000 + field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000 + field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000 field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800 field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400 field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200 @@ -11790,6 +11933,7 @@ package android.telephony.ims.feature { public abstract class ImsFeature { ctor public ImsFeature(); method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); + method public int getFeatureState(); method public final int getSlotIndex(); method public abstract void onFeatureReady(); method public abstract void onFeatureRemoved(); @@ -12037,6 +12181,17 @@ package android.telephony.ims.stub { method public int updateClir(int); method public int updateColp(boolean); method public int updateColr(int); + field public static final int CALL_BARRING_ALL = 7; // 0x7 + field public static final int CALL_BARRING_ALL_INCOMING = 1; // 0x1 + field public static final int CALL_BARRING_ALL_OUTGOING = 2; // 0x2 + field public static final int CALL_BARRING_ANONYMOUS_INCOMING = 6; // 0x6 + field public static final int CALL_BARRING_INCOMING_ALL_SERVICES = 9; // 0x9 + field public static final int CALL_BARRING_OUTGOING_ALL_SERVICES = 8; // 0x8 + field public static final int CALL_BARRING_OUTGOING_INTL = 3; // 0x3 + field public static final int CALL_BARRING_OUTGOING_INTL_EXCL_HOME = 4; // 0x4 + field public static final int CALL_BARRING_SPECIFIC_INCOMING_CALLS = 10; // 0xa + field public static final int CALL_BLOCKING_INCOMING_WHEN_ROAMING = 5; // 0x5 + field public static final int INVALID_RESULT = -1; // 0xffffffff } } diff --git a/api/test-current.txt b/api/test-current.txt index 28119e3c120b..190f9fe8b69a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3141,8 +3141,6 @@ package android.telephony { public final class AccessNetworkConstants { field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff - field public static final int TRANSPORT_TYPE_WLAN = 2; // 0x2 - field public static final int TRANSPORT_TYPE_WWAN = 1; // 0x1 } public final class BarringInfo implements android.os.Parcelable { @@ -3218,36 +3216,18 @@ package android.telephony { } public final class NetworkRegistrationInfo implements android.os.Parcelable { - method public int describeContents(); - method public int getAccessNetworkTechnology(); - method @NonNull public java.util.List<java.lang.Integer> getAvailableServices(); - method @Nullable public android.telephony.CellIdentity getCellIdentity(); method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo(); - method public int getDomain(); method public int getRegistrationState(); method public int getRejectCause(); method public int getRoamingType(); - method public int getTransportType(); method public boolean isEmergencyEnabled(); - method public boolean isRoaming(); method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationInfo> CREATOR; - field public static final int DOMAIN_CS = 1; // 0x1 - field public static final int DOMAIN_CS_PS = 3; // 0x3 - field public static final int DOMAIN_PS = 2; // 0x2 - field public static final int DOMAIN_UNKNOWN = 0; // 0x0 field public static final int REGISTRATION_STATE_DENIED = 3; // 0x3 field public static final int REGISTRATION_STATE_HOME = 1; // 0x1 field public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; // 0x0 field public static final int REGISTRATION_STATE_NOT_REGISTERED_SEARCHING = 2; // 0x2 field public static final int REGISTRATION_STATE_ROAMING = 5; // 0x5 field public static final int REGISTRATION_STATE_UNKNOWN = 4; // 0x4 - field public static final int SERVICE_TYPE_DATA = 2; // 0x2 - field public static final int SERVICE_TYPE_EMERGENCY = 5; // 0x5 - field public static final int SERVICE_TYPE_SMS = 3; // 0x3 - field public static final int SERVICE_TYPE_UNKNOWN = 0; // 0x0 - field public static final int SERVICE_TYPE_VIDEO = 4; // 0x4 - field public static final int SERVICE_TYPE_VOICE = 1; // 0x1 } public static final class NetworkRegistrationInfo.Builder { @@ -3296,7 +3276,7 @@ package android.telephony { public final class SmsManager { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int checkSmsShortCodeDestination(String, String); - method public void sendMultipartTextMessage(@NonNull String, @NonNull String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String); + method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String); field public static final int SMS_CATEGORY_FREE_SHORT_CODE = 1; // 0x1 field public static final int SMS_CATEGORY_NOT_SHORT_CODE = 0; // 0x0 field public static final int SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3; // 0x3 @@ -3393,6 +3373,7 @@ package android.telephony.ims { method public int getEmergencyServiceCategories(); method @NonNull public java.util.List<java.lang.String> getEmergencyUrns(); method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile(); + method @Nullable public android.os.Bundle getProprietaryCallExtras(); method public int getRestrictCause(); method public int getServiceType(); method public static int getVideoStateFromCallType(int); @@ -3446,6 +3427,7 @@ package android.telephony.ims { field public static final String EXTRA_DISPLAY_TEXT = "DisplayText"; field public static final String EXTRA_EMERGENCY_CALL = "e_call"; field public static final String EXTRA_IS_CALL_PULL = "CallPull"; + field public static final String EXTRA_OEM_EXTRAS = "android.telephony.ims.extra.OEM_EXTRAS"; field public static final String EXTRA_OI = "oi"; field public static final String EXTRA_OIR = "oir"; field public static final String EXTRA_REMOTE_URI = "remote_uri"; @@ -3924,6 +3906,7 @@ package android.telephony.ims.feature { public abstract class ImsFeature { ctor public ImsFeature(); method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); + method public int getFeatureState(); method public final int getSlotIndex(); method public abstract void onFeatureReady(); method public abstract void onFeatureRemoved(); @@ -4171,6 +4154,17 @@ package android.telephony.ims.stub { method public int updateClir(int); method public int updateColp(boolean); method public int updateColr(int); + field public static final int CALL_BARRING_ALL = 7; // 0x7 + field public static final int CALL_BARRING_ALL_INCOMING = 1; // 0x1 + field public static final int CALL_BARRING_ALL_OUTGOING = 2; // 0x2 + field public static final int CALL_BARRING_ANONYMOUS_INCOMING = 6; // 0x6 + field public static final int CALL_BARRING_INCOMING_ALL_SERVICES = 9; // 0x9 + field public static final int CALL_BARRING_OUTGOING_ALL_SERVICES = 8; // 0x8 + field public static final int CALL_BARRING_OUTGOING_INTL = 3; // 0x3 + field public static final int CALL_BARRING_OUTGOING_INTL_EXCL_HOME = 4; // 0x4 + field public static final int CALL_BARRING_SPECIFIC_INCOMING_CALLS = 10; // 0xa + field public static final int CALL_BLOCKING_INCOMING_WHEN_ROAMING = 5; // 0x5 + field public static final int INVALID_RESULT = -1; // 0xffffffff } } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 8fac31a05c49..2537edaf9e81 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -100,6 +100,7 @@ static constexpr size_t FONT_NUM_ROWS = FONT_NUM_CHARS / FONT_NUM_COLS; static const int TEXT_CENTER_VALUE = INT_MAX; static const int TEXT_MISSING_VALUE = INT_MIN; static const char EXIT_PROP_NAME[] = "service.bootanim.exit"; +static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays"; static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1; static constexpr size_t TEXT_POS_LEN_MAX = 16; @@ -291,10 +292,10 @@ status_t BootAnimation::readyToRun() { // this guest property specifies multi-display IDs to show the boot animation // multiple ids can be set with comma (,) as separator, for example: - // setprop boot.animation.displays 19260422155234049,19261083906282754 + // setprop persist.boot.animation.displays 19260422155234049,19261083906282754 Vector<uint64_t> physicalDisplayIds; char displayValue[PROPERTY_VALUE_MAX] = ""; - property_get("boot.animation.displays", displayValue, ""); + property_get(DISPLAYS_PROP_NAME, displayValue, ""); bool isValid = displayValue[0] != '\0'; if (isValid) { char *p = displayValue; @@ -306,7 +307,7 @@ status_t BootAnimation::readyToRun() { p ++; } if (!isValid) - SLOGE("Invalid syntax for the value of system prop: boot.animation.displays"); + SLOGE("Invalid syntax for the value of system prop: %s", DISPLAYS_PROP_NAME); } if (isValid) { std::istringstream stream(displayValue); diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 1c6867c39790..6eafbd8bb3f1 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -73,7 +73,6 @@ cc_defaults { "src/external/puller_util.cpp", "src/external/ResourceHealthManagerPuller.cpp", "src/external/StatsCallbackPuller.cpp", - "src/external/StatsCallbackPullerDeprecated.cpp", "src/external/StatsCompanionServicePuller.cpp", "src/external/StatsPuller.cpp", "src/external/StatsPullerManager.cpp", diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index ada2f2de28f4..c1a8d69191d2 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1265,16 +1265,6 @@ Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) { return Status::ok(); } -Status StatsService::registerPullerCallback(int32_t atomTag, - const sp<android::os::IStatsPullerCallback>& pullerCallback, - const String16& packageName) { - ENFORCE_DUMP_AND_USAGE_STATS(packageName); - - VLOG("StatsService::registerPullerCallback called."); - mPullerManager->RegisterPullerCallback(atomTag, pullerCallback); - return Status::ok(); -} - Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs, int64_t timeoutNs, const std::vector<int32_t>& additiveFields, const sp<android::os::IPullAtomCallback>& pullerCallback) { @@ -1297,14 +1287,6 @@ Status StatsService::registerNativePullAtomCallback(int32_t atomTag, int64_t coo return Status::ok(); } -Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& packageName) { - ENFORCE_DUMP_AND_USAGE_STATS(packageName); - - VLOG("StatsService::unregisterPullerCallback called."); - mPullerManager->UnregisterPullerCallback(atomTag); - return Status::ok(); -} - Status StatsService::unregisterPullAtomCallback(int32_t uid, int32_t atomTag) { ENFORCE_UID(AID_SYSTEM); VLOG("StatsService::unregisterPullAtomCallback called."); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 7990e5e7d018..f2079d9f278f 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -171,14 +171,6 @@ public: virtual Status sendAppBreadcrumbAtom(int32_t label, int32_t state) override; /** - * Binder call to register a callback function for a vendor pulled atom. - * Note: this atom must NOT have uid as a field. - */ - virtual Status registerPullerCallback(int32_t atomTag, - const sp<android::os::IStatsPullerCallback>& pullerCallback, - const String16& packageName) override; - - /** * Binder call to register a callback function for a pulled atom. */ virtual Status registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs, @@ -193,11 +185,6 @@ public: const sp<android::os::IPullAtomCallback>& pullerCallback) override; /** - * Binder call to unregister any existing callback function for a vendor pulled atom. - */ - virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override; - - /** * Binder call to unregister any existing callback for the given uid and atom. */ virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override; diff --git a/cmds/statsd/src/external/StatsCallbackPullerDeprecated.cpp b/cmds/statsd/src/external/StatsCallbackPullerDeprecated.cpp deleted file mode 100644 index 4f88a91f9ec6..000000000000 --- a/cmds/statsd/src/external/StatsCallbackPullerDeprecated.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false // STOPSHIP if true -#include "Log.h" - -#include "StatsCallbackPullerDeprecated.h" - -#include <android/os/IStatsPullerCallback.h> - -#include "logd/LogEvent.h" -#include "stats_log_util.h" - -using namespace android::binder; - -namespace android { -namespace os { -namespace statsd { - -StatsCallbackPullerDeprecated::StatsCallbackPullerDeprecated( - int tagId, const sp<IStatsPullerCallback>& callback) - : StatsPuller(tagId), mCallback(callback) { - VLOG("StatsCallbackPuller created for tag %d", tagId); -} - -bool StatsCallbackPullerDeprecated::PullInternal(vector<shared_ptr<LogEvent>>* data) { - VLOG("StatsCallbackPuller called for tag %d", mTagId) - if (mCallback == nullptr) { - ALOGW("No callback registered"); - return false; - } - int64_t wallClockTimeNs = getWallClockNs(); - int64_t elapsedTimeNs = getElapsedRealtimeNs(); - vector<StatsLogEventWrapper> returned_value; - Status status = mCallback->pullData(mTagId, elapsedTimeNs, wallClockTimeNs, &returned_value); - if (!status.isOk()) { - ALOGW("StatsCallbackPuller::pull failed for %d", mTagId); - return false; - } - data->clear(); - for (const StatsLogEventWrapper& it : returned_value) { - LogEvent::createLogEvents(it, *data); - } - VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId); - return true; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsCallbackPullerDeprecated.h b/cmds/statsd/src/external/StatsCallbackPullerDeprecated.h deleted file mode 100644 index 028902975923..000000000000 --- a/cmds/statsd/src/external/StatsCallbackPullerDeprecated.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <android/os/IStatsPullerCallback.h> -#include <utils/String16.h> - -#include "StatsPuller.h" - -namespace android { -namespace os { -namespace statsd { - -class StatsCallbackPullerDeprecated : public StatsPuller { -public: - explicit StatsCallbackPullerDeprecated(int tagId, const sp<IStatsPullerCallback>& callback); - -private: - bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override; - const sp<IStatsPullerCallback> mCallback; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 591d7277b299..b4fde1666b17 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -21,7 +21,6 @@ #include <android/os/IPullAtomCallback.h> #include <android/os/IStatsCompanionService.h> -#include <android/os/IStatsPullerCallback.h> #include <cutils/log.h> #include <math.h> #include <stdint.h> @@ -38,7 +37,6 @@ #include "PowerStatsPuller.h" #include "ResourceHealthManagerPuller.h" #include "StatsCallbackPuller.h" -#include "StatsCallbackPullerDeprecated.h" #include "StatsCompanionServicePuller.h" #include "SubsystemSleepStatePuller.h" #include "TrainInfoPuller.h" @@ -68,13 +66,6 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {{.atomTag = android::util::ON_DEVICE_POWER_MEASUREMENT}, {.puller = new PowerStatsPuller()}}, - // system_elapsed_realtime - {{.atomTag = android::util::SYSTEM_ELAPSED_REALTIME}, - {.coolDownNs = NS_PER_SEC, - .puller = new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME), - .pullTimeoutNs = NS_PER_SEC / 2, - }}, - // remaining_battery_capacity {{.atomTag = android::util::REMAINING_BATTERY_CAPACITY}, {.puller = new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}}, @@ -95,74 +86,6 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {{.atomTag = android::util::BATTERY_CYCLE_COUNT}, {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}}, - // process_memory_state - {{.atomTag = android::util::PROCESS_MEMORY_STATE}, - {.additiveFields = {4, 5, 6, 7, 8}, - .puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}}, - - // process_memory_high_water_mark - {{.atomTag = android::util::PROCESS_MEMORY_HIGH_WATER_MARK}, - {.puller = - new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}}, - - // process_memory_snapshot - {{.atomTag = android::util::PROCESS_MEMORY_SNAPSHOT}, - {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_SNAPSHOT)}}, - - // system_ion_heap_size - {{.atomTag = android::util::SYSTEM_ION_HEAP_SIZE}, - {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}}, - - // process_system_ion_heap_size - {{.atomTag = android::util::PROCESS_SYSTEM_ION_HEAP_SIZE}, - {.puller = new StatsCompanionServicePuller(android::util::PROCESS_SYSTEM_ION_HEAP_SIZE)}}, - - // temperature - {{.atomTag = android::util::TEMPERATURE}, - {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}}, - - // cooling_device - {{.atomTag = android::util::COOLING_DEVICE}, - {.puller = new StatsCompanionServicePuller(android::util::COOLING_DEVICE)}}, - - // binder_calls - {{.atomTag = android::util::BINDER_CALLS}, - {.additiveFields = {4, 5, 6, 8, 12}, - .puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS)}}, - - // binder_calls_exceptions - {{.atomTag = android::util::BINDER_CALLS_EXCEPTIONS}, - {.puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}}, - - // looper_stats - {{.atomTag = android::util::LOOPER_STATS}, - {.additiveFields = {5, 6, 7, 8, 9}, - .puller = new StatsCompanionServicePuller(android::util::LOOPER_STATS)}}, - - // Disk Stats - {{.atomTag = android::util::DISK_STATS}, - {.puller = new StatsCompanionServicePuller(android::util::DISK_STATS)}}, - - // Directory usage - {{.atomTag = android::util::DIRECTORY_USAGE}, - {.puller = new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}}, - - // Size of app's code, data, and cache - {{.atomTag = android::util::APP_SIZE}, - {.puller = new StatsCompanionServicePuller(android::util::APP_SIZE)}}, - - // Size of specific categories of files. Eg. Music. - {{.atomTag = android::util::CATEGORY_SIZE}, - {.puller = new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}}, - - // Number of fingerprints enrolled for each user. - {{.atomTag = android::util::NUM_FINGERPRINTS_ENROLLED}, - {.puller = new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS_ENROLLED)}}, - - // Number of faces enrolled for each user. - {{.atomTag = android::util::NUM_FACES_ENROLLED}, - {.puller = new StatsCompanionServicePuller(android::util::NUM_FACES_ENROLLED)}}, - // ProcStats. {{.atomTag = android::util::PROC_STATS}, {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS)}}, @@ -171,20 +94,6 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {{.atomTag = android::util::PROC_STATS_PKG_PROC}, {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}}, - // Disk I/O stats per uid. - {{.atomTag = android::util::DISK_IO}, - {.additiveFields = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, - .coolDownNs = 3 * NS_PER_SEC, - .puller = new StatsCompanionServicePuller(android::util::DISK_IO)}}, - - // Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses. - {{.atomTag = android::util::PROCESS_CPU_TIME}, - {.coolDownNs = 5 * NS_PER_SEC /* min cool-down in seconds*/, - .puller = new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}}, - {{.atomTag = android::util::CPU_TIME_PER_THREAD_FREQ}, - {.additiveFields = {7, 9, 11, 13, 15, 17, 19, 21}, - .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}}, - // DebugElapsedClock. {{.atomTag = android::util::DEBUG_ELAPSED_CLOCK}, {.additiveFields = {1, 2, 3, 4}, @@ -195,25 +104,9 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {.additiveFields = {1, 2, 3, 4}, .puller = new StatsCompanionServicePuller(android::util::DEBUG_FAILING_ELAPSED_CLOCK)}}, - // RoleHolder. - {{.atomTag = android::util::ROLE_HOLDER}, - {.puller = new StatsCompanionServicePuller(android::util::ROLE_HOLDER)}}, - - // PermissionState. - {{.atomTag = android::util::DANGEROUS_PERMISSION_STATE}, - {.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}}, - // TrainInfo. {{.atomTag = android::util::TRAIN_INFO}, {.puller = new TrainInfoPuller()}}, - // TimeZoneDataInfo. - {{.atomTag = android::util::TIME_ZONE_DATA_INFO}, - {.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}}, - - // ExternalStorageInfo - {{.atomTag = android::util::EXTERNAL_STORAGE_INFO}, - {.puller = new StatsCompanionServicePuller(android::util::EXTERNAL_STORAGE_INFO)}}, - // GpuStatsGlobalInfo {{.atomTag = android::util::GPU_STATS_GLOBAL_INFO}, {.puller = new GpuStatsPuller(android::util::GPU_STATS_GLOBAL_INFO)}}, @@ -222,31 +115,14 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {{.atomTag = android::util::GPU_STATS_APP_INFO}, {.puller = new GpuStatsPuller(android::util::GPU_STATS_APP_INFO)}}, - // AppsOnExternalStorageInfo - {{.atomTag = android::util::APPS_ON_EXTERNAL_STORAGE_INFO}, - {.puller = new StatsCompanionServicePuller(android::util::APPS_ON_EXTERNAL_STORAGE_INFO)}}, - // Face Settings {{.atomTag = android::util::FACE_SETTINGS}, {.puller = new StatsCompanionServicePuller(android::util::FACE_SETTINGS)}}, - // App ops - {{.atomTag = android::util::APP_OPS}, - {.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}}, - // VmsClientStats {{.atomTag = android::util::VMS_CLIENT_STATS}, {.additiveFields = {5, 6, 7, 8, 9, 10}, .puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}}, - - // NotiifcationRemoteViews. - {{.atomTag = android::util::NOTIFICATION_REMOTE_VIEWS}, - {.puller = new StatsCompanionServicePuller(android::util::NOTIFICATION_REMOTE_VIEWS)}}, - - // PermissionStateSampled. - {{.atomTag = android::util::DANGEROUS_PERMISSION_STATE_SAMPLED}, - {.puller = - new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE_SAMPLED)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { @@ -445,21 +321,6 @@ int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) { return totalCleared; } -// Deprecated, remove after puller API is complete. -void StatsPullerManager::RegisterPullerCallback(int32_t atomTag, - const sp<IStatsPullerCallback>& callback) { - AutoMutex _l(mLock); - // Platform pullers cannot be changed. - if (!isVendorPulledAtom(atomTag)) { - VLOG("RegisterPullerCallback: atom tag %d is not vendor pulled", atomTag); - return; - } - VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag); - StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true); - kAllPullAtomInfo[{.atomTag = atomTag}] = { - .puller = new StatsCallbackPullerDeprecated(atomTag, callback)}; -} - void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs, const int64_t timeoutNs, const vector<int32_t>& additiveFields, @@ -476,16 +337,6 @@ void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t a }; } -void StatsPullerManager::UnregisterPullerCallback(int32_t atomTag) { - AutoMutex _l(mLock); - // Platform pullers cannot be changed. - if (!isVendorPulledAtom(atomTag)) { - return; - } - StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false); - kAllPullAtomInfo.erase({.atomTag = atomTag}); -} - void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) { AutoMutex _l(mLock); StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false); diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h index 349fd47b6c9d..4b6ab57b3bbe 100644 --- a/cmds/statsd/src/external/StatsPullerManager.h +++ b/cmds/statsd/src/external/StatsPullerManager.h @@ -18,7 +18,6 @@ #include <android/os/IPullAtomCallback.h> #include <android/os/IStatsCompanionService.h> -#include <android/os/IStatsPullerCallback.h> #include <binder/IServiceManager.h> #include <utils/RefBase.h> #include <utils/threads.h> @@ -116,15 +115,10 @@ public: void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService); - // Deprecated, remove after puller API is complete. - void RegisterPullerCallback(int32_t atomTag, const sp<IStatsPullerCallback>& callback); - void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs, const int64_t timeoutNs, const vector<int32_t>& additiveFields, const sp<IPullAtomCallback>& callback); - void UnregisterPullerCallback(int32_t atomTag); - void UnregisterPullAtomCallback(const int uid, const int32_t atomTag); static std::map<PullerKey, PullAtomInfo> kAllPullAtomInfo; diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 692d91e1a82f..32827852ecad 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -164,9 +164,6 @@ public: // Maximum number of pushed atoms statsd stats will track above kMaxPushedAtomId. static const int kMaxNonPlatformPushedAtoms = 100; - // Max platform atom tag number. - static const int32_t kMaxPlatformAtomTag = 100000; - // Vendor pulled atom start id. static const int32_t kVendorPulledAtomStartTag = 150000; diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 0bd8ce692884..ee6ccc2f5cd7 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -48,6 +48,7 @@ import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityWindowInfo; import com.android.internal.os.HandlerCaller; @@ -56,6 +57,7 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -392,7 +394,8 @@ public abstract class AccessibilityService extends Service { private static final String LOG_TAG = "AccessibilityService"; /** - * Interface used by IAccessibilityServiceWrapper to call the service from its main thread. + * Interface used by IAccessibilityServiceClientWrapper to call the service from its main + * thread. * @hide */ public interface Callbacks { @@ -413,6 +416,8 @@ public abstract class AccessibilityService extends Service { /** Accessbility button clicked callbacks for different displays */ void onAccessibilityButtonClicked(int displayId); void onAccessibilityButtonAvailabilityChanged(boolean available); + /** This is called when the system action list is changed. */ + void onSystemActionsChanged(); } /** @@ -1643,6 +1648,29 @@ public abstract class AccessibilityService extends Service { available); } + /** This is called when the system action list is changed. */ + public void onSystemActionsChanged() { + } + + /** + * Returns a list of system actions available in the system right now. + * + * @return A list of available system actions. + */ + public final @NonNull List<AccessibilityAction> getSystemActions() { + IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + if (connection != null) { + try { + return connection.getSystemActions(); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while calling getSystemActions", re); + re.rethrowFromSystemServer(); + } + } + return Collections.emptyList(); + } + /** * Performs a global action. Such an action can be performed * at any moment regardless of the current application or user @@ -1894,6 +1922,11 @@ public abstract class AccessibilityService extends Service { public void onAccessibilityButtonAvailabilityChanged(boolean available) { AccessibilityService.this.onAccessibilityButtonAvailabilityChanged(available); } + + @Override + public void onSystemActionsChanged() { + AccessibilityService.this.onSystemActionsChanged(); + } }); } @@ -1918,6 +1951,7 @@ public abstract class AccessibilityService extends Service { private static final int DO_ON_FINGERPRINT_GESTURE = 11; private static final int DO_ACCESSIBILITY_BUTTON_CLICKED = 12; private static final int DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 13; + private static final int DO_ON_SYSTEM_ACTIONS_CHANGED = 14; private final HandlerCaller mCaller; @@ -2014,6 +2048,11 @@ public abstract class AccessibilityService extends Service { mCaller.sendMessage(message); } + /** This is called when the system action list is changed. */ + public void onSystemActionsChanged() { + mCaller.sendMessage(mCaller.obtainMessage(DO_ON_SYSTEM_ACTIONS_CHANGED)); + } + @Override public void executeMessage(Message message) { switch (message.what) { @@ -2035,14 +2074,14 @@ public abstract class AccessibilityService extends Service { /* ignore - best effort */ } } - } return; - + return; + } case DO_ON_INTERRUPT: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { mCallback.onInterrupt(); } - } return; - + return; + } case DO_INIT: { mConnectionId = message.arg1; SomeArgs args = (SomeArgs) message.obj; @@ -2062,18 +2101,18 @@ public abstract class AccessibilityService extends Service { AccessibilityInteractionClient.getInstance().clearCache(); mCallback.init(AccessibilityInteractionClient.NO_ID, null); } - } return; - + return; + } case DO_ON_GESTURE: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { mCallback.onGesture((AccessibilityGestureEvent) message.obj); } - } return; - + return; + } case DO_CLEAR_ACCESSIBILITY_CACHE: { AccessibilityInteractionClient.getInstance().clearCache(); - } return; - + return; + } case DO_ON_KEY_EVENT: { KeyEvent event = (KeyEvent) message.obj; try { @@ -2096,8 +2135,8 @@ public abstract class AccessibilityService extends Service { /* ignore - best effort */ } } - } return; - + return; + } case DO_ON_MAGNIFICATION_CHANGED: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { final SomeArgs args = (SomeArgs) message.obj; @@ -2110,45 +2149,53 @@ public abstract class AccessibilityService extends Service { mCallback.onMagnificationChanged(displayId, region, scale, centerX, centerY); } - } return; - + return; + } case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { final int showMode = (int) message.arg1; mCallback.onSoftKeyboardShowModeChanged(showMode); } - } return; - + return; + } case DO_GESTURE_COMPLETE: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { final boolean successfully = message.arg2 == 1; mCallback.onPerformGestureResult(message.arg1, successfully); } - } return; + return; + } case DO_ON_FINGERPRINT_ACTIVE_CHANGED: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1); } - } return; + return; + } case DO_ON_FINGERPRINT_GESTURE: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { mCallback.onFingerprintGesture(message.arg1); } - } return; - - case (DO_ACCESSIBILITY_BUTTON_CLICKED): { + return; + } + case DO_ACCESSIBILITY_BUTTON_CLICKED: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { mCallback.onAccessibilityButtonClicked(message.arg1); } - } return; - - case (DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED): { + return; + } + case DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { final boolean available = (message.arg1 != 0); mCallback.onAccessibilityButtonAvailabilityChanged(available); } - } return; - + return; + } + case DO_ON_SYSTEM_ACTIONS_CHANGED: { + if (mConnectionId != AccessibilityInteractionClient.NO_ID) { + mCallback.onSystemActionsChanged(); + } + return; + } default : Log.w(LOG_TAG, "Unknown message type " + message.what); } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index 8ec3aea2728f..58b0d19f605a 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -55,4 +55,6 @@ import android.view.KeyEvent; void onAccessibilityButtonClicked(int displayId); void onAccessibilityButtonAvailabilityChanged(boolean available); + + void onSystemActionsChanged(); } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 4ea5c62bf05b..5db4dd7470d8 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -25,8 +25,10 @@ import android.os.RemoteCallback; import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import android.view.accessibility.AccessibilityWindowInfo; +import java.util.List; /** * Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService. @@ -67,6 +69,7 @@ interface IAccessibilityServiceConnection { AccessibilityServiceInfo getServiceInfo(); boolean performGlobalAction(int action); + List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions(); void disableSelf(); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 2010cc98ef2d..e1bd6aeb6f48 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -65,6 +65,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; +import android.os.UserManager; import android.os.WorkSource; import android.util.ArrayMap; import android.util.DisplayMetrics; @@ -4463,6 +4464,29 @@ public class ActivityManager { } /** + * Return if a given profile is in the foreground. + * @param userHandle UserHandle to check + * @return Returns the boolean result. + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) + public boolean isProfileForeground(@NonNull UserHandle userHandle) { + UserManager userManager = mContext.getSystemService(UserManager.class); + if (userManager != null) { + for (UserInfo userInfo : userManager.getProfiles(getCurrentUser())) { + if (userInfo.id == userHandle.getIdentifier()) { + return true; + } + } + } + return false; + } + + /** * The AppTask allows you to manage your own application's tasks. * See {@link android.app.ActivityManager#getAppTasks()} */ diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index a11f41fbc5d0..bc7e1e591021 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -4342,6 +4342,9 @@ public class AppOpsManager { mHistoricalUidOps.removeAt(i); } else { uidOp.filter(packageName, featureId, opNames, filter, scaleFactor); + if (uidOp.getPackageCount() == 0) { + mHistoricalUidOps.removeAt(i); + } } } } @@ -4681,6 +4684,9 @@ public class AppOpsManager { mHistoricalPackageOps.removeAt(i); } else { packageOps.filter(featureId, opNames, filter, fractionToRemove); + if (packageOps.getFeatureCount() == 0) { + mHistoricalPackageOps.removeAt(i); + } } } } @@ -4930,6 +4936,9 @@ public class AppOpsManager { mHistoricalFeatureOps.removeAt(i); } else { featureOps.filter(opNames, filter, fractionToRemove); + if (featureOps.getOpCount() == 0) { + mHistoricalFeatureOps.removeAt(i); + } } } } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index d39a8c4699e8..326a0761dff3 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1675,6 +1675,10 @@ public class ApplicationPackageManager extends PackageManager { } @UnsupportedAppUsage + protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) { + this(context, pm, ActivityThread.getPermissionManager()); + } + protected ApplicationPackageManager(ContextImpl context, IPackageManager pm, IPermissionManager permissionManager) { mContext = context; diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 7af7a4adc748..f6014e5fdf80 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -95,7 +95,7 @@ interface INotificationManager void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel); NotificationChannel getNotificationChannel(String callingPkg, int userId, String pkg, String channelId); NotificationChannel getConversationNotificationChannel(String callingPkg, int userId, String pkg, String channelId, boolean returnParentIfNoConversationChannel, String conversationId); - void createConversationNotificationChannelForPackage(String pkg, int uid, in NotificationChannel parentChannel, String conversationId); + void createConversationNotificationChannelForPackage(String pkg, int uid, String triggeringKey, in NotificationChannel parentChannel, String conversationId); NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted); void deleteNotificationChannel(String pkg, String channelId); void deleteConversationNotificationChannels(String pkg, int uid, String conversationId); diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 78f9cc8eaa62..5a4622e0b245 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -15,6 +15,8 @@ */ package android.app; +import static android.annotation.SystemApi.Client.MODULE_APPS; + import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -354,9 +356,13 @@ public final class NotificationChannel implements Parcelable { } /** + * Allows users to block notifications sent through this channel, if this channel belongs to + * a package that is signed with the system signature. If the channel does not belong to a + * package that is signed with the system signature, this method does nothing. + * @param blockableSystem if {@code true}, allows users to block notifications on this channel. * @hide */ - @UnsupportedAppUsage + @SystemApi(client = MODULE_APPS) @TestApi public void setBlockableSystem(boolean blockableSystem) { mBlockableSystem = blockableSystem; diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index d23754e9d433..7ab85a4a7468 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -1208,8 +1208,7 @@ public class ResourcesManager { WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i); ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null; if (r != null) { - applyConfigurationToResourcesLocked(config, compat, tmpConfig, - defaultDisplayMetrics, key, r); + applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r); } else { mResourceImpls.removeAt(i); } @@ -1224,8 +1223,7 @@ public class ResourcesManager { } applyConfigurationToResourcesLocked(config, compat, tmpConfig, - defaultDisplayMetrics, resourcesWithLoaders.resourcesKey(), - resources.getImpl()); + resourcesWithLoaders.resourcesKey(), resources.getImpl()); } return changes != 0; @@ -1236,40 +1234,33 @@ public class ResourcesManager { private void applyConfigurationToResourcesLocked(@NonNull Configuration config, @Nullable CompatibilityInfo compat, Configuration tmpConfig, - DisplayMetrics defaultDisplayMetrics, ResourcesKey key, ResourcesImpl resourcesImpl) { + ResourcesKey key, ResourcesImpl resourcesImpl) { if (DEBUG || DEBUG_CONFIGURATION) { Slog.v(TAG, "Changing resources " + resourcesImpl + " config to: " + config); } int displayId = key.mDisplayId; - boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); - DisplayMetrics dm = defaultDisplayMetrics; final boolean hasOverrideConfiguration = key.hasOverrideConfiguration(); - if (!isDefaultDisplay || hasOverrideConfiguration) { - tmpConfig.setTo(config); - - // Get new DisplayMetrics based on the DisplayAdjustments given - // to the ResourcesImpl. Update a copy if the CompatibilityInfo - // changed, because the ResourcesImpl object will handle the - // update internally. - DisplayAdjustments daj = resourcesImpl.getDisplayAdjustments(); - if (compat != null) { - daj = new DisplayAdjustments(daj); - daj.setCompatibilityInfo(compat); - } - dm = getDisplayMetrics(displayId, daj); - - if (!isDefaultDisplay) { - applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig); - } + tmpConfig.setTo(config); + + // Get new DisplayMetrics based on the DisplayAdjustments given to the ResourcesImpl. Update + // a copy if the CompatibilityInfo changed, because the ResourcesImpl object will handle the + // update internally. + DisplayAdjustments daj = resourcesImpl.getDisplayAdjustments(); + if (compat != null) { + daj = new DisplayAdjustments(daj); + daj.setCompatibilityInfo(compat); + } + daj.setConfiguration(config); + DisplayMetrics dm = getDisplayMetrics(displayId, daj); + if (displayId != Display.DEFAULT_DISPLAY) { + applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig); + } - if (hasOverrideConfiguration) { - tmpConfig.updateFrom(key.mOverrideConfiguration); - } - resourcesImpl.updateConfiguration(tmpConfig, dm, compat); - } else { - resourcesImpl.updateConfiguration(config, dm, compat); + if (hasOverrideConfiguration) { + tmpConfig.updateFrom(key.mOverrideConfiguration); } + resourcesImpl.updateConfiguration(tmpConfig, dm, compat); } /** diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index dde6dda8e448..0ea05d8f683c 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -18,16 +18,16 @@ package android.app; import static android.Manifest.permission.DUMP; import static android.Manifest.permission.PACKAGE_USAGE_STATS; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; -import android.os.IBinder; +import android.os.Binder; import android.os.IPullAtomCallback; import android.os.IPullAtomResultReceiver; import android.os.IStatsManagerService; -import android.os.IStatsPullerCallback; import android.os.IStatsd; import android.os.RemoteException; import android.os.ServiceManager; @@ -108,13 +108,11 @@ public final class StatsManager { /** * Value indicating that this pull was successful and that the result should be used. * - * @hide **/ public static final int PULL_SUCCESS = 0; /** * Value indicating that this pull was unsuccessful and that the result should not be used. - * @hide **/ public static final int PULL_SKIP = 1; @@ -476,41 +474,16 @@ public final class StatsManager { } /** - * Registers a callback for an atom when that atom is to be pulled. The stats service will - * invoke pullData in the callback when the stats service determines that this atom needs to be - * pulled. Currently, this only works for atoms with tags above 100,000 that do not have a uid. - * - * @param atomTag The tag of the atom for this puller callback. Must be at least 100000. - * @param callback The callback to be invoked when the stats service pulls the atom. - * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service + * Temp registration for while the migration is in progress. * * @hide - * @deprecated Please use registerPullAtomCallback */ - @Deprecated - @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) - public void setPullerCallback(int atomTag, IStatsPullerCallback callback) - throws StatsUnavailableException { - synchronized (sLock) { - try { - IStatsd service = getIStatsdLocked(); - if (callback == null) { - service.unregisterPullerCallback(atomTag, mContext.getOpPackageName()); - } else { - service.registerPullerCallback(atomTag, callback, - mContext.getOpPackageName()); - } - - } catch (RemoteException e) { - Slog.e(TAG, "Failed to connect to statsd when registering data listener."); - throw new StatsUnavailableException("could not connect", e); - } catch (SecurityException e) { - throw new StatsUnavailableException(e.getMessage(), e); - } - } + public void registerPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata, + @NonNull StatsPullAtomCallback callback, + @NonNull @CallbackExecutor Executor executor) { + registerPullAtomCallback(atomTag, metadata, executor, callback); } - /** * Registers a callback for an atom when that atom is to be pulled. The stats service will * invoke pullData in the callback when the stats service determines that this atom needs to be @@ -520,18 +493,19 @@ public final class StatsManager { * @param metadata Optional metadata specifying the timeout, cool down time, and * additive fields for mapping isolated to host uids. * @param callback The callback to be invoked when the stats service pulls the atom. - * @param executor The executor in which to run the callback + * @param executor The executor in which to run the callback. * - * @hide */ public void registerPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata, - @NonNull StatsPullAtomCallback callback, @NonNull Executor executor) { + @NonNull @CallbackExecutor Executor executor, + @NonNull StatsPullAtomCallback callback) { long coolDownNs = metadata == null ? DEFAULT_COOL_DOWN_NS : metadata.mCoolDownNs; long timeoutNs = metadata == null ? DEFAULT_TIMEOUT_NS : metadata.mTimeoutNs; int[] additiveFields = metadata == null ? new int[0] : metadata.mAdditiveFields; if (additiveFields == null) { additiveFields = new int[0]; } + synchronized (sLock) { try { IStatsManagerService service = getIStatsManagerServiceLocked(); @@ -551,7 +525,6 @@ public final class StatsManager { * * @param atomTag The tag of the atom of which to unregister * - * @hide */ public void unregisterPullAtomCallback(int atomTag) { synchronized (sLock) { @@ -577,21 +550,26 @@ public final class StatsManager { @Override public void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver) { - mExecutor.execute(() -> { - List<StatsEvent> data = new ArrayList<>(); - int successInt = mCallback.onPullAtom(atomTag, data); - boolean success = successInt == PULL_SUCCESS; - StatsEventParcel[] parcels = new StatsEventParcel[data.size()]; - for (int i = 0; i < data.size(); i++) { - parcels[i] = new StatsEventParcel(); - parcels[i].buffer = data.get(i).getBytes(); - } - try { - resultReceiver.pullFinished(atomTag, success, parcels); - } catch (RemoteException e) { - Slog.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId); - } - }); + long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> { + List<StatsEvent> data = new ArrayList<>(); + int successInt = mCallback.onPullAtom(atomTag, data); + boolean success = successInt == PULL_SUCCESS; + StatsEventParcel[] parcels = new StatsEventParcel[data.size()]; + for (int i = 0; i < data.size(); i++) { + parcels[i] = new StatsEventParcel(); + parcels[i].buffer = data.get(i).getBytes(); + } + try { + resultReceiver.pullFinished(atomTag, success, parcels); + } catch (RemoteException e) { + Slog.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId); + } + }); + } finally { + Binder.restoreCallingIdentity(token); + } } } @@ -599,7 +577,6 @@ public final class StatsManager { * Metadata required for registering a StatsPullAtomCallback. * All fields are optional, and defaults will be used for fields that are unspecified. * - * @hide */ public static class PullAtomMetadata { private final long mCoolDownNs; @@ -614,22 +591,27 @@ public final class StatsManager { } /** - * Returns a new PullAtomMetadata.Builder object for constructing PullAtomMetadata for - * StatsManager#registerPullAtomCallback + * Temp for while migrations are in progress. + * + * @hide */ public static PullAtomMetadata.Builder newBuilder() { return new PullAtomMetadata.Builder(); } /** - * Builder for PullAtomMetadata. + * Builder for PullAtomMetadata. */ public static class Builder { private long mCoolDownNs; private long mTimeoutNs; private int[] mAdditiveFields; - private Builder() { + /** + * Returns a new PullAtomMetadata.Builder object for constructing PullAtomMetadata for + * StatsManager#registerPullAtomCallback + */ + public Builder() { mCoolDownNs = DEFAULT_COOL_DOWN_NS; mTimeoutNs = DEFAULT_TIMEOUT_NS; mAdditiveFields = null; @@ -662,7 +644,7 @@ public final class StatsManager { * will be combined when the non-additive fields are the same. */ @NonNull - public Builder setAdditiveFields(int[] additiveFields) { + public Builder setAdditiveFields(@NonNull int[] additiveFields) { mAdditiveFields = additiveFields; return this; } @@ -705,40 +687,13 @@ public final class StatsManager { /** * Callback interface for pulling atoms requested by the stats service. * - * @hide */ public interface StatsPullAtomCallback { /** * Pull data for the specified atom tag, filling in the provided list of StatsEvent data. * @return {@link #PULL_SUCCESS} if the pull was successful, or {@link #PULL_SKIP} if not. */ - int onPullAtom(int atomTag, List<StatsEvent> data); - } - - private class StatsdDeathRecipient implements IBinder.DeathRecipient { - @Override - public void binderDied() { - synchronized (sLock) { - mService = null; - } - } - } - - @GuardedBy("sLock") - private IStatsd getIStatsdLocked() throws StatsUnavailableException { - if (mService != null) { - return mService; - } - mService = IStatsd.Stub.asInterface(ServiceManager.getService("stats")); - if (mService == null) { - throw new StatsUnavailableException("could not be found"); - } - try { - mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0); - } catch (RemoteException e) { - throw new StatsUnavailableException("could not connect when linkToDeath", e); - } - return mService; + int onPullAtom(int atomTag, @NonNull List<StatsEvent> data); } @GuardedBy("sLock") diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 2579bd1abbfd..35cf68737ccc 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -1254,6 +1254,11 @@ public final class UiAutomation { } @Override + public void onSystemActionsChanged() { + /* do nothing */ + } + + @Override public boolean onGesture(AccessibilityGestureEvent gestureEvent) { /* do nothing */ return false; diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 9ad3d38a3084..a88500920e97 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -1564,6 +1564,8 @@ public class WallpaperManager { * @return The desired minimum width for the wallpaper. This value should * be honored by applications that set the wallpaper but it is not * mandatory. + * + * @see #getDesiredMinimumHeight() */ public int getDesiredMinimumWidth() { if (sGlobals.mService == null) { @@ -1590,6 +1592,8 @@ public class WallpaperManager { * @return The desired minimum height for the wallpaper. This value should * be honored by applications that set the wallpaper but it is not * mandatory. + * + * @see #getDesiredMinimumWidth() */ public int getDesiredMinimumHeight() { if (sGlobals.mService == null) { @@ -1609,12 +1613,11 @@ public class WallpaperManager { * a virtual wallpaper that is larger than the physical screen, matching * the size of their workspace. * - * <p>Note developers, who don't seem to be reading this. This is - * for <em>home apps</em> to tell what size wallpaper they would like. - * Nobody else should be calling this! Certainly not other non-home - * apps that change the wallpaper. Those apps are supposed to - * <b>retrieve</b> the suggested size so they can construct a wallpaper - * that matches it. + * <p class="note">Calling this method from apps other than the active + * home app is not guaranteed to work properly. Other apps that supply + * wallpaper imagery should use {@link #getDesiredMinimumWidth()} and + * {@link #getDesiredMinimumHeight()} and construct a wallpaper that + * matches those dimensions. * * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#SET_WALLPAPER_HINTS}. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index be8e1d60f290..54a64ef3f392 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -11493,4 +11493,48 @@ public class DevicePolicyManager { } return Collections.emptyList(); } + + /** + * Called by device owner or profile owner of an organization-owned managed profile to toggle + * Common Criteria mode for the device. When the device is in Common Criteria mode, + * certain device functionalities are tuned to meet the higher + * security level required by Common Criteria certification. For example: + * <ul> + * <li> Bluetooth long term key material is additionally integrity-protected with AES-GCM. </li> + * <li> WiFi configuration store is additionally integrity-protected with AES-GCM. </li> + * </ul> + * Common Criteria mode is disabled by default. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @param enabled whether Common Criteria mode should be enabled or not. + */ + public void setCommonCriteriaModeEnabled(@NonNull ComponentName admin, boolean enabled) { + throwIfParentInstance("setCommonCriteriaModeEnabled"); + if (mService != null) { + try { + mService.setCommonCriteriaModeEnabled(admin, enabled); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Called by device owner or profile owner of an organization-owned managed profile to return + * whether Common Criteria mode is currently enabled for the device. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @return {@code true} if Common Criteria mode is enabled, {@code false} otherwise. + */ + public boolean isCommonCriteriaModeEnabled(@NonNull ComponentName admin) { + throwIfParentInstance("isCommonCriteriaModeEnabled"); + if (mService != null) { + try { + return mService.isCommonCriteriaModeEnabled(admin); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 21c9eb5c60ad..f649286206bb 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -461,4 +461,7 @@ interface IDevicePolicyManager { void setProtectedPackages(in ComponentName admin, in List<String> packages); List<String> getProtectedPackages(in ComponentName admin); + + void setCommonCriteriaModeEnabled(in ComponentName admin, boolean enabled); + boolean isCommonCriteriaModeEnabled(in ComponentName admin); } diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java index 6df92a78cc9f..a34be5c3edc7 100644 --- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java +++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java @@ -75,9 +75,15 @@ public class TransactionExecutorHelper { mLifecycleSequence.clear(); if (finish >= start) { - // just go there - for (int i = start + 1; i <= finish; i++) { - mLifecycleSequence.add(i); + if (start == ON_START && finish == ON_STOP) { + // A case when we from start to stop state soon, we don't need to go + // through the resumed, paused state. + mLifecycleSequence.add(ON_STOP); + } else { + // just go there + for (int i = start + 1; i <= finish; i++) { + mLifecycleSequence.add(i); + } } } else { // finish < start, can't just cycle down if (start == ON_PAUSE && finish == ON_RESUME) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 679de8a281c3..3df94a7e233a 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -30,6 +30,7 @@ import android.annotation.StringDef; import android.annotation.StringRes; import android.annotation.StyleRes; import android.annotation.StyleableRes; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; @@ -3930,6 +3931,7 @@ public abstract class Context { */ @SystemApi @TestApi + @SuppressLint("ServiceName") public static final String STATUS_BAR_SERVICE = "statusbar"; /** @@ -4024,6 +4026,7 @@ public abstract class Context { public static final String NETWORK_STATS_SERVICE = "netstats"; /** {@hide} */ @SystemApi + @SuppressLint("ServiceName") public static final String NETWORK_POLICY_SERVICE = "netpolicy"; /** {@hide} */ public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist"; @@ -4048,6 +4051,7 @@ public abstract class Context { * @hide */ @SystemApi + @SuppressLint("ServiceName") public static final String WIFI_COND_SERVICE = "wificond"; /** @@ -4379,6 +4383,7 @@ public abstract class Context { * @see #getSystemService(String) */ @TestApi + @SuppressLint("ServiceName") // TODO: This should be renamed to CONTENT_CAPTURE_SERVICE public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture"; /** @@ -4474,6 +4479,7 @@ public abstract class Context { * @hide */ @TestApi + @SuppressLint("ServiceName") // TODO: This should be renamed to DEVICE_IDLE_SERVICE public static final String DEVICE_IDLE_CONTROLLER = "deviceidle"; /** @@ -4483,6 +4489,7 @@ public abstract class Context { * @hide */ @TestApi + @SuppressLint("ServiceName") // TODO: This should be renamed to POWER_WHITELIST_SERVICE public static final String POWER_WHITELIST_MANAGER = "power_whitelist"; /** @@ -5053,6 +5060,7 @@ public abstract class Context { * @hide */ @SystemApi + @SuppressLint("ServiceName") public static final String BATTERY_STATS_SERVICE = "batterystats"; /** diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index ee758024fb9f..c8c31028b9d2 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4021,6 +4021,7 @@ public class Intent implements Parcelable, Cloneable { * <p> * @see #EXTRA_SIM_STATE * @see #EXTRA_SIM_LOCKED_REASON + * @see #EXTRA_REBROADCAST_ON_UNLOCK * * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED} @@ -4197,6 +4198,18 @@ public class Intent implements Parcelable, Cloneable { public static final String SIM_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED"; /** + * The extra used with {@link #ACTION_SIM_STATE_CHANGED} for indicating whether this broadcast + * is a rebroadcast on unlock. Defaults to {@code false} if not specified. + * + * @hide + * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or + * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED} + */ + @Deprecated + @SystemApi + public static final String EXTRA_REBROADCAST_ON_UNLOCK = "rebroadcastOnUnlock"; + + /** * Broadcast Action: indicate that the phone service state has changed. * The intent will have the following extra values:</p> * <p> diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 9d5751480a80..5aa9c9bebc2c 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -19,6 +19,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.app.AppOpsManager.Mode; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -42,6 +43,18 @@ import java.util.Set; * use this class to start its main activity in managed profile. */ public class CrossProfileApps { + + /** + * Broadcast signalling that the receiving app's ability to interact across profiles has + * changed, as defined by the return value of {@link #canInteractAcrossProfiles()}. + * + * <p>Apps that have set the {@code android:crossProfile} manifest attribute to {@code true} + * can receive this broadcast in manifest broadcast receivers. Otherwise, it can only be + * received by dynamically-registered broadcast receivers. + */ + public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = + "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED"; + private final Context mContext; private final ICrossProfileApps mService; private final UserManager mUserManager; @@ -254,6 +267,38 @@ public class CrossProfileApps { return settingsIntent; } + /** + * Sets the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} that is + * configurable by users in Settings. This configures it for the profile group of the calling + * package. + * + * <p>Before calling, check {@link #canRequestInteractAcrossProfiles()} and do not call if it is + * {@code false}. If presenting a user interface, do not allow the user to configure the app-op + * in that case. + * + * <p>The underlying app-op {@link android.app.AppOpsManager#OP_INTERACT_ACROSS_PROFILES} should + * never be set directly. This method ensures that the app-op is kept in sync for the app across + * each user in the profile group and that those apps are sent a broadcast when their ability to + * interact across profiles changes. + * + * <p>This method should be used whenever an app's ability to interact across profiles changes, + * as defined by the return value of {@link #canInteractAcrossProfiles()}. This includes user + * consent changes in Settings or during provisioning, plus changes to the admin or OEM consent + * whitelists that make the current app-op value invalid. + * + * @hide + */ + @RequiresPermission( + allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, + android.Manifest.permission.INTERACT_ACROSS_USERS}) + public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) { + try { + mService.setInteractAcrossProfilesAppOp(packageName, newMode); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private void verifyCanAccessUser(UserHandle userHandle) { if (!getTargetUserProfiles().contains(userHandle)) { throw new SecurityException("Not allowed to access " + userHandle); diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl index c5db0ccebf52..694b1a3c4e73 100644 --- a/core/java/android/content/pm/ICrossProfileApps.aidl +++ b/core/java/android/content/pm/ICrossProfileApps.aidl @@ -32,4 +32,5 @@ interface ICrossProfileApps { List<UserHandle> getTargetUserProfiles(in String callingPackage); boolean canInteractAcrossProfiles(in String callingPackage); boolean canRequestInteractAcrossProfiles(in String callingPackage); + void setInteractAcrossProfilesAppOp(in String packageName, int newMode); }
\ No newline at end of file diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index f7222750b89b..796cfdce2c0d 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -39,6 +39,8 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Map; +import java.util.function.BinaryOperator; +import java.util.function.UnaryOperator; /** * Represents a SQLite database connection. @@ -123,8 +125,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen boolean enableTrace, boolean enableProfile, int lookasideSlotSize, int lookasideSlotCount); private static native void nativeClose(long connectionPtr); - private static native void nativeRegisterCustomFunction(long connectionPtr, - SQLiteCustomFunction function); + private static native void nativeRegisterCustomScalarFunction(long connectionPtr, + String name, UnaryOperator<String> function); + private static native void nativeRegisterCustomAggregateFunction(long connectionPtr, + String name, BinaryOperator<String> function); private static native void nativeRegisterLocalizedCollators(long connectionPtr, String locale); private static native long nativePrepareStatement(long connectionPtr, String sql); private static native void nativeFinalizeStatement(long connectionPtr, long statementPtr); @@ -225,13 +229,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen setJournalSizeLimit(); setAutoCheckpointInterval(); setLocaleFromConfiguration(); - - // Register custom functions. - final int functionCount = mConfiguration.customFunctions.size(); - for (int i = 0; i < functionCount; i++) { - SQLiteCustomFunction function = mConfiguration.customFunctions.get(i); - nativeRegisterCustomFunction(mConnectionPtr, function); - } + setCustomFunctionsFromConfiguration(); } private void dispose(boolean finalized) { @@ -457,6 +455,19 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } } + private void setCustomFunctionsFromConfiguration() { + for (int i = 0; i < mConfiguration.customScalarFunctions.size(); i++) { + nativeRegisterCustomScalarFunction(mConnectionPtr, + mConfiguration.customScalarFunctions.keyAt(i), + mConfiguration.customScalarFunctions.valueAt(i)); + } + for (int i = 0; i < mConfiguration.customAggregateFunctions.size(); i++) { + nativeRegisterCustomAggregateFunction(mConnectionPtr, + mConfiguration.customAggregateFunctions.keyAt(i), + mConfiguration.customAggregateFunctions.valueAt(i)); + } + } + private void checkDatabaseWiped() { if (!SQLiteGlobal.checkDbWipe()) { return; @@ -491,15 +502,6 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen void reconfigure(SQLiteDatabaseConfiguration configuration) { mOnlyAllowReadOnlyOperations = false; - // Register custom functions. - final int functionCount = configuration.customFunctions.size(); - for (int i = 0; i < functionCount; i++) { - SQLiteCustomFunction function = configuration.customFunctions.get(i); - if (!mConfiguration.customFunctions.contains(function)) { - nativeRegisterCustomFunction(mConnectionPtr, function); - } - } - // Remember what changed. boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled != mConfiguration.foreignKeyConstraintsEnabled; @@ -507,6 +509,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen & (SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING | SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL)) != 0; boolean localeChanged = !configuration.locale.equals(mConfiguration.locale); + boolean customScalarFunctionsChanged = !configuration.customScalarFunctions + .equals(mConfiguration.customScalarFunctions); + boolean customAggregateFunctionsChanged = !configuration.customAggregateFunctions + .equals(mConfiguration.customAggregateFunctions); // Update configuration parameters. mConfiguration.updateParametersFrom(configuration); @@ -514,20 +520,18 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen // Update prepared statement cache size. mPreparedStatementCache.resize(configuration.maxSqlCacheSize); - // Update foreign key mode. if (foreignKeyModeChanged) { setForeignKeyModeFromConfiguration(); } - - // Update WAL. if (walModeChanged) { setWalModeFromConfiguration(); } - - // Update locale. if (localeChanged) { setLocaleFromConfiguration(); } + if (customScalarFunctionsChanged || customAggregateFunctionsChanged) { + setCustomFunctionsFromConfiguration(); + } } // Called by SQLiteConnectionPool only. diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 44c78aa783a7..458914efcbbd 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -62,6 +62,8 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.WeakHashMap; +import java.util.function.BinaryOperator; +import java.util.function.UnaryOperator; /** * Exposes methods to manage a SQLite database. @@ -958,26 +960,87 @@ public final class SQLiteDatabase extends SQLiteClosable { } /** - * Registers a CustomFunction callback as a function that can be called from - * SQLite database triggers. - * - * @param name the name of the sqlite3 function - * @param numArgs the number of arguments for the function - * @param function callback to call when the function is executed - * @hide - */ - public void addCustomFunction(String name, int numArgs, CustomFunction function) { - // Create wrapper (also validates arguments). - SQLiteCustomFunction wrapper = new SQLiteCustomFunction(name, numArgs, function); + * Register a custom scalar function that can be called from SQL + * expressions. + * <p> + * For example, registering a custom scalar function named {@code REVERSE} + * could be used in a query like + * {@code SELECT REVERSE(name) FROM employees}. + * <p> + * When attempting to register multiple functions with the same function + * name, SQLite will replace any previously defined functions with the + * latest definition, regardless of what function type they are. SQLite does + * not support unregistering functions. + * + * @param functionName Case-insensitive name to register this function + * under, limited to 255 UTF-8 bytes in length. + * @param scalarFunction Functional interface that will be invoked when the + * function name is used by a SQL statement. The argument values + * from the SQL statement are passed to the functional interface, + * and the return values from the functional interface are + * returned back into the SQL statement. + * @throws SQLiteException if the custom function could not be registered. + * @see #setCustomAggregateFunction(String, BinaryOperator) + */ + public void setCustomScalarFunction(@NonNull String functionName, + @NonNull UnaryOperator<String> scalarFunction) throws SQLiteException { + Objects.requireNonNull(functionName); + Objects.requireNonNull(scalarFunction); + + synchronized (mLock) { + throwIfNotOpenLocked(); + + mConfigurationLocked.customScalarFunctions.put(functionName, scalarFunction); + try { + mConnectionPoolLocked.reconfigure(mConfigurationLocked); + } catch (RuntimeException ex) { + mConfigurationLocked.customScalarFunctions.remove(functionName); + throw ex; + } + } + } + + /** + * Register a custom aggregate function that can be called from SQL + * expressions. + * <p> + * For example, registering a custom aggregation function named + * {@code LONGEST} could be used in a query like + * {@code SELECT LONGEST(name) FROM employees}. + * <p> + * The implementation of this method follows the reduction flow outlined in + * {@link java.util.stream.Stream#reduce(BinaryOperator)}, and the custom + * aggregation function is expected to be an associative accumulation + * function, as defined by that class. + * <p> + * When attempting to register multiple functions with the same function + * name, SQLite will replace any previously defined functions with the + * latest definition, regardless of what function type they are. SQLite does + * not support unregistering functions. + * + * @param functionName Case-insensitive name to register this function + * under, limited to 255 UTF-8 bytes in length. + * @param aggregateFunction Functional interface that will be invoked when + * the function name is used by a SQL statement. The argument + * values from the SQL statement are passed to the functional + * interface, and the return values from the functional interface + * are returned back into the SQL statement. + * @throws SQLiteException if the custom function could not be registered. + * @see #setCustomScalarFunction(String, UnaryOperator) + */ + public void setCustomAggregateFunction(@NonNull String functionName, + @NonNull BinaryOperator<String> aggregateFunction) throws SQLiteException { + Objects.requireNonNull(functionName); + Objects.requireNonNull(aggregateFunction); synchronized (mLock) { throwIfNotOpenLocked(); - mConfigurationLocked.customFunctions.add(wrapper); + mConfigurationLocked.customAggregateFunctions.put(functionName, aggregateFunction); try { mConnectionPoolLocked.reconfigure(mConfigurationLocked); } catch (RuntimeException ex) { - mConfigurationLocked.customFunctions.remove(wrapper); + mConfigurationLocked.customAggregateFunctions.remove(functionName); throw ex; } } diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java index 6a52b72a9e1c..b11942abe0c7 100644 --- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java +++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java @@ -17,9 +17,12 @@ package android.database.sqlite; import android.compat.annotation.UnsupportedAppUsage; +import android.util.ArrayMap; -import java.util.ArrayList; import java.util.Locale; +import java.util.Map; +import java.util.function.BinaryOperator; +import java.util.function.UnaryOperator; import java.util.regex.Pattern; /** @@ -87,10 +90,16 @@ public final class SQLiteDatabaseConfiguration { public boolean foreignKeyConstraintsEnabled; /** - * The custom functions to register. + * The custom scalar functions to register. */ - public final ArrayList<SQLiteCustomFunction> customFunctions = - new ArrayList<SQLiteCustomFunction>(); + public final ArrayMap<String, UnaryOperator<String>> customScalarFunctions + = new ArrayMap<>(); + + /** + * The custom aggregate functions to register. + */ + public final ArrayMap<String, BinaryOperator<String>> customAggregateFunctions + = new ArrayMap<>(); /** * The size in bytes of each lookaside slot @@ -181,8 +190,10 @@ public final class SQLiteDatabaseConfiguration { maxSqlCacheSize = other.maxSqlCacheSize; locale = other.locale; foreignKeyConstraintsEnabled = other.foreignKeyConstraintsEnabled; - customFunctions.clear(); - customFunctions.addAll(other.customFunctions); + customScalarFunctions.clear(); + customScalarFunctions.putAll(other.customScalarFunctions); + customAggregateFunctions.clear(); + customAggregateFunctions.putAll(other.customAggregateFunctions); lookasideSlotSize = other.lookasideSlotSize; lookasideSlotCount = other.lookasideSlotCount; idleConnectionTimeoutMs = other.idleConnectionTimeoutMs; diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 799dff9632c8..fb5f136f2fca 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -26,6 +26,7 @@ import android.annotation.TestApi; import android.app.KeyguardManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.content.res.Resources; import android.graphics.Point; import android.media.projection.MediaProjection; import android.os.Handler; @@ -400,10 +401,10 @@ public final class DisplayManager { if (display == null) { // TODO: We cannot currently provide any override configurations for metrics on displays // other than the display the context is associated with. - final Context context = mContext.getDisplayId() == displayId - ? mContext : mContext.getApplicationContext(); + final Resources resources = mContext.getDisplayId() == displayId + ? mContext.getResources() : null; - display = mGlobal.getCompatibleDisplay(displayId, context.getResources()); + display = mGlobal.getCompatibleDisplay(displayId, resources); if (display != null) { mDisplays.put(displayId, display); } diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index aae9fd4725b4..61a1484cef0d 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -262,32 +262,60 @@ public abstract class NetworkAgent { */ public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17; - // TODO : remove these two constructors. They are a stopgap measure to help sheperding a number - // of dependent changes that would conflict throughout the automerger graph. Having these - // temporarily helps with the process of going through with all these dependent changes across - // the entire tree. - /** @hide TODO: decide which of these to expose. */ + /** @hide TODO: remove and replace usage with the public constructor. */ public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score) { this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE); } - /** @hide TODO: decide which of these to expose. */ + /** @hide TODO: remove and replace usage with the public constructor. */ public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) { this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE); } - /** @hide TODO: decide which of these to expose. */ + /** @hide TODO: remove and replace usage with the public constructor. */ public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, int providerId) { this(looper, context, logTag, ni, nc, lp, score, null, providerId); } - /** @hide TODO: decide which of these to expose. */ + /** @hide TODO: remove and replace usage with the public constructor. */ public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config, int providerId) { + this(looper, context, logTag, nc, lp, score, config, providerId, ni); + } + + private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) { + // The subtype can be changed with (TODO) setLegacySubtype, but it starts + // with the type and an empty description. + return new NetworkInfo(config.legacyType, config.legacyType, config.legacyTypeName, ""); + } + + /** + * Create a new network agent. + * @param context a {@link Context} to get system services from. + * @param looper the {@link Looper} on which to invoke the callbacks. + * @param logTag the tag for logs + * @param nc the initial {@link NetworkCapabilities} of this network. Update with + * sendNetworkCapabilities. + * @param lp the initial {@link LinkProperties} of this network. Update with sendLinkProperties. + * @param score the initial score of this network. Update with sendNetworkScore. + * @param config an immutable {@link NetworkAgentConfig} for this agent. + * @param provider the {@link NetworkProvider} managing this agent. + */ + public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag, + @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score, + @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) { + this(looper, context, logTag, nc, lp, score, config, + provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(), + getLegacyNetworkInfo(config)); + } + + private NetworkAgent(Looper looper, Context context, String logTag, NetworkCapabilities nc, + LinkProperties lp, int score, NetworkAgentConfig config, int providerId, + NetworkInfo ni) { mHandler = new NetworkAgentHandler(looper); LOG_TAG = logTag; mContext = context; diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java index abc6b67efb11..1e5af673b860 100644 --- a/core/java/android/net/NetworkAgentConfig.java +++ b/core/java/android/net/NetworkAgentConfig.java @@ -21,12 +21,11 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; -import android.text.TextUtils; /** * Allows a network transport to provide the system with policy and configuration information about - * a particular network when registering a {@link NetworkAgent}. This information cannot change once - * the agent is registered. + * a particular network when registering a {@link NetworkAgent}. + * @note This information cannot change once the agent is registered. * * @hide */ @@ -120,6 +119,19 @@ public final class NetworkAgentConfig implements Parcelable { } /** + * The legacy type of this network agent, or TYPE_NONE if unset. + * @hide + */ + public int legacyType = ConnectivityManager.TYPE_NONE; + + /** + * @return the legacy type + */ + public int getLegacyType() { + return legacyType; + } + + /** * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network. * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode. * @@ -127,6 +139,21 @@ public final class NetworkAgentConfig implements Parcelable { */ public boolean hasShownBroken; + /** + * The name of the legacy network type. It's a free-form string used in logging. + * @hide + */ + @NonNull + public String legacyTypeName = ""; + + /** + * @return the name of the legacy network type. It's a free-form string used in logging. + */ + @NonNull + public String getLegacyTypeName() { + return legacyTypeName; + } + /** @hide */ public NetworkAgentConfig() { } @@ -140,6 +167,8 @@ public final class NetworkAgentConfig implements Parcelable { subscriberId = nac.subscriberId; provisioningNotificationDisabled = nac.provisioningNotificationDisabled; skip464xlat = nac.skip464xlat; + legacyType = nac.legacyType; + legacyTypeName = nac.legacyTypeName; } } @@ -185,6 +214,29 @@ public final class NetworkAgentConfig implements Parcelable { } /** + * Sets the legacy type for this network. + * + * @param legacyType the type + * @return this builder, to facilitate chaining. + */ + @NonNull + public Builder setLegacyType(int legacyType) { + mConfig.legacyType = legacyType; + return this; + } + + /** + * Sets the name of the legacy type of the agent. It's a free-form string used in logging. + * @param legacyTypeName the name + * @return this builder, to facilitate chaining. + */ + @NonNull + public Builder setLegacyTypeName(@NonNull String legacyTypeName) { + mConfig.legacyTypeName = legacyTypeName; + return this; + } + + /** * Returns the constructed {@link NetworkAgentConfig} object. */ @NonNull diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index edaaf81cd906..33d613152bc1 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -113,7 +113,7 @@ interface IUserManager { boolean isUserRunning(int userId); boolean isUserNameSet(int userId); boolean hasRestrictedProfiles(); - boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target); + boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags); String getUserName(); long getUserStartRealtime(); long getUserUnlockRealtime(); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 82b04a661b54..0414b14ae02d 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -373,9 +373,15 @@ public final class PowerManager { public static final int GO_TO_SLEEP_REASON_INATTENTIVE = 9; /** + * Go to sleep reason code: Going to sleep due to quiescent boot. * @hide */ - public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_INATTENTIVE; + public static final int GO_TO_SLEEP_REASON_QUIESCENT = 10; + + /** + * @hide + */ + public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_QUIESCENT; /** * @hide diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 2eaefca0efa3..12e843c87481 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -133,6 +133,22 @@ public class UserManager { public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS"; /** + * Flag passed to {@link #requestQuietModeEnabled} to request disabling quiet mode only if + * there is no need to confirm the user credentials. If credentials are required to disable + * quiet mode, {@link #requestQuietModeEnabled} will do nothing and return {@code false}. + */ + public static final int QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED = 0x1; + + /** + * List of flags available for the {@link #requestQuietModeEnabled} method. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "QUIET_MODE_" }, value = { + QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED }) + public @interface QuietModeFlag {} + + /** * @hide * No user restriction. */ @@ -3216,6 +3232,25 @@ public class UserManager { } /** + * Perform the same operation as {@link #requestQuietModeEnabled(boolean, UserHandle)}, but + * with a flag to tweak the behavior of the request. + * + * @param enableQuietMode whether quiet mode should be enabled or disabled + * @param userHandle user handle of the profile + * @param flags Can be 0 or {@link #QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED}. + * @return {@code false} if user's credential is needed in order to turn off quiet mode, + * {@code true} otherwise + * @throws SecurityException if the caller is invalid + * @throws IllegalArgumentException if {@code userHandle} is not a managed profile + * + * @see #isQuietModeEnabled(UserHandle) + */ + public boolean requestQuietModeEnabled(boolean enableQuietMode, @NonNull UserHandle userHandle, + @QuietModeFlag int flags) { + return requestQuietModeEnabled(enableQuietMode, userHandle, null, flags); + } + + /** * Similar to {@link #requestQuietModeEnabled(boolean, UserHandle)}, except you can specify * a target to start when user is unlocked. If {@code target} is specified, caller must have * the {@link android.Manifest.permission#MANAGE_USERS} permission. @@ -3225,9 +3260,23 @@ public class UserManager { */ public boolean requestQuietModeEnabled( boolean enableQuietMode, @NonNull UserHandle userHandle, IntentSender target) { + return requestQuietModeEnabled(enableQuietMode, userHandle, target, 0); + } + /** + * Similar to {@link #requestQuietModeEnabled(boolean, UserHandle)}, except you can specify + * a target to start when user is unlocked. If {@code target} is specified, caller must have + * the {@link android.Manifest.permission#MANAGE_USERS} permission. + * + * @see {@link #requestQuietModeEnabled(boolean, UserHandle)} + * @hide + */ + public boolean requestQuietModeEnabled( + boolean enableQuietMode, @NonNull UserHandle userHandle, IntentSender target, + int flags) { try { return mService.requestQuietModeEnabled( - mContext.getPackageName(), enableQuietMode, userHandle.getIdentifier(), target); + mContext.getPackageName(), enableQuietMode, userHandle.getIdentifier(), target, + flags); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index f0a11748fbd6..3ea64f13fe6b 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -876,14 +876,7 @@ public class StorageManager { */ public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException { Preconditions.checkNotNull(path); - String pathString = path.getCanonicalPath(); - if (path.getPath().startsWith("/sdcard")) { - // On FUSE enabled devices, realpath(2) /sdcard is /mnt/user/<userid>/emulated/<userid> - // as opposed to /storage/emulated/<userid>. - // And vol.path below expects to match with a path starting with /storage - pathString = pathString.replaceFirst("^/mnt/user/[0-9]+/", "/storage/"); - } - + final String pathString = path.getCanonicalPath(); if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) { return UUID_DEFAULT; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f6633201ec73..0e3dd3a8292a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -13929,6 +13929,19 @@ public final class Settings { */ public static final String POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE = "power_button_suppression_delay_after_gesture_wake"; + + /** + * An integer indicating whether the device is in Common Criteria mode. When enabled, + * certain device functionalities are tuned to meet the higher security level required + * by Common Criteria certification. Examples include: + * Bluetooth long term key material is additionally integrity-protected with AES-GCM. + * WiFi configuration store is additionally integrity-protected with AES-GCM. + * A value of 0 means Common Criteria mode is not enabled (default), a value of non-zero + * means Common Criteria mode is enabled. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_APPS) + public static final String COMMON_CRITERIA_MODE = "common_criteria_mode"; } /** @@ -14360,46 +14373,74 @@ public final class Settings { /** * Activity Action: Show setting page to process the addition of Wi-Fi networks to the user's - * saved network list. The app should send a new intent with an extra that holds a maximum of - * five {@link android.net.wifi.WifiConfiguration} that specify credentials for the networks to - * be added to the user's database. The Intent should be sent via the {@link - * android.app.Activity#startActivityForResult(Intent, int)} API. + * saved network list. The app should send a new intent with an extra that holds a maximum + * of five {@link android.net.wifi.WifiNetworkSuggestion} that specify credentials for the + * networks to be added to the user's database. The Intent should be sent via the + * {@link android.app.Activity#startActivityForResult(Intent, int)} API. * <p> * Note: The app sending the Intent to add the credentials doesn't get any ownership over the * newly added network(s). For the Wi-Fi stack, these networks will look like the user * manually added them from the Settings UI. * <p> - * Input: The app should put parcelable array list to - * {@link android.net.wifi.WifiConfiguration} into the - * {@link #EXTRA_WIFI_CONFIGURATION_LIST} extra. + * Input: The app should put parcelable array list of + * {@link android.net.wifi.WifiNetworkSuggestion} into the {@link #EXTRA_WIFI_NETWORK_LIST} + * extra. * <p> * Output: After {@link android.app.Activity#startActivityForResult(Intent, int)}, the * callback {@link android.app.Activity#onActivityResult(int, int, Intent)} will have a * result code {@link android.app.Activity#RESULT_OK} to indicate user pressed the save * button to save the networks or {@link android.app.Activity#RESULT_CANCELED} to indicate * that the user rejected the request. Additionally, an integer array list, stored in - * {@link #EXTRA_WIFI_CONFIGURATION_RESULT_LIST}, will indicate the process result of - * each network. + * {@link #EXTRA_WIFI_NETWORK_RESULT_LIST}, will indicate the process result of each network. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_WIFI_ADD_NETWORKS = "android.settings.WIFI_ADD_NETWORKS"; /** - * A bundle extra of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that indicates all the - * {@link android.net.wifi.WifiConfiguration} that would be saved. - */ - public static final String EXTRA_WIFI_CONFIGURATION_LIST = - "android.provider.extra.WIFI_CONFIGURATION_LIST"; + * A bundle extra of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that indicates the list + * of the {@link android.net.wifi.WifiNetworkSuggestion} elements. The maximum count of the + * {@link android.net.wifi.WifiNetworkSuggestion} elements in the list will be five. + * <p> + * For example: + * To provide credentials for one open and one WPA2 networks: + * + * <pre>{@code + * final WifiNetworkSuggestion suggestion1 = + * new WifiNetworkSuggestion.Builder() + * .setSsid("test111111") + * .build(); + * final WifiNetworkSuggestion suggestion2 = + * new WifiNetworkSuggestion.Builder() + * .setSsid("test222222") + * .setWpa2Passphrase("test123456") + * .build(); + * final List<WifiNetworkSuggestion> suggestionsList = new ArrayList<>; + * suggestionsList.add(suggestion1); + * suggestionsList.add(suggestion2); + * Bundle bundle = new Bundle(); + * bundle.putParcelableArrayList(Settings.EXTRA_WIFI_NETWORK_LIST,(ArrayList<? extends + * Parcelable>) suggestionsList); + * final Intent intent = new Intent(Settings.ACTION_WIFI_ADD_NETWORKS); + * intent.putExtras(bundle); + * startActivityForResult(intent, 0); + * }</pre> + */ + public static final String EXTRA_WIFI_NETWORK_LIST = + "android.provider.extra.WIFI_NETWORK_LIST"; /** * A bundle extra of the result of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that - * indicates the action result of the saved {@link android.net.wifi.WifiConfiguration}. It's - * value of AddWifiResult interface, and will be 1:1 mapping to the element in {@link - * #EXTRA_WIFI_CONFIGURATION_LIST}. + * indicates the action result of the saved {@link android.net.wifi.WifiNetworkSuggestion}. + * Its value is a list of integers, and all the elements will be 1:1 mapping to the elements + * in {@link #EXTRA_WIFI_NETWORK_LIST}, if user press cancel to cancel the add networks + * request, then its value will be null. + * <p> + * Note: The integer value will be one of the {@link #ADD_WIFI_RESULT_SUCCESS}, + * {@link #ADD_WIFI_RESULT_ADD_OR_UPDATE_FAILED}, or {@link #ADD_WIFI_RESULT_ALREADY_EXISTS}}. */ - public static final String EXTRA_WIFI_CONFIGURATION_RESULT_LIST = - "android.provider.extra.WIFI_CONFIGURATION_RESULT_LIST"; + public static final String EXTRA_WIFI_NETWORK_RESULT_LIST = + "android.provider.extra.WIFI_NETWORK_RESULT_LIST"; /** @hide */ @Retention(RetentionPolicy.SOURCE) diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java index 00060ab8ef4a..a5c5c613e1f2 100644 --- a/core/java/android/se/omapi/SEService.java +++ b/core/java/android/se/omapi/SEService.java @@ -98,6 +98,8 @@ public final class SEService { private static final String TAG = "OMAPI.SEService"; + private static final String UICC_TERMINAL = "SIM"; + private final Object mLock = new Object(); /** The client context (e.g. activity). */ @@ -190,32 +192,33 @@ public final class SEService { * is of length 0. */ public @NonNull Reader[] getReaders() { - if (mSecureElementService == null) { - throw new IllegalStateException("service not connected to system"); - } - String[] readerNames; - try { - readerNames = mSecureElementService.getReaders(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + loadReaders(); - Reader[] readers = new Reader[readerNames.length]; - int i = 0; - for (String readerName : readerNames) { - if (mReaders.get(readerName) == null) { - try { - mReaders.put(readerName, new Reader(this, readerName, - getReader(readerName))); - readers[i++] = mReaders.get(readerName); - } catch (Exception e) { - Log.e(TAG, "Error adding Reader: " + readerName, e); - } - } else { - readers[i++] = mReaders.get(readerName); - } - } - return readers; + return mReaders.values().toArray(new Reader[0]); + } + + /** + * Obtain a UICC Reader instance with specific slot number from the SecureElementService + * + * @param slotNumber The index of the uicc slot. The index starts from 1. + * @throws IllegalArgumentException if the reader object corresponding to the uiccSlotNumber + * is not exist. + * @return A Reader object for this uicc slot. + */ + public @NonNull Reader getUiccReader(int slotNumber) { + if (slotNumber < 1) { + throw new IllegalArgumentException("slotNumber should be larger than 0"); + } + loadReaders(); + + String readerName = UICC_TERMINAL + slotNumber; + Reader reader = mReaders.get(readerName); + + if (reader == null) { + throw new IllegalArgumentException("Reader:" + readerName + " doesn't exist"); + } + + return reader; } /** @@ -270,4 +273,30 @@ public final class SEService { throw new IllegalStateException(e.getMessage()); } } + + /** + * Load available Secure Element Readers + */ + private void loadReaders() { + if (mSecureElementService == null) { + throw new IllegalStateException("service not connected to system"); + } + String[] readerNames; + try { + readerNames = mSecureElementService.getReaders(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + + for (String readerName : readerNames) { + if (mReaders.get(readerName) == null) { + try { + mReaders.put(readerName, new Reader(this, readerName, + getReader(readerName))); + } catch (Exception e) { + Log.e(TAG, "Error adding Reader: " + readerName, e); + } + } + } + } } diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 389040cea864..4f400a8e9cb2 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -16,6 +16,9 @@ package android.service.notification; +import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID; +import static android.util.FeatureFlagUtils.*; + import android.annotation.NonNull; import android.app.Notification; import android.app.NotificationManager; @@ -29,6 +32,8 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import android.text.TextUtils; +import android.util.FeatureFlagUtils; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -436,6 +441,19 @@ public class StatusBarNotification implements Parcelable { return logMaker; } + /** + * @hide + */ + public String getShortcutId(Context context) { + String conversationId = getNotification().getShortcutId(); + if (isEnabled(context, NOTIF_CONVO_BYPASS_SHORTCUT_REQ) + && getNotification().getNotificationStyle() == Notification.MessagingStyle.class + && TextUtils.isEmpty(conversationId)) { + conversationId = getId() + getTag() + PLACEHOLDER_CONVERSATION_ID; + } + return conversationId; + } + private String getGroupLogTag() { return shortenTag(getGroup()); } diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 178b3c0cb94e..904c510a5b01 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -454,7 +454,7 @@ public final class Display { mResources = res; mDisplayAdjustments = mResources != null ? new DisplayAdjustments(mResources.getConfiguration()) - : daj != null ? new DisplayAdjustments(daj) : null; + : daj != null ? new DisplayAdjustments(daj) : new DisplayAdjustments(); mIsValid = true; // Cache properties that cannot change as long as the display is valid. diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index cc4278bdd2b6..38416eeede32 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -159,7 +159,6 @@ public final class SurfaceControl implements Parcelable { private static native DisplayedContentSample nativeGetDisplayedContentSample( IBinder displayToken, long numFrames, long timestamp); private static native int nativeGetActiveConfig(IBinder displayToken); - private static native boolean nativeSetActiveConfig(IBinder displayToken, int id); private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken, SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs); private static native SurfaceControl.DesiredDisplayConfigSpecs @@ -1482,16 +1481,6 @@ public final class SurfaceControl implements Parcelable { /** - * @hide - */ - public static boolean setActiveConfig(IBinder displayToken, int id) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - return nativeSetActiveConfig(displayToken, id); - } - - /** * Contains information about desired display configuration. * * @hide diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 562ed0eb8e70..6724e9d3289d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -12602,11 +12602,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return findViewInsideOutShouldExist(root, mNextFocusForwardId); case FOCUS_BACKWARD: { if (mID == View.NO_ID) return null; - final int id = mID; return root.findViewByPredicateInsideOut(this, new Predicate<View>() { @Override public boolean test(View t) { - return t.mNextFocusForwardId == id; + return t.findViewById(t.mNextFocusForwardId) == View.this; } }); } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 3dfeffbf9e6a..9cbba87e6856 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -1444,6 +1444,29 @@ public final class AccessibilityManager { return null; } + /** + * + * Sets an {@link IWindowMagnificationConnection} that manipulates window magnification. + * + * @param connection The connection that manipulates window magnification. + * @hide + */ + public void setWindowMagnificationConnection(@Nullable + IWindowMagnificationConnection connection) { + final IAccessibilityManager service; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return; + } + } + try { + service.setWindowMagnificationConnection(connection); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error setting window magnfication connection", re); + } + } + private IAccessibilityManager getServiceLocked() { if (mService == null) { tryConnectToServiceLocked(null); diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index fcaaa2e74778..7f8fdf83995a 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -25,6 +25,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityManagerClient; +import android.view.accessibility.IWindowMagnificationConnection; import android.view.IWindow; /** @@ -86,4 +87,5 @@ interface IAccessibilityManager { oneway void registerSystemAction(in RemoteAction action, int actionId); oneway void unregisterSystemAction(int actionId); + oneway void setWindowMagnificationConnection(in IWindowMagnificationConnection connection); } diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl new file mode 100644 index 000000000000..0b45c6bed9bd --- /dev/null +++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.accessibility; + +import android.graphics.PointF; +import android.graphics.Rect; +import android.view.accessibility.IWindowMagnificationConnectionCallback; + +/** + * Interface for interaction between {@link AccessibilityManagerService} + * and {@link WindowMagnification} in SystemUI. + * + * @hide + */ +oneway interface IWindowMagnificationConnection { + + /** + * Enables window magnification on specifed display with specified center and scale. + * + * @param displayId The logical display id. + * @param scale magnification scale. + * @param centerX the screen-relative X coordinate around which to center, + * or {@link Float#NaN} to leave unchanged. + * @param centerY the screen-relative Y coordinate around which to center, + * or {@link Float#NaN} to leave unchanged. + */ + void enableWindowMagnification(int displayId, float scale, float centerX, float centerY); + + /** + * Sets the scale of the window magnifier on specifed display. + * + * @param displayId The logical display id. + * @param scale magnification scale. + */ + void setScale(int displayId, float scale); + + /** + * Disables window magnification on specifed display. + * + * @param displayId The logical display id. + */ + void disableWindowMagnification(int displayId); + + /** + * Moves the window magnifier on the specifed display. + * + * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in + * current screen pixels. + * @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in + * current screen pixels. + */ + void moveWindowMagnifier(int displayId, float offsetX, float offsetY); + + /** + * Sets {@link IWindowMagnificationConnectionCallback} to receive the request or the callback. + * + * + * @param callback the interface to be called. + */ + void setConnectionCallback(in IWindowMagnificationConnectionCallback callback); +} diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl new file mode 100644 index 000000000000..7327bb571d59 --- /dev/null +++ b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.accessibility; + +import android.graphics.Rect; + +/** + * interface to notify the change of the window magnifier bounds and request to change + * the magnification mode. + * + * @hide + */ + oneway interface IWindowMagnificationConnectionCallback { + + /** + * Called when the bounds of the window magnifier is changed. + * + * @param displayId The logical display id. + * @param bounds The window magnifier bounds in screen coordinates. + */ + void onWindowMagnifierBoundsChanged(int display, in Rect bounds); + /** + * Changes the magnification mode on specified display. It is invoked by System UI when the + * switch button is toggled. + * + * @param displayId The logical display id. + * @param magnificationMode new magnification mode. + */ + void onChangeMagnificationMode(int display, int magnificationMode); +} diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index 1aa2aeccc0db..bda12b0893d1 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -113,7 +113,7 @@ public final class TextLinks implements Parcelable { * Returns the text that was used to generate these links. */ @NonNull - public String getText() { + public CharSequence getText() { return mFullText; } @@ -370,8 +370,8 @@ public final class TextLinks implements Parcelable { } /** - * @return ordered list of locale preferences that can be used to disambiguate - * the provided text + * Returns an ordered list of locale preferences that can be used to disambiguate the + * provided text. */ @Nullable public LocaleList getDefaultLocales() { @@ -379,7 +379,8 @@ public final class TextLinks implements Parcelable { } /** - * @return The config representing the set of entities to look for + * Returns the config representing the set of entities to look for + * * @see Builder#setEntityConfig(EntityConfig) */ @Nullable @@ -398,8 +399,8 @@ public final class TextLinks implements Parcelable { } /** - * @return reference time based on which relative dates (e.g. "tomorrow") should be - * interpreted. + * Returns reference time based on which relative dates (e.g. "tomorrow") should be + * interpreted. */ @Nullable public ZonedDateTime getReferenceTime() { @@ -473,6 +474,9 @@ public final class TextLinks implements Parcelable { } /** + * Sets ordered list of locale preferences that may be used to disambiguate the + * provided text. + * * @param defaultLocales ordered list of locale preferences that may be used to * disambiguate the provided text. If no locale preferences exist, * set this to null or an empty locale list. @@ -524,9 +528,11 @@ public final class TextLinks implements Parcelable { } /** - * @param referenceTime reference time based on which relative dates (e.g. "tomorrow" - * should be interpreted. This should usually be the time when the text was - * originally composed. + * Sets the reference time based on which relative dates (e.g. + * "tomorrow") should be interpreted. + * + * @param referenceTime reference time based on which relative dates. This should + * usually be the time when the text was originally composed. * * @return this builder */ @@ -716,6 +722,8 @@ public final class TextLinks implements Parcelable { } /** + * Adds a TextLink. + * * @see #addLink(int, int, Map) * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled. */ diff --git a/core/java/android/view/textclassifier/TextLinksParams.java b/core/java/android/view/textclassifier/TextLinksParams.java index b7d63bfabd6d..f12b0d7aa2e0 100644 --- a/core/java/android/view/textclassifier/TextLinksParams.java +++ b/core/java/android/view/textclassifier/TextLinksParams.java @@ -113,7 +113,7 @@ public final class TextLinksParams { return TextLinks.STATUS_UNSUPPORTED_CHARACTER; } - if (!textString.startsWith(textLinks.getText())) { + if (!textString.startsWith(textLinks.getText().toString())) { return TextLinks.STATUS_DIFFERENT_TEXT; } if (textLinks.getLinks().isEmpty()) { diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index 3fdedc88fe53..93659a4ac1eb 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -371,7 +371,9 @@ public class AccessibilityShortcutController { // targets during boot. Needs to read settings directly here. String shortcutTargets = Settings.Secure.getStringForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, mUserId); - if (TextUtils.isEmpty(shortcutTargets)) { + // A11y warning dialog updates settings to empty string, when user disables a11y shortcut. + // Only fallback to default a11y service, when setting is never updated. + if (shortcutTargets == null) { shortcutTargets = mContext.getString(R.string.config_defaultAccessibilityService); } return !TextUtils.isEmpty(shortcutTargets); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index b6b548c13d66..9532faecb4df 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -71,6 +71,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Parcelable; +import android.os.PatternMatcher; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; @@ -1650,10 +1651,28 @@ public class ChooserActivity extends ResolverActivity implements try { final Intent intent = getTargetIntent(); String dataString = intent.getDataString(); - if (TextUtils.isEmpty(dataString)) { - dataString = intent.getType(); + if (!TextUtils.isEmpty(dataString)) { + return new IntentFilter(intent.getAction(), dataString); + } + IntentFilter intentFilter = new IntentFilter(intent.getAction(), intent.getType()); + List<Uri> contentUris = new ArrayList<>(); + if (Intent.ACTION_SEND.equals(intent.getAction())) { + Uri uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); + if (uri != null) { + contentUris.add(uri); + } + } else { + List<Uri> uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + if (uris != null) { + contentUris.addAll(uris); + } + } + for (Uri uri : contentUris) { + intentFilter.addDataScheme(uri.getScheme()); + intentFilter.addDataAuthority(uri.getAuthority(), null); + intentFilter.addDataPath(uri.getPath(), PatternMatcher.PATTERN_LITERAL); } - return new IntentFilter(intent.getAction(), dataString); + return intentFilter; } catch (Exception e) { Log.e(TAG, "failed to get target intent filter " + e); return null; diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 179828c4b456..13d0c5c831b6 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -35,6 +35,7 @@ import com.android.internal.logging.AndroidConfig; import com.android.server.NetworkManagementSocketTagger; import dalvik.system.RuntimeHooks; +import dalvik.system.ThreadPrioritySetter; import dalvik.system.VMRuntime; import libcore.content.type.MimeMap; @@ -204,6 +205,7 @@ public class RuntimeInit { */ public static void preForkInit() { if (DEBUG) Slog.d(TAG, "Entered preForkInit."); + RuntimeHooks.setThreadPrioritySetter(new RuntimeThreadPrioritySetter()); RuntimeInit.enableDdms(); // TODO(b/142019040#comment13): Decide whether to load the default instance eagerly, i.e. // MimeMap.setDefault(DefaultMimeMapFactory.create()); @@ -216,6 +218,35 @@ public class RuntimeInit { MimeMap.setDefaultSupplier(DefaultMimeMapFactory::create); } + private static class RuntimeThreadPrioritySetter implements ThreadPrioritySetter { + // Should remain consistent with kNiceValues[] in system/libartpalette/palette_android.cc + private static final int[] NICE_VALUES = { + Process.THREAD_PRIORITY_LOWEST, // 1 (MIN_PRIORITY) + Process.THREAD_PRIORITY_BACKGROUND + 6, + Process.THREAD_PRIORITY_BACKGROUND + 3, + Process.THREAD_PRIORITY_BACKGROUND, + Process.THREAD_PRIORITY_DEFAULT, // 5 (NORM_PRIORITY) + Process.THREAD_PRIORITY_DEFAULT - 2, + Process.THREAD_PRIORITY_DEFAULT - 4, + Process.THREAD_PRIORITY_URGENT_DISPLAY + 3, + Process.THREAD_PRIORITY_URGENT_DISPLAY + 2, + Process.THREAD_PRIORITY_URGENT_DISPLAY // 10 (MAX_PRIORITY) + }; + + @Override + public void setPriority(int nativeTid, int priority) { + // Check NICE_VALUES[] length first. + if (NICE_VALUES.length != (1 + Thread.MAX_PRIORITY - Thread.MIN_PRIORITY)) { + throw new AssertionError("Unexpected NICE_VALUES.length=" + NICE_VALUES.length); + } + // Priority should be in the range of MIN_PRIORITY (1) to MAX_PRIORITY (10). + if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) { + throw new IllegalArgumentException("Priority out of range: " + priority); + } + Process.setThreadPriority(nativeTid, NICE_VALUES[priority - Thread.MIN_PRIORITY]); + } + } + @UnsupportedAppUsage protected static final void commonInit() { if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!"); diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 984f93caa937..593728350037 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -457,15 +457,6 @@ static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, sk_ref_sp(bitmap->info().colorSpace()))); } -// These must match the int values in Bitmap.java -enum JavaEncodeFormat { - kJPEG_JavaEncodeFormat = 0, - kPNG_JavaEncodeFormat = 1, - kWEBP_JavaEncodeFormat = 2, - kWEBP_LOSSY_JavaEncodeFormat = 3, - kWEBP_LOSSLESS_JavaEncodeFormat = 4, -}; - static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, jint format, jint quality, jobject jstream, jbyteArray jstorage) { @@ -479,51 +470,9 @@ static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle, return JNI_FALSE; } - SkBitmap skbitmap; - bitmap->getSkBitmap(&skbitmap); - if (skbitmap.colorType() == kRGBA_F16_SkColorType) { - // Convert to P3 before encoding. This matches SkAndroidCodec::computeOutputColorSpace - // for wide gamuts. - auto cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); - auto info = skbitmap.info().makeColorType(kRGBA_8888_SkColorType) - .makeColorSpace(std::move(cs)); - SkBitmap p3; - if (!p3.tryAllocPixels(info)) { - return JNI_FALSE; - } - - SkPixmap pm; - SkAssertResult(p3.peekPixels(&pm)); // should always work if tryAllocPixels() did. - if (!skbitmap.readPixels(pm)) { - return JNI_FALSE; - } - skbitmap = p3; - } - SkEncodedImageFormat fm; - switch (format) { - case kJPEG_JavaEncodeFormat: - fm = SkEncodedImageFormat::kJPEG; - break; - case kPNG_JavaEncodeFormat: - fm = SkEncodedImageFormat::kPNG; - break; - case kWEBP_JavaEncodeFormat: - fm = SkEncodedImageFormat::kWEBP; - break; - case kWEBP_LOSSY_JavaEncodeFormat: - case kWEBP_LOSSLESS_JavaEncodeFormat: { - SkWebpEncoder::Options options; - options.fQuality = quality; - options.fCompression = format == kWEBP_LOSSY_JavaEncodeFormat ? - SkWebpEncoder::Compression::kLossy : SkWebpEncoder::Compression::kLossless; - return SkWebpEncoder::Encode(strm.get(), skbitmap.pixmap(), options) ? - JNI_TRUE : JNI_FALSE; - } - default: - return JNI_FALSE; - } - - return SkEncodeImage(strm.get(), skbitmap, fm, quality) ? JNI_TRUE : JNI_FALSE; + auto fm = static_cast<Bitmap::JavaCompressFormat>(format); + auto result = bitmap->bitmap().compress(fm, quality, strm.get()); + return result == Bitmap::CompressResult::Success ? JNI_TRUE : JNI_FALSE; } static inline void bitmapErase(SkBitmap bitmap, const SkColor4f& color, diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp index a9002867ae10..e17e057d75c7 100644 --- a/core/jni/android/graphics/ImageDecoder.cpp +++ b/core/jni/android/graphics/ImageDecoder.cpp @@ -241,7 +241,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong doThrowISE(env, "Could not scale to target size!"); return nullptr; } - if (requireUnpremul && !decoder->setOutAlphaType(kUnpremul_SkAlphaType)) { + if (requireUnpremul && !decoder->setUnpremultipliedRequired(true)) { doThrowISE(env, "Cannot scale unpremultiplied pixels!"); return nullptr; } @@ -301,11 +301,15 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong } } - SkBitmap bm; SkImageInfo bitmapInfo = decoder->getOutputInfo(); + if (decoder->opaque()) { + bitmapInfo = bitmapInfo.makeAlphaType(kOpaque_SkAlphaType); + } if (asAlphaMask && colorType == kGray_8_SkColorType) { bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType); } + + SkBitmap bm; if (!bm.setInfo(bitmapInfo)) { doThrowIOE(env, "Failed to setInfo properly"); return nullptr; diff --git a/core/jni/android/graphics/apex/android_bitmap.cpp b/core/jni/android/graphics/apex/android_bitmap.cpp index 6a3c01efe98c..b8e04a7e9a2b 100644 --- a/core/jni/android/graphics/apex/android_bitmap.cpp +++ b/core/jni/android/graphics/apex/android_bitmap.cpp @@ -23,6 +23,7 @@ #include <GraphicsJNI.h> #include <hwui/Bitmap.h> +#include <utils/Color.h> using namespace android; @@ -122,6 +123,7 @@ AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmapHandle) { return getInfo(bitmap->info(), bitmap->rowBytes()); } +namespace { static bool nearlyEqual(float a, float b) { // By trial and error, this is close enough to match for the ADataSpaces we // compare for. @@ -156,6 +158,7 @@ static constexpr skcms_Matrix3x3 kDCIP3 = {{ {0.226676, 0.710327, 0.0629966}, {0.000800549, 0.0432385, 0.78275}, }}; +} // anonymous namespace ADataSpace ABitmap_getDataSpace(ABitmap* bitmapHandle) { Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); @@ -243,3 +246,135 @@ void ABitmap_notifyPixelsChanged(ABitmap* bitmapHandle) { } return bitmap->notifyPixelsChanged(); } + +namespace { +SkAlphaType getAlphaType(const AndroidBitmapInfo* info) { + switch (info->flags & ANDROID_BITMAP_FLAGS_ALPHA_MASK) { + case ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE: + return kOpaque_SkAlphaType; + case ANDROID_BITMAP_FLAGS_ALPHA_PREMUL: + return kPremul_SkAlphaType; + case ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL: + return kUnpremul_SkAlphaType; + default: + return kUnknown_SkAlphaType; + } +} + +class CompressWriter : public SkWStream { +public: + CompressWriter(void* userContext, AndroidBitmap_compress_write_fn fn) + : mUserContext(userContext), mFn(fn), mBytesWritten(0) {} + + bool write(const void* buffer, size_t size) override { + if (mFn(mUserContext, buffer, size)) { + mBytesWritten += size; + return true; + } + return false; + } + + size_t bytesWritten() const override { return mBytesWritten; } + +private: + void* mUserContext; + AndroidBitmap_compress_write_fn mFn; + size_t mBytesWritten; +}; + +} // anonymous namespace + +int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, + AndroidBitmapCompressFormat inFormat, int32_t quality, void* userContext, + AndroidBitmap_compress_write_fn fn) { + Bitmap::JavaCompressFormat format; + switch (inFormat) { + case ANDROID_BITMAP_COMPRESS_FORMAT_JPEG: + format = Bitmap::JavaCompressFormat::Jpeg; + break; + case ANDROID_BITMAP_COMPRESS_FORMAT_PNG: + format = Bitmap::JavaCompressFormat::Png; + break; + case ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY: + format = Bitmap::JavaCompressFormat::WebpLossy; + break; + case ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS: + format = Bitmap::JavaCompressFormat::WebpLossless; + break; + default: + // kWEBP_JavaEncodeFormat is a valid parameter for Bitmap::compress, + // for the deprecated Bitmap.CompressFormat.WEBP, but it should not + // be provided via the NDK. Other integers are likewise invalid. + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + SkColorType colorType; + switch (info->format) { + case ANDROID_BITMAP_FORMAT_RGBA_8888: + colorType = kN32_SkColorType; + break; + case ANDROID_BITMAP_FORMAT_RGB_565: + colorType = kRGB_565_SkColorType; + break; + case ANDROID_BITMAP_FORMAT_A_8: + // FIXME b/146637821: Should this encode as grayscale? We should + // make the same decision as for encoding an android.graphics.Bitmap. + // Note that encoding kAlpha_8 as WebP or JPEG will fail. Encoding + // it to PNG encodes as GRAY+ALPHA with a secret handshake that we + // only care about the alpha. I'm not sure whether Android decoding + // APIs respect that handshake. + colorType = kAlpha_8_SkColorType; + break; + case ANDROID_BITMAP_FORMAT_RGBA_F16: + colorType = kRGBA_F16_SkColorType; + break; + default: + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + auto alphaType = getAlphaType(info); + if (alphaType == kUnknown_SkAlphaType) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + sk_sp<SkColorSpace> cs; + if (info->format == ANDROID_BITMAP_FORMAT_A_8) { + // FIXME: A Java Bitmap with ALPHA_8 never has a ColorSpace. So should + // we force that here (as I'm doing now) or should we treat anything + // besides ADATASPACE_UNKNOWN as an error? + cs = nullptr; + } else { + cs = uirenderer::DataSpaceToColorSpace((android_dataspace) dataSpace); + // DataSpaceToColorSpace treats UNKNOWN as SRGB, but compress forces the + // client to specify SRGB if that is what they want. + if (!cs || dataSpace == ADATASPACE_UNKNOWN) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + } + + { + size_t size; + if (!Bitmap::computeAllocationSize(info->stride, info->height, &size)) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + } + + auto imageInfo = + SkImageInfo::Make(info->width, info->height, colorType, alphaType, std::move(cs)); + SkBitmap bitmap; + // We are not going to modify the pixels, but installPixels expects them to + // not be const, since for all it knows we might want to draw to the SkBitmap. + if (!bitmap.installPixels(imageInfo, const_cast<void*>(pixels), info->stride)) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + CompressWriter stream(userContext, fn); + switch (Bitmap::compress(bitmap, format, quality, &stream)) { + case Bitmap::CompressResult::Success: + return ANDROID_BITMAP_RESULT_SUCCESS; + case Bitmap::CompressResult::AllocationFailed: + return ANDROID_BITMAP_RESULT_ALLOCATION_FAILED; + case Bitmap::CompressResult::Error: + return ANDROID_BITMAP_RESULT_JNI_EXCEPTION; + } +} diff --git a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h index 32b8a450e147..683851d09d93 100644 --- a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h +++ b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h @@ -58,6 +58,11 @@ void ABitmap_notifyPixelsChanged(ABitmap* bitmap); AndroidBitmapFormat ABitmapConfig_getFormatFromConfig(JNIEnv* env, jobject bitmapConfigObj); jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format); +// NDK access +int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, + AndroidBitmapCompressFormat format, int32_t quality, void* userContext, + AndroidBitmap_compress_write_fn); + __END_DECLS #ifdef __cplusplus diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp index d9ed07ebcfbb..c80f1dc28595 100644 --- a/core/jni/android_database_SQLiteConnection.cpp +++ b/core/jni/android_database_SQLiteConnection.cpp @@ -59,14 +59,12 @@ namespace android { static const int BUSY_TIMEOUT_MS = 2500; static struct { - jfieldID name; - jfieldID numArgs; - jmethodID dispatchCallback; -} gSQLiteCustomFunctionClassInfo; + jmethodID apply; +} gUnaryOperator; static struct { - jclass clazz; -} gStringClassInfo; + jmethodID apply; +} gBinaryOperator; struct SQLiteConnection { // Open flags. @@ -203,74 +201,146 @@ static void nativeClose(JNIEnv* env, jclass clazz, jlong connectionPtr) { } } -// Called each time a custom function is evaluated. -static void sqliteCustomFunctionCallback(sqlite3_context *context, +static void sqliteCustomScalarFunctionCallback(sqlite3_context *context, int argc, sqlite3_value **argv) { JNIEnv* env = AndroidRuntime::getJNIEnv(); - - // Get the callback function object. - // Create a new local reference to it in case the callback tries to do something - // dumb like unregister the function (thereby destroying the global ref) while it is running. jobject functionObjGlobal = reinterpret_cast<jobject>(sqlite3_user_data(context)); - jobject functionObj = env->NewLocalRef(functionObjGlobal); - - jobjectArray argsArray = env->NewObjectArray(argc, gStringClassInfo.clazz, NULL); - if (argsArray) { - for (int i = 0; i < argc; i++) { - const jchar* arg = static_cast<const jchar*>(sqlite3_value_text16(argv[i])); - if (!arg) { - ALOGW("NULL argument in custom_function_callback. This should not happen."); - } else { - size_t argLen = sqlite3_value_bytes16(argv[i]) / sizeof(jchar); - jstring argStr = env->NewString(arg, argLen); - if (!argStr) { - goto error; // out of memory error - } - env->SetObjectArrayElement(argsArray, i, argStr); - env->DeleteLocalRef(argStr); - } - } + ScopedLocalRef<jobject> functionObj(env, env->NewLocalRef(functionObjGlobal)); + ScopedLocalRef<jstring> argString(env, + env->NewStringUTF(reinterpret_cast<const char*>(sqlite3_value_text(argv[0])))); + ScopedLocalRef<jstring> resString(env, + (jstring) env->CallObjectMethod(functionObj.get(), gUnaryOperator.apply, argString.get())); - // TODO: Support functions that return values. - env->CallVoidMethod(functionObj, - gSQLiteCustomFunctionClassInfo.dispatchCallback, argsArray); + if (env->ExceptionCheck()) { + ALOGE("Exception thrown by custom scalar function"); + sqlite3_result_error(context, "Exception thrown by custom scalar function", -1); + env->ExceptionDescribe(); + env->ExceptionClear(); + return; + } -error: - env->DeleteLocalRef(argsArray); + if (resString.get() == nullptr) { + sqlite3_result_null(context); + } else { + ScopedUtfChars res(env, resString.get()); + sqlite3_result_text(context, res.c_str(), -1, SQLITE_TRANSIENT); } +} + +static void sqliteCustomScalarFunctionDestructor(void* data) { + jobject functionObjGlobal = reinterpret_cast<jobject>(data); + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(functionObjGlobal); +} - env->DeleteLocalRef(functionObj); +static void nativeRegisterCustomScalarFunction(JNIEnv* env, jclass clazz, jlong connectionPtr, + jstring functionName, jobject functionObj) { + SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr); + + jobject functionObjGlobal = env->NewGlobalRef(functionObj); + ScopedUtfChars functionNameChars(env, functionName); + int err = sqlite3_create_function_v2(connection->db, + functionNameChars.c_str(), 1, SQLITE_UTF8, + reinterpret_cast<void*>(functionObjGlobal), + &sqliteCustomScalarFunctionCallback, + nullptr, + nullptr, + &sqliteCustomScalarFunctionDestructor); + + if (err != SQLITE_OK) { + ALOGE("sqlite3_create_function returned %d", err); + env->DeleteGlobalRef(functionObjGlobal); + throw_sqlite3_exception(env, connection->db); + return; + } +} + +static void sqliteCustomAggregateFunctionStep(sqlite3_context *context, + int argc, sqlite3_value **argv) { + char** agg = reinterpret_cast<char**>( + sqlite3_aggregate_context(context, sizeof(const char**))); + if (agg == nullptr) { + return; + } else if (*agg == nullptr) { + // During our first call the best we can do is allocate our result + // holder and populate it with our first value; we'll reduce it + // against any additional values in future calls + const char* res = reinterpret_cast<const char*>(sqlite3_value_text(argv[0])); + if (res == nullptr) { + *agg = nullptr; + } else { + *agg = strdup(res); + } + return; + } + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject functionObjGlobal = reinterpret_cast<jobject>(sqlite3_user_data(context)); + ScopedLocalRef<jobject> functionObj(env, env->NewLocalRef(functionObjGlobal)); + ScopedLocalRef<jstring> arg0String(env, + env->NewStringUTF(reinterpret_cast<const char*>(*agg))); + ScopedLocalRef<jstring> arg1String(env, + env->NewStringUTF(reinterpret_cast<const char*>(sqlite3_value_text(argv[0])))); + ScopedLocalRef<jstring> resString(env, + (jstring) env->CallObjectMethod(functionObj.get(), gBinaryOperator.apply, + arg0String.get(), arg1String.get())); if (env->ExceptionCheck()) { - ALOGE("An exception was thrown by custom SQLite function."); - LOGE_EX(env); + ALOGE("Exception thrown by custom aggregate function"); + sqlite3_result_error(context, "Exception thrown by custom aggregate function", -1); + env->ExceptionDescribe(); env->ExceptionClear(); + return; + } + + // One way or another, we have a new value to collect, and we need to + // free our previous value + if (*agg != nullptr) { + free(*agg); + } + if (resString.get() == nullptr) { + *agg = nullptr; + } else { + ScopedUtfChars res(env, resString.get()); + *agg = strdup(res.c_str()); + } +} + +static void sqliteCustomAggregateFunctionFinal(sqlite3_context *context) { + // We pass zero size here to avoid allocating for empty sets + char** agg = reinterpret_cast<char**>( + sqlite3_aggregate_context(context, 0)); + if (agg == nullptr) { + return; + } else if (*agg == nullptr) { + sqlite3_result_null(context); + } else { + sqlite3_result_text(context, *agg, -1, SQLITE_TRANSIENT); + free(*agg); } } -// Called when a custom function is destroyed. -static void sqliteCustomFunctionDestructor(void* data) { +static void sqliteCustomAggregateFunctionDestructor(void* data) { jobject functionObjGlobal = reinterpret_cast<jobject>(data); JNIEnv* env = AndroidRuntime::getJNIEnv(); env->DeleteGlobalRef(functionObjGlobal); } -static void nativeRegisterCustomFunction(JNIEnv* env, jclass clazz, jlong connectionPtr, - jobject functionObj) { +static void nativeRegisterCustomAggregateFunction(JNIEnv* env, jclass clazz, jlong connectionPtr, + jstring functionName, jobject functionObj) { SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr); - jstring nameStr = jstring(env->GetObjectField( - functionObj, gSQLiteCustomFunctionClassInfo.name)); - jint numArgs = env->GetIntField(functionObj, gSQLiteCustomFunctionClassInfo.numArgs); - jobject functionObjGlobal = env->NewGlobalRef(functionObj); - - const char* name = env->GetStringUTFChars(nameStr, NULL); - int err = sqlite3_create_function_v2(connection->db, name, numArgs, SQLITE_UTF16, + ScopedUtfChars functionNameChars(env, functionName); + int err = sqlite3_create_function_v2(connection->db, + functionNameChars.c_str(), 1, SQLITE_UTF8, reinterpret_cast<void*>(functionObjGlobal), - &sqliteCustomFunctionCallback, NULL, NULL, &sqliteCustomFunctionDestructor); - env->ReleaseStringUTFChars(nameStr, name); + nullptr, + &sqliteCustomAggregateFunctionStep, + &sqliteCustomAggregateFunctionFinal, + &sqliteCustomAggregateFunctionDestructor); if (err != SQLITE_OK) { ALOGE("sqlite3_create_function returned %d", err); @@ -812,8 +882,10 @@ static const JNINativeMethod sMethods[] = (void*)nativeOpen }, { "nativeClose", "(J)V", (void*)nativeClose }, - { "nativeRegisterCustomFunction", "(JLandroid/database/sqlite/SQLiteCustomFunction;)V", - (void*)nativeRegisterCustomFunction }, + { "nativeRegisterCustomScalarFunction", "(JLjava/lang/String;Ljava/util/function/UnaryOperator;)V", + (void*)nativeRegisterCustomScalarFunction }, + { "nativeRegisterCustomAggregateFunction", "(JLjava/lang/String;Ljava/util/function/BinaryOperator;)V", + (void*)nativeRegisterCustomAggregateFunction }, { "nativeRegisterLocalizedCollators", "(JLjava/lang/String;)V", (void*)nativeRegisterLocalizedCollators }, { "nativePrepareStatement", "(JLjava/lang/String;)J", @@ -864,15 +936,13 @@ static const JNINativeMethod sMethods[] = int register_android_database_SQLiteConnection(JNIEnv *env) { - jclass clazz = FindClassOrDie(env, "android/database/sqlite/SQLiteCustomFunction"); - - gSQLiteCustomFunctionClassInfo.name = GetFieldIDOrDie(env, clazz, "name", "Ljava/lang/String;"); - gSQLiteCustomFunctionClassInfo.numArgs = GetFieldIDOrDie(env, clazz, "numArgs", "I"); - gSQLiteCustomFunctionClassInfo.dispatchCallback = GetMethodIDOrDie(env, clazz, - "dispatchCallback", "([Ljava/lang/String;)V"); + jclass unaryClazz = FindClassOrDie(env, "java/util/function/UnaryOperator"); + gUnaryOperator.apply = GetMethodIDOrDie(env, unaryClazz, + "apply", "(Ljava/lang/Object;)Ljava/lang/Object;"); - clazz = FindClassOrDie(env, "java/lang/String"); - gStringClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); + jclass binaryClazz = FindClassOrDie(env, "java/util/function/BinaryOperator"); + gBinaryOperator.apply = GetMethodIDOrDie(env, binaryClazz, + "apply", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); return RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteConnection", sMethods, NELEM(sMethods)); diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index c979133d2493..041019ec4841 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -102,6 +102,47 @@ class AudioTrackJniStorage { } }; +class TunerConfigurationHelper { + JNIEnv *const mEnv; + jobject const mTunerConfiguration; + + struct Ids { + Ids(JNIEnv *env) + : mClass(FindClassOrDie(env, "android/media/AudioTrack$TunerConfiguration")), + mContentId(GetFieldIDOrDie(env, mClass, "mContentId", "I")), + mSyncId(GetFieldIDOrDie(env, mClass, "mSyncId", "I")) {} + const jclass mClass; + const jfieldID mContentId; + const jfieldID mSyncId; + }; + + static const Ids &getIds(JNIEnv *env) { + // Meyer's singleton, initializes first time control passes through + // declaration in a block and is thread-safe per ISO/IEC 14882:2011 6.7.4. + static Ids ids(env); + return ids; + } + +public: + TunerConfigurationHelper(JNIEnv *env, jobject tunerConfiguration) + : mEnv(env), mTunerConfiguration(tunerConfiguration) {} + + int32_t getContentId() const { + if (mEnv == nullptr || mTunerConfiguration == nullptr) return 0; + const Ids &ids = getIds(mEnv); + return (int32_t)mEnv->GetIntField(mTunerConfiguration, ids.mContentId); + } + + int32_t getSyncId() const { + if (mEnv == nullptr || mTunerConfiguration == nullptr) return 0; + const Ids &ids = getIds(mEnv); + return (int32_t)mEnv->GetIntField(mTunerConfiguration, ids.mSyncId); + } + + // optional check to confirm class and field ids can be found. + static void initCheckOrDie(JNIEnv *env) { (void)getIds(env); } +}; + static Mutex sLock; static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies; @@ -213,24 +254,36 @@ sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audio } // ---------------------------------------------------------------------------- -static jint -android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa, - jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask, - jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession, - jlong nativeAudioTrack, jboolean offload) { - +static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, + jobject jaa, jintArray jSampleRate, + jint channelPositionMask, jint channelIndexMask, + jint audioFormat, jint buffSizeInBytes, jint memoryMode, + jintArray jSession, jlong nativeAudioTrack, + jboolean offload, jint encapsulationMode, + jobject tunerConfiguration) { ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d," - " nativeAudioTrack=0x%" PRIX64 ", offload=%d", - jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes, - nativeAudioTrack, offload); - - sp<AudioTrack> lpTrack = 0; + " nativeAudioTrack=0x%" PRIX64 ", offload=%d encapsulationMode=%d tuner=%p", + jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes, + nativeAudioTrack, offload, encapsulationMode, tunerConfiguration); if (jSession == NULL) { ALOGE("Error creating AudioTrack: invalid session ID pointer"); return (jint) AUDIO_JAVA_ERROR; } + // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev. + if (tunerConfiguration != nullptr) { + const TunerConfigurationHelper tunerHelper(env, tunerConfiguration); + ALOGE("Error creating AudioTrack: unsupported tuner contentId:%d syncId:%d", + tunerHelper.getContentId(), tunerHelper.getSyncId()); + return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; + } + // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev. + if (encapsulationMode != 0 /* ENCAPSULATION_MODE_NONE */) { + ALOGE("Error creating AudioTrack: unsupported encapsulationMode %d", encapsulationMode); + return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; + } + jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); if (nSession == NULL) { ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); @@ -249,6 +302,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job } // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one. + sp<AudioTrack> lpTrack; if (nativeAudioTrack == 0) { if (jaa == 0) { ALOGE("Error creating AudioTrack: invalid audio attributes"); @@ -1304,82 +1358,75 @@ static void android_media_AudioTrack_set_delay_padding(JNIEnv *env, jobject thi // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { - // name, signature, funcPtr - {"native_is_direct_output_supported", - "(IIIIIII)Z", - (void *)android_media_AudioTrack_is_direct_output_supported}, - {"native_start", "()V", (void *)android_media_AudioTrack_start}, - {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, - {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, - {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, - {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I", - (void *)android_media_AudioTrack_setup}, - {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, - {"native_release", "()V", (void *)android_media_AudioTrack_release}, - {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>}, - {"native_write_native_bytes", - "(Ljava/nio/ByteBuffer;IIIZ)I", - (void *)android_media_AudioTrack_write_native_bytes}, - {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>}, - {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>}, - {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, - {"native_get_buffer_size_frames", - "()I", (void *)android_media_AudioTrack_get_buffer_size_frames}, - {"native_set_buffer_size_frames", - "(I)I", (void *)android_media_AudioTrack_set_buffer_size_frames}, - {"native_get_buffer_capacity_frames", - "()I", (void *)android_media_AudioTrack_get_buffer_capacity_frames}, - {"native_set_playback_rate", - "(I)I", (void *)android_media_AudioTrack_set_playback_rate}, - {"native_get_playback_rate", - "()I", (void *)android_media_AudioTrack_get_playback_rate}, - {"native_set_playback_params", - "(Landroid/media/PlaybackParams;)V", - (void *)android_media_AudioTrack_set_playback_params}, - {"native_get_playback_params", - "()Landroid/media/PlaybackParams;", - (void *)android_media_AudioTrack_get_playback_params}, - {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos}, - {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos}, - {"native_set_pos_update_period", - "(I)I", (void *)android_media_AudioTrack_set_pos_update_period}, - {"native_get_pos_update_period", - "()I", (void *)android_media_AudioTrack_get_pos_update_period}, - {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position}, - {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position}, - {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency}, - {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count}, - {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags}, - {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp}, - {"native_getMetrics", "()Landroid/os/PersistableBundle;", - (void *)android_media_AudioTrack_native_getMetrics}, - {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop}, - {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, - {"native_get_output_sample_rate", - "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate}, - {"native_get_min_buff_size", - "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, - {"native_setAuxEffectSendLevel", - "(F)I", (void *)android_media_AudioTrack_setAuxEffectSendLevel}, - {"native_attachAuxEffect", - "(I)I", (void *)android_media_AudioTrack_attachAuxEffect}, - {"native_setOutputDevice", "(I)Z", - (void *)android_media_AudioTrack_setOutputDevice}, - {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId}, - {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback}, - {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback}, - {"native_applyVolumeShaper", - "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I", - (void *)android_media_AudioTrack_apply_volume_shaper}, - {"native_getVolumeShaperState", - "(I)Landroid/media/VolumeShaper$State;", - (void *)android_media_AudioTrack_get_volume_shaper_state}, - {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation}, - {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id}, - {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding}, + // name, signature, funcPtr + {"native_is_direct_output_supported", "(IIIIIII)Z", + (void *)android_media_AudioTrack_is_direct_output_supported}, + {"native_start", "()V", (void *)android_media_AudioTrack_start}, + {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, + {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, + {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, + {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;)I", + (void *)android_media_AudioTrack_setup}, + {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, + {"native_release", "()V", (void *)android_media_AudioTrack_release}, + {"native_write_byte", "([BIIIZ)I", (void *)android_media_AudioTrack_writeArray<jbyteArray>}, + {"native_write_native_bytes", "(Ljava/nio/ByteBuffer;IIIZ)I", + (void *)android_media_AudioTrack_write_native_bytes}, + {"native_write_short", "([SIIIZ)I", + (void *)android_media_AudioTrack_writeArray<jshortArray>}, + {"native_write_float", "([FIIIZ)I", + (void *)android_media_AudioTrack_writeArray<jfloatArray>}, + {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume}, + {"native_get_buffer_size_frames", "()I", + (void *)android_media_AudioTrack_get_buffer_size_frames}, + {"native_set_buffer_size_frames", "(I)I", + (void *)android_media_AudioTrack_set_buffer_size_frames}, + {"native_get_buffer_capacity_frames", "()I", + (void *)android_media_AudioTrack_get_buffer_capacity_frames}, + {"native_set_playback_rate", "(I)I", (void *)android_media_AudioTrack_set_playback_rate}, + {"native_get_playback_rate", "()I", (void *)android_media_AudioTrack_get_playback_rate}, + {"native_set_playback_params", "(Landroid/media/PlaybackParams;)V", + (void *)android_media_AudioTrack_set_playback_params}, + {"native_get_playback_params", "()Landroid/media/PlaybackParams;", + (void *)android_media_AudioTrack_get_playback_params}, + {"native_set_marker_pos", "(I)I", (void *)android_media_AudioTrack_set_marker_pos}, + {"native_get_marker_pos", "()I", (void *)android_media_AudioTrack_get_marker_pos}, + {"native_set_pos_update_period", "(I)I", + (void *)android_media_AudioTrack_set_pos_update_period}, + {"native_get_pos_update_period", "()I", + (void *)android_media_AudioTrack_get_pos_update_period}, + {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position}, + {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position}, + {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency}, + {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count}, + {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags}, + {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp}, + {"native_getMetrics", "()Landroid/os/PersistableBundle;", + (void *)android_media_AudioTrack_native_getMetrics}, + {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop}, + {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload}, + {"native_get_output_sample_rate", "(I)I", + (void *)android_media_AudioTrack_get_output_sample_rate}, + {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioTrack_get_min_buff_size}, + {"native_setAuxEffectSendLevel", "(F)I", + (void *)android_media_AudioTrack_setAuxEffectSendLevel}, + {"native_attachAuxEffect", "(I)I", (void *)android_media_AudioTrack_attachAuxEffect}, + {"native_setOutputDevice", "(I)Z", (void *)android_media_AudioTrack_setOutputDevice}, + {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId}, + {"native_enableDeviceCallback", "()V", + (void *)android_media_AudioTrack_enableDeviceCallback}, + {"native_disableDeviceCallback", "()V", + (void *)android_media_AudioTrack_disableDeviceCallback}, + {"native_applyVolumeShaper", + "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I", + (void *)android_media_AudioTrack_apply_volume_shaper}, + {"native_getVolumeShaperState", "(I)Landroid/media/VolumeShaper$State;", + (void *)android_media_AudioTrack_get_volume_shaper_state}, + {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation}, + {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id}, + {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding}, }; - // field names found in android/media/AudioTrack.java #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" #define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj" @@ -1436,6 +1483,10 @@ int register_android_media_AudioTrack(JNIEnv *env) gPlaybackParamsFields.init(env); gVolumeShaperFields.init(env); + + // optional check that the TunerConfiguration class and fields exist. + TunerConfigurationHelper::initCheckOrDie(env); + return res; } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 573f378f57b1..50a60a917185 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -826,13 +826,6 @@ static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) { return static_cast<jint>(SurfaceComposerClient::getActiveConfig(token)); } -static jboolean nativeSetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj, jint id) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return JNI_FALSE; - status_t err = SurfaceComposerClient::setActiveConfig(token, static_cast<int>(id)); - return err == NO_ERROR ? JNI_TRUE : JNI_FALSE; -} - static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == NULL) return NULL; @@ -1400,8 +1393,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetDisplayConfigs }, {"nativeGetActiveConfig", "(Landroid/os/IBinder;)I", (void*)nativeGetActiveConfig }, - {"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z", - (void*)nativeSetActiveConfig }, {"nativeSetDesiredDisplayConfigSpecs", "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z", (void*)nativeSetDesiredDisplayConfigSpecs }, diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto index 0fca1d19c0e5..0ae11a106a54 100644 --- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto +++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto @@ -155,4 +155,5 @@ enum EventId { SET_AUTO_TIME_ZONE = 128; SET_PACKAGES_PROTECTED = 129; SET_FACTORY_RESET_PROTECTION = 130; + SET_COMMON_CRITERIA_MODE = 131; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index f8c51666d19a..718114244059 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -131,7 +131,6 @@ <protected-broadcast android:name="android.os.action.SETTING_RESTORED" /> - <protected-broadcast android:name="android.app.backup.intent.RUN" /> <protected-broadcast android:name="android.app.backup.intent.CLEAR" /> <protected-broadcast android:name="android.app.backup.intent.INIT" /> @@ -564,10 +563,12 @@ <protected-broadcast android:name="com.android.sync.SYNC_CONN_STATUS_CHANGED" /> - <protected-broadcast android:name="com.android.phone.SIP_INCOMING_CALL" /> + <protected-broadcast android:name="android.net.sip.action.SIP_INCOMING_CALL" /> <protected-broadcast android:name="com.android.phone.SIP_ADD_PHONE" /> - <protected-broadcast android:name="com.android.phone.SIP_REMOVE_PHONE" /> - <protected-broadcast android:name="com.android.phone.SIP_CALL_OPTION_CHANGED" /> + <protected-broadcast android:name="android.net.sip.action.SIP_REMOVE_PROFILE" /> + <protected-broadcast android:name="android.net.sip.action.SIP_SERVICE_UP" /> + <protected-broadcast android:name="android.net.sip.action.SIP_CALL_OPTION_CHANGED" /> + <protected-broadcast android:name="android.net.sip.action.START_SIP" /> <protected-broadcast android:name="android.bluetooth.adapter.action.BLE_ACL_CONNECTED" /> <protected-broadcast android:name="android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a78195b05d8f..2585197d85ec 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3545,6 +3545,12 @@ <!-- Whether the device supports quick settings and its associated APIs --> <bool name="config_quickSettingsSupported">true</bool> + <!-- Comma separated list of extra quick settings tiles to be added to the default set as + defined in SystemUi (com.android.systemui.R.string.quick_settings_tiles_default). + Custom tiles (TileService) must be specified as "custom(pkg_name/class_in_package)" + (without the quotes, both absolute and relative class works). --> + <string name="config_defaultExtraQuickSettingsTiles" translatable="false"></string> + <!-- The component name, flattened to a string, for the default autofill service to enabled for an user. This service must be trusted, as it can be activated without explicit consent of the user. If no autofill service with the diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 669b41e53ba1..379d0aa3da3d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3409,6 +3409,7 @@ <java-symbol type="string" name="etws_primary_default_message_others" /> <java-symbol type="bool" name="config_quickSettingsSupported" /> + <java-symbol type="string" name="config_defaultExtraQuickSettingsTiles" /> <java-symbol type="style" name="Theme.DeviceDefault.QuickSettings" /> diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java new file mode 100644 index 000000000000..c65ef9a56cd8 --- /dev/null +++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java @@ -0,0 +1,104 @@ +/* + * 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.accessibilityservice; + +import static org.mockito.Mockito.verify; + +import android.content.Intent; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.view.accessibility.AccessibilityEvent; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Unit tests for AccessibilityService. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AccessibilityServiceTest { + private static final String TAG = "AccessibilityServiceTest"; + private static final int CONNECTION_ID = 1; + + private static class AccessibilityServiceTestClass extends AccessibilityService { + private IAccessibilityServiceClient mCallback; + private Looper mLooper; + + AccessibilityServiceTestClass() { + super(); + attachBaseContext(InstrumentationRegistry.getContext()); + mLooper = InstrumentationRegistry.getContext().getMainLooper(); + } + + public void setupCallback(IAccessibilityServiceClient callback) { + mCallback = callback; + } + + public Looper getMainLooper() { + return mLooper; + } + + public void onAccessibilityEvent(AccessibilityEvent event) { } + public void onInterrupt() { } + + @Override + public void onSystemActionsChanged() { + try { + if (mCallback != null) mCallback.onSystemActionsChanged(); + } catch (RemoteException e) { + } + } + } + + private @Mock IAccessibilityServiceClient mMockClientForCallback; + private @Mock IAccessibilityServiceConnection mMockConnection; + private @Mock IBinder mMockIBinder; + private IAccessibilityServiceClient mServiceInterface; + private AccessibilityServiceTestClass mService; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + mService = new AccessibilityServiceTestClass(); + mService.setupCallback(mMockClientForCallback); + mServiceInterface = (IAccessibilityServiceClient) mService.onBind(new Intent()); + mServiceInterface.init(mMockConnection, CONNECTION_ID, mMockIBinder); + } + + @Test + public void testOnSystemActionsChanged() throws RemoteException { + mServiceInterface.onSystemActionsChanged(); + + verify(mMockClientForCallback).onSystemActionsChanged(); + } + + @Test + public void testGetSystemActions() throws RemoteException { + mService.getSystemActions(); + + verify(mMockConnection).getSystemActions(); + } +} diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java index 2091d556394d..9e4440a1d57d 100644 --- a/core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java +++ b/core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java @@ -40,14 +40,14 @@ public class AppSearchDocumentTest { @Test public void testDocumentEquals_Identical() { Document document1 = Document.newBuilder("uri1", "schemaType1") - .setCreationTimestampSecs(0L) + .setCreationTimestampMillis(0L) .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") .build(); Document document2 = Document.newBuilder("uri1", "schemaType1") - .setCreationTimestampSecs(0L) + .setCreationTimestampMillis(0L) .setProperty("longKey1", 1L, 2L, 3L) .setProperty("doubleKey1", 1.0, 2.0, 3.0) .setProperty("booleanKey1", true, false, true) @@ -60,7 +60,7 @@ public class AppSearchDocumentTest { @Test public void testDocumentEquals_DifferentOrder() { Document document1 = Document.newBuilder("uri1", "schemaType1") - .setCreationTimestampSecs(0L) + .setCreationTimestampMillis(0L) .setProperty("longKey1", 1L, 2L, 3L) .setProperty("doubleKey1", 1.0, 2.0, 3.0) .setProperty("booleanKey1", true, false, true) @@ -69,7 +69,7 @@ public class AppSearchDocumentTest { // Create second document with same parameter but different order. Document document2 = Document.newBuilder("uri1", "schemaType1") - .setCreationTimestampSecs(0L) + .setCreationTimestampMillis(0L) .setProperty("booleanKey1", true, false, true) .setProperty("stringKey1", "test-value1", "test-value2", "test-value3") .setProperty("doubleKey1", 1.0, 2.0, 3.0) @@ -182,7 +182,7 @@ public class AppSearchDocumentTest { public void testDocumentProtoPopulation() { Document document = Document.newBuilder("uri1", "schemaType1") .setScore(1) - .setCreationTimestampSecs(0) + .setCreationTimestampMillis(0) .setProperty("longKey1", 1L) .setProperty("doubleKey1", 1.0) .setProperty("booleanKey1", true) @@ -191,7 +191,7 @@ public class AppSearchDocumentTest { // Create the Document proto. Need to sort the property order by key. DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder() - .setUri("uri1").setSchema("schemaType1").setScore(1).setCreationTimestampSecs(0); + .setUri("uri1").setSchema("schemaType1").setScore(1).setCreationTimestampMs(0); HashMap<String, PropertyProto.Builder> propertyProtoMap = new HashMap<>(); propertyProtoMap.put("longKey1", PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L)); diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java index 0be52c180338..08ec2d0b1067 100644 --- a/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java +++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java @@ -21,14 +21,12 @@ import static com.google.common.truth.Truth.assertThat; import static org.testng.Assert.assertThrows; import static org.testng.Assert.expectThrows; -import android.app.appsearch.AppSearchSchema.IndexingConfig; import android.app.appsearch.AppSearchSchema.PropertyConfig; import androidx.test.filters.SmallTest; import com.google.android.icing.proto.IndexingConfig.TokenizerType; import com.google.android.icing.proto.PropertyConfigProto; -import com.google.android.icing.proto.SchemaProto; import com.google.android.icing.proto.SchemaTypeConfigProto; import com.google.android.icing.proto.TermMatchType; @@ -37,94 +35,87 @@ import org.junit.Test; @SmallTest public class AppSearchSchemaTest { @Test - public void testSuccess() { - AppSearchSchema schema = AppSearchSchema.newBuilder() - .addType(AppSearchSchema.newSchemaTypeBuilder("Email") - .addProperty(AppSearchSchema.newPropertyBuilder("subject") - .setDataType(PropertyConfig.DATA_TYPE_STRING) - .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder() - .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN) - .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX) - .build() - ).build() - ).addProperty(AppSearchSchema.newPropertyBuilder("body") - .setDataType(PropertyConfig.DATA_TYPE_STRING) - .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder() - .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN) - .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX) - .build() - ).build() - ).build() - - ).addType(AppSearchSchema.newSchemaTypeBuilder("MusicRecording") - .addProperty(AppSearchSchema.newPropertyBuilder("artist") - .setDataType(PropertyConfig.DATA_TYPE_STRING) - .setCardinality(PropertyConfig.CARDINALITY_REPEATED) - .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder() - .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN) - .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX) - .build() - ).build() - ).addProperty(AppSearchSchema.newPropertyBuilder("pubDate") - .setDataType(PropertyConfig.DATA_TYPE_INT64) - .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder() - .setTokenizerType(IndexingConfig.TOKENIZER_TYPE_NONE) - .setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_UNKNOWN) - .build() - ).build() - ).build() + public void testGetProto_Email() { + AppSearchSchema emailSchema = AppSearchSchema.newBuilder("Email") + .addProperty(AppSearchSchema.newPropertyBuilder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build() + ).addProperty(AppSearchSchema.newPropertyBuilder("body") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build() ).build(); - SchemaProto expectedProto = SchemaProto.newBuilder() - .addTypes(SchemaTypeConfigProto.newBuilder() - .setSchemaType("Email") - .addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("subject") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setIndexingConfig( - com.google.android.icing.proto.IndexingConfig.newBuilder() - .setTokenizerType(TokenizerType.Code.PLAIN) - .setTermMatchType(TermMatchType.Code.PREFIX) - ) - ).addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("body") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setIndexingConfig( - com.google.android.icing.proto.IndexingConfig.newBuilder() - .setTokenizerType(TokenizerType.Code.PLAIN) - .setTermMatchType(TermMatchType.Code.PREFIX) - ) + SchemaTypeConfigProto expectedEmailProto = SchemaTypeConfigProto.newBuilder() + .setSchemaType("Email") + .addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("subject") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setIndexingConfig( + com.google.android.icing.proto.IndexingConfig.newBuilder() + .setTokenizerType(TokenizerType.Code.PLAIN) + .setTermMatchType(TermMatchType.Code.PREFIX) ) + ).addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("body") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setIndexingConfig( + com.google.android.icing.proto.IndexingConfig.newBuilder() + .setTokenizerType(TokenizerType.Code.PLAIN) + .setTermMatchType(TermMatchType.Code.PREFIX) + ) + ).build(); + + assertThat(emailSchema.getProto()).isEqualTo(expectedEmailProto); + } + + @Test + public void testGetProto_MusicRecording() { + AppSearchSchema musicRecordingSchema = AppSearchSchema.newBuilder("MusicRecording") + .addProperty(AppSearchSchema.newPropertyBuilder("artist") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_REPEATED) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build() + ).addProperty(AppSearchSchema.newPropertyBuilder("pubDate") + .setDataType(PropertyConfig.DATA_TYPE_INT64) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_NONE) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_NONE) + .build() + ).build(); - ).addTypes(SchemaTypeConfigProto.newBuilder() - .setSchemaType("MusicRecording") - .addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("artist") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED) - .setIndexingConfig( - com.google.android.icing.proto.IndexingConfig.newBuilder() - .setTokenizerType(TokenizerType.Code.PLAIN) - .setTermMatchType(TermMatchType.Code.PREFIX) - ) - ).addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("pubDate") - .setDataType(PropertyConfigProto.DataType.Code.INT64) - .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setIndexingConfig( - com.google.android.icing.proto.IndexingConfig.newBuilder() - .setTokenizerType(TokenizerType.Code.NONE) - .setTermMatchType(TermMatchType.Code.UNKNOWN) - ) + SchemaTypeConfigProto expectedMusicRecordingProto = SchemaTypeConfigProto.newBuilder() + .setSchemaType("MusicRecording") + .addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("artist") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED) + .setIndexingConfig( + com.google.android.icing.proto.IndexingConfig.newBuilder() + .setTokenizerType(TokenizerType.Code.PLAIN) + .setTermMatchType(TermMatchType.Code.PREFIX) + ) + ).addProperties(PropertyConfigProto.newBuilder() + .setPropertyName("pubDate") + .setDataType(PropertyConfigProto.DataType.Code.INT64) + .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setIndexingConfig( + com.google.android.icing.proto.IndexingConfig.newBuilder() + .setTokenizerType(TokenizerType.Code.NONE) + .setTermMatchType(TermMatchType.Code.UNKNOWN) ) ).build(); - assertThat(schema.getProto()).isEqualTo(expectedProto); + assertThat(musicRecordingSchema.getProto()).isEqualTo(expectedMusicRecordingProto); } @Test @@ -151,4 +142,25 @@ public class AppSearchSchemaTest { builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED); builder.build(); } + + @Test + public void testDuplicateProperties() { + AppSearchSchema.Builder builder = AppSearchSchema.newBuilder("Email") + .addProperty(AppSearchSchema.newPropertyBuilder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build() + ).addProperty(AppSearchSchema.newPropertyBuilder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build() + ); + + Exception e = expectThrows(IllegalSchemaException.class, builder::build); + assertThat(e).hasMessageThat().contains("Property defined more than once: subject"); + } } diff --git a/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java b/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java new file mode 100644 index 000000000000..21259cc81758 --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java @@ -0,0 +1,66 @@ +/* + * 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.app.appsearch; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + +import androidx.test.filters.SmallTest; + +import com.google.android.icing.proto.DocumentProto; +import com.google.android.icing.proto.SearchResultProto; + +import org.junit.Test; + +@SmallTest +public class SearchResultsTest { + + @Test + public void testSearchResultsEqual() { + final String uri = "testUri"; + final String schemaType = "testSchema"; + SearchResultProto.ResultProto result1 = SearchResultProto.ResultProto.newBuilder() + .setDocument(DocumentProto.newBuilder() + .setUri(uri) + .setSchema(schemaType) + .build()) + .build(); + SearchResultProto searchResults1 = SearchResultProto.newBuilder() + .addResults(result1) + .build(); + SearchResults res1 = new SearchResults(searchResults1); + SearchResultProto.ResultProto result2 = SearchResultProto.ResultProto.newBuilder() + .setDocument(DocumentProto.newBuilder() + .setUri(uri) + .setSchema(schemaType) + .build()) + .build(); + SearchResultProto searchResults2 = SearchResultProto.newBuilder() + .addResults(result2) + .build(); + SearchResults res2 = new SearchResults(searchResults2); + assertThat(res1.toString()).isEqualTo(res2.toString()); + } + + @Test + public void buildSearchSpecWithoutTermMatchType() { + assertThrows(RuntimeException.class, () -> SearchSpec.newBuilder() + .setSchemaTypes("testSchemaType") + .build()); + } +} diff --git a/core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java index 4ee4aa6d527f..c5986bb2f07b 100644 --- a/core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java +++ b/core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java @@ -36,7 +36,7 @@ public class CustomerDocumentTest { public void testBuildCustomerDocument() { CustomerDocument customerDocument = CustomerDocument.newBuilder("uri1") .setScore(1) - .setCreationTimestampSecs(0) + .setCreationTimestampMillis(0) .setProperty("longKey1", 1L, 2L, 3L) .setProperty("doubleKey1", 1.0, 2.0, 3.0) .setProperty("booleanKey1", true, false, true) @@ -46,7 +46,7 @@ public class CustomerDocumentTest { assertThat(customerDocument.getUri()).isEqualTo("uri1"); assertThat(customerDocument.getSchemaType()).isEqualTo("customerDocument"); assertThat(customerDocument.getScore()).isEqualTo(1); - assertThat(customerDocument.getCreationTimestampSecs()).isEqualTo(0L); + assertThat(customerDocument.getCreationTimestampMillis()).isEqualTo(0L); assertThat(customerDocument.getPropertyLongArray("longKey1")).asList() .containsExactly(1L, 2L, 3L); assertThat(customerDocument.getPropertyDoubleArray("doubleKey1")).usingExactEquality() diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java index 1410f4f1bf72..09ea1b1865c0 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java @@ -124,7 +124,7 @@ public class TransactionExecutorTests { assertArrayEquals(new int[] {}, path(ON_START)); assertArrayEquals(new int[] {ON_RESUME}, path(ON_RESUME)); assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE}, path(ON_PAUSE)); - assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP}, path(ON_STOP)); + assertArrayEquals(new int[] {ON_STOP}, path(ON_STOP)); assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}, path(ON_DESTROY)); } @@ -362,7 +362,9 @@ public class TransactionExecutorTests { public void testClosestStateResolutionFromOnStart() { mClientRecord.setState(ON_START); assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( - new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}))); + new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_DESTROY}))); + assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( + new int[] {ON_STOP}))); assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray( new int[] {ON_CREATE}))); } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java index e23c51e66a02..8e2490789a6f 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java @@ -46,6 +46,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.ArrayList; @@ -193,4 +194,15 @@ public class AccessibilityManagerTest { } }); } + + @Test + public void testSetWindowMagnificationConnection() throws Exception { + AccessibilityManager manager = createManager(WITH_A11Y_ENABLED); + IWindowMagnificationConnection connection = Mockito.mock( + IWindowMagnificationConnection.class); + + manager.setWindowMagnificationConnection(connection); + + verify(mMockService).setWindowMagnificationConnection(connection); + } } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index f151b810eea3..8b8e9ea4c6ee 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -25,6 +25,9 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteCallback; +import java.util.Collections; +import java.util.List; + /** * Stub implementation of IAccessibilityServiceConnection so each test doesn't need to implement * all of the methods @@ -85,6 +88,10 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon return false; } + public List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions() { + return Collections.emptyList(); + } + public void disableSelf() {} public void setOnKeyEventResult(boolean handled, int sequence) {} diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 84549e8ce6e4..3c402e996218 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -38,7 +38,7 @@ #include <SkCanvas.h> #include <SkImagePriv.h> - +#include <SkWebpEncoder.h> #include <SkHighContrastFilter.h> #include <limits> @@ -471,4 +471,59 @@ BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, return BitmapPalette::Unknown; } +Bitmap::CompressResult Bitmap::compress(JavaCompressFormat format, int32_t quality, + SkWStream* stream) { + SkBitmap skbitmap; + getSkBitmap(&skbitmap); + return compress(skbitmap, format, quality, stream); +} + +Bitmap::CompressResult Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format, + int32_t quality, SkWStream* stream) { + SkBitmap skbitmap = bitmap; + if (skbitmap.colorType() == kRGBA_F16_SkColorType) { + // Convert to P3 before encoding. This matches + // SkAndroidCodec::computeOutputColorSpace for wide gamuts. Now that F16 + // could already be P3, we still want to convert to 8888. + auto cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); + auto info = skbitmap.info().makeColorType(kRGBA_8888_SkColorType) + .makeColorSpace(std::move(cs)); + SkBitmap p3; + if (!p3.tryAllocPixels(info)) { + return CompressResult::AllocationFailed; + } + + SkPixmap pm; + SkAssertResult(p3.peekPixels(&pm)); // should always work if tryAllocPixels() did. + if (!skbitmap.readPixels(pm)) { + return CompressResult::Error; + } + skbitmap = p3; + } + + SkEncodedImageFormat fm; + switch (format) { + case JavaCompressFormat::Jpeg: + fm = SkEncodedImageFormat::kJPEG; + break; + case JavaCompressFormat::Png: + fm = SkEncodedImageFormat::kPNG; + break; + case JavaCompressFormat::Webp: + fm = SkEncodedImageFormat::kWEBP; + break; + case JavaCompressFormat::WebpLossy: + case JavaCompressFormat::WebpLossless: { + SkWebpEncoder::Options options; + options.fQuality = quality; + options.fCompression = format == JavaCompressFormat::WebpLossy ? + SkWebpEncoder::Compression::kLossy : SkWebpEncoder::Compression::kLossless; + return SkWebpEncoder::Encode(stream, skbitmap.pixmap(), options) + ? CompressResult::Success : CompressResult::Error; + } + } + + return SkEncodeImage(stream, skbitmap, fm, quality) + ? CompressResult::Success : CompressResult::Error; +} } // namespace android diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 1cda0465ae64..ee365af2f7be 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -27,6 +27,8 @@ #include <android/hardware_buffer.h> #endif +class SkWStream; + namespace android { enum class PixelStorageType { @@ -142,6 +144,26 @@ public: // and places that value in size. static bool computeAllocationSize(size_t rowBytes, int height, size_t* size); + // These must match the int values of CompressFormat in Bitmap.java, as well as + // AndroidBitmapCompressFormat. + enum class JavaCompressFormat { + Jpeg = 0, + Png = 1, + Webp = 2, + WebpLossy = 3, + WebpLossless = 4, + }; + + enum class CompressResult { + Success, + AllocationFailed, + Error, + }; + + CompressResult compress(JavaCompressFormat format, int32_t quality, SkWStream* stream); + + static CompressResult compress(const SkBitmap& bitmap, JavaCompressFormat format, + int32_t quality, SkWStream* stream); private: static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index 4f2027d978a1..a6c4e9db7280 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -30,19 +30,25 @@ ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChu , mTargetSize(mCodec->getInfo().dimensions()) , mDecodeSize(mTargetSize) , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType)) - , mOutAlphaType(mCodec->getInfo().isOpaque() ? - kOpaque_SkAlphaType : kPremul_SkAlphaType) + , mUnpremultipliedRequired(false) , mOutColorSpace(mCodec->getInfo().refColorSpace()) , mSampleSize(1) { } +SkAlphaType ImageDecoder::getOutAlphaType() const { + // While an SkBitmap may want to use kOpaque_SkAlphaType for a performance + // optimization, this class just outputs raw pixels. Using either + // premultiplication choice has no effect on decoding an opaque encoded image. + return mUnpremultipliedRequired ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; +} + bool ImageDecoder::setTargetSize(int width, int height) { if (width <= 0 || height <= 0) { return false; } - auto info = SkImageInfo::Make(width, height, mOutColorType, mOutAlphaType); + auto info = SkImageInfo::Make(width, height, mOutColorType, getOutAlphaType()); size_t rowBytes = info.minRowBytes(); if (rowBytes == 0) { // This would have overflowed. @@ -63,7 +69,7 @@ bool ImageDecoder::setTargetSize(int width, int height) { SkISize targetSize = { width, height }, decodeSize = targetSize; int sampleSize = mCodec->computeSampleSize(&decodeSize); - if (decodeSize != targetSize && mOutAlphaType == kUnpremul_SkAlphaType + if (decodeSize != targetSize && mUnpremultipliedRequired && !mCodec->getInfo().isOpaque()) { return false; } @@ -119,29 +125,11 @@ bool ImageDecoder::setOutColorType(SkColorType colorType) { return true; } -bool ImageDecoder::setOutAlphaType(SkAlphaType alpha) { - switch (alpha) { - case kOpaque_SkAlphaType: - return opaque(); - case kPremul_SkAlphaType: - if (opaque()) { - // Opaque can be treated as premul. - return true; - } - break; - case kUnpremul_SkAlphaType: - if (opaque()) { - // Opaque can be treated as unpremul. - return true; - } - if (mDecodeSize != mTargetSize) { - return false; - } - break; - default: - return false; +bool ImageDecoder::setUnpremultipliedRequired(bool required) { + if (required && !opaque() && mDecodeSize != mTargetSize) { + return false; } - mOutAlphaType = alpha; + mUnpremultipliedRequired = required; return true; } @@ -151,11 +139,11 @@ void ImageDecoder::setOutColorSpace(sk_sp<SkColorSpace> colorSpace) { SkImageInfo ImageDecoder::getOutputInfo() const { SkISize size = mCropRect ? mCropRect->size() : mTargetSize; - return SkImageInfo::Make(size, mOutColorType, mOutAlphaType, mOutColorSpace); + return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), mOutColorSpace); } bool ImageDecoder::opaque() const { - return mOutAlphaType == kOpaque_SkAlphaType; + return mCodec->getInfo().alphaType() == kOpaque_SkAlphaType; } bool ImageDecoder::gray() const { @@ -165,7 +153,8 @@ bool ImageDecoder::gray() const { SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { void* decodePixels = pixels; size_t decodeRowBytes = rowBytes; - auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, mOutAlphaType, mOutColorSpace); + auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(), + mOutColorSpace); // Used if we need a temporary before scaling or subsetting. // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380. SkBitmap tmp; diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h index b956f4a77793..96f97e5421f3 100644 --- a/libs/hwui/hwui/ImageDecoder.h +++ b/libs/hwui/hwui/ImageDecoder.h @@ -41,14 +41,12 @@ public: bool setOutColorType(SkColorType outColorType); - bool setOutAlphaType(SkAlphaType outAlphaType); + bool setUnpremultipliedRequired(bool unpremultipliedRequired); void setOutColorSpace(sk_sp<SkColorSpace> cs); // The size is the final size after scaling and cropping. SkImageInfo getOutputInfo() const; - SkColorType getOutColorType() const { return mOutColorType; } - SkAlphaType getOutAlphaType() const { return mOutAlphaType; } bool opaque() const; bool gray() const; @@ -59,13 +57,15 @@ private: SkISize mTargetSize; SkISize mDecodeSize; SkColorType mOutColorType; - SkAlphaType mOutAlphaType; + bool mUnpremultipliedRequired; sk_sp<SkColorSpace> mOutColorSpace; int mSampleSize; std::optional<SkIRect> mCropRect; ImageDecoder(const ImageDecoder&) = delete; ImageDecoder& operator=(const ImageDecoder&) = delete; + + SkAlphaType getOutAlphaType() const; }; } // namespace android diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp index 2c78b024536b..551bdc63121d 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp @@ -24,8 +24,8 @@ namespace android { namespace uirenderer { namespace skiapipeline { -// When purgeable is INVALID_TIME it won't be logged at all. -#define INVALID_TIME -1 +// When purgeable is INVALID_MEMORY_SIZE it won't be logged at all. +#define INVALID_MEMORY_SIZE -1 /** * Skia invokes the following SkTraceMemoryDump functions: @@ -42,10 +42,10 @@ namespace skiapipeline { * "GPU Memory" category. */ static std::unordered_map<const char*, const char*> sResourceMap = { - {"malloc", "Graphics CPU Memory"}, // taken from setMemoryBacking(backingType) - {"gl_texture", "Graphics Texture Memory"}, // taken from setMemoryBacking(backingType) + {"malloc", "HWUI CPU Memory"}, // taken from setMemoryBacking(backingType) + {"gl_texture", "HWUI Texture Memory"}, // taken from setMemoryBacking(backingType) {"Texture", - "Graphics Texture Memory"}, // taken from dumpStringValue(value, valueName="type") + "HWUI Texture Memory"}, // taken from dumpStringValue(value, valueName="type") // Uncomment categories below to split "GPU Memory" into more brackets for debugging. /*{"vk_buffer", "vk_buffer"}, {"gl_renderbuffer", "gl_renderbuffer"}, @@ -105,10 +105,10 @@ void ATraceMemoryDump::startFrame() { // Once a category is observed in at least one frame, it is always reported in subsequent // frames (even if it is 0). Not logging a category to ATRACE would mean its value has not // changed since the previous frame, which is not what we want. - it.second.time = 0; - // If purgeableTime is INVALID_TIME, then logTraces won't log it at all. - if (it.second.purgeableTime != INVALID_TIME) { - it.second.purgeableTime = 0; + it.second.memory = 0; + // If purgeableMemory is INVALID_MEMORY_SIZE, then logTraces won't log it at all. + if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) { + it.second.purgeableMemory = 0; } } } @@ -119,12 +119,15 @@ void ATraceMemoryDump::startFrame() { void ATraceMemoryDump::logTraces() { // Accumulate data from last dumpName recordAndResetCountersIfNeeded(""); + uint64_t hwui_all_frame_memory = 0; for (auto& it : mCurrentValues) { - ATRACE_INT64(it.first.c_str(), it.second.time); - if (it.second.purgeableTime != INVALID_TIME) { - ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableTime); + hwui_all_frame_memory += it.second.memory; + ATRACE_INT64(it.first.c_str(), it.second.memory); + if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) { + ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableMemory); } } + ATRACE_INT64("HWUI All Memory", hwui_all_frame_memory); } /** @@ -145,12 +148,12 @@ void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) { // A new dumpName observed -> store the data already collected. auto memoryCounter = mCurrentValues.find(mCategory); if (memoryCounter != mCurrentValues.end()) { - memoryCounter->second.time += mLastDumpValue; - if (mLastPurgeableDumpValue != INVALID_TIME) { - if (memoryCounter->second.purgeableTime == INVALID_TIME) { - memoryCounter->second.purgeableTime = mLastPurgeableDumpValue; + memoryCounter->second.memory += mLastDumpValue; + if (mLastPurgeableDumpValue != INVALID_MEMORY_SIZE) { + if (memoryCounter->second.purgeableMemory == INVALID_MEMORY_SIZE) { + memoryCounter->second.purgeableMemory = mLastPurgeableDumpValue; } else { - memoryCounter->second.purgeableTime += mLastPurgeableDumpValue; + memoryCounter->second.purgeableMemory += mLastPurgeableDumpValue; } } } else { @@ -164,10 +167,10 @@ void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) { void ATraceMemoryDump::resetCurrentCounter(const char* dumpName) { mLastDumpValue = 0; - mLastPurgeableDumpValue = INVALID_TIME; + mLastPurgeableDumpValue = INVALID_MEMORY_SIZE; mLastDumpName = dumpName; // Categories not listed in sResourceMap are reported as "GPU memory" - mCategory = "GPU Memory"; + mCategory = "HWUI GPU Memory"; } } /* namespace skiapipeline */ diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h index aa5c401ad1ca..4592711dd5b5 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.h +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h @@ -62,8 +62,8 @@ private: std::string mCategory; struct TraceValue { - uint64_t time; - uint64_t purgeableTime; + uint64_t memory; + uint64_t purgeableMemory; }; // keys are define in sResourceMap diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 0ced68ef8695..4dbc79b54199 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -213,6 +213,36 @@ public class AudioTrack extends PlayerBase private final static String TAG = "android.media.AudioTrack"; + /** @hide */ + @IntDef({ + ENCAPSULATION_MODE_NONE, + ENCAPSULATION_MODE_ELEMENTARY_STREAM, + ENCAPSULATION_MODE_HANDLE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EncapsulationMode {} + + // Important: The ENCAPSULATION_MODE values must be kept in sync with native header files. + /** + * This mode indicates no metadata encapsulation, + * which is the default mode for sending audio data + * through {@code AudioTrack}. + */ + public static final int ENCAPSULATION_MODE_NONE = 0; + /** + * This mode indicates metadata encapsulation with an elementary stream payload. + * Both compressed and PCM format is allowed. + * + * TODO(b/147778408) Link: See the Android developers guide for more information. + */ + public static final int ENCAPSULATION_MODE_ELEMENTARY_STREAM = 1; + /** + * This mode indicates metadata encapsulation with a handle payload. + * The handle is a 64 bit long, provided by the Tuner API. + * + * TODO(b/147778408) Link: Fill in Tuner API to obtain the handle. + */ + public static final int ENCAPSULATION_MODE_HANDLE = 2; /** @hide */ @IntDef({ @@ -592,11 +622,13 @@ public class AudioTrack extends PlayerBase public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int mode, int sessionId) throws IllegalArgumentException { - this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/); + this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/, + ENCAPSULATION_MODE_NONE, null /* tunerConfiguration */); } private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, - int mode, int sessionId, boolean offload) + int mode, int sessionId, boolean offload, int encapsulationMode, + @Nullable TunerConfiguration tunerConfiguration) throws IllegalArgumentException { super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK); // mState already == STATE_UNINITIALIZED @@ -663,7 +695,7 @@ public class AudioTrack extends PlayerBase int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes, sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/, - offload); + offload, encapsulationMode, tunerConfiguration); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -672,6 +704,8 @@ public class AudioTrack extends PlayerBase mSampleRate = sampleRate[0]; mSessionId = session[0]; + // TODO: consider caching encapsulationMode and tunerConfiguration in the Java object. + if ((mAttributes.getFlags() & AudioAttributes.FLAG_HW_AV_SYNC) != 0) { int frameSizeInBytes; if (AudioFormat.isEncodingLinearFrames(mAudioFormat)) { @@ -745,7 +779,9 @@ public class AudioTrack extends PlayerBase 0 /*mDataLoadMode - NA*/, session, nativeTrackInJavaObj, - false /*offload*/); + false /*offload*/, + ENCAPSULATION_MODE_NONE, + null /* tunerConfiguration */); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -758,6 +794,99 @@ public class AudioTrack extends PlayerBase } /** + * TunerConfiguration is used to convey tuner information + * from the android.media.tv.Tuner API to AudioTrack construction. + * + * Use the Builder to construct the TunerConfiguration object, + * which is then used by the {@link AudioTrack.Builder} to create an AudioTrack. + */ + public static class TunerConfiguration { + private final int mContentId; + private final int mSyncId; + + private TunerConfiguration(int contentId, int syncId) { + mContentId = contentId; + mSyncId = syncId; + } + + /** + * Returns the contentId. + */ + public int getContentId() { + return mContentId; + } + + /** + * Returns the syncId. + */ + public int getSyncId() { + return mSyncId; + } + + /** + * Builder class for {@link AudioTrack.TunerConfiguration} objects. + */ + public static class Builder { + private int mContentId; + private int mSyncId; + + /** + * Sets the contentId from the Tuner filter. + * + * @param contentId selects the audio stream to use. + * See android.media.tv.tuner.filter.Filter#getId(). + * This is always a positive number. + * TODO(b/147778408) Link to tuner filter doc when unhidden. + * @return the same Builder instance. + */ + public @NonNull Builder setContentId(@IntRange(from = 1) int contentId) { + if (contentId < 1) { + throw new IllegalArgumentException( + "contentId " + contentId + " must be positive"); + } + mContentId = contentId; + return this; + } + + /** + * Sets the syncId from the Tuner filter. + * + * @param syncId selects the clock to use for synchronization + * of audio with other streams such as video. + * See android.media.tv.tuner.Tuner#getAvSyncHwId(). + * This is always a positive number. + * TODO(b/147778408) Link to tuner filter doc when unhidden. + * @return the same Builder instance. + */ + public @NonNull Builder setSyncId(@IntRange(from = 1) int syncId) { + if (syncId < 1) { + throw new IllegalArgumentException("syncId " + syncId + " must be positive"); + } + mSyncId = syncId; + return this; + } + + /** + * Builds a {@link AudioTrack.TunerConfiguration} instance initialized with + * the parameters set on this {@code Builder}. + * + * @return a new successfully initialized {@link AudioTrack.TunerConfiguration}. + * @throws UnsupportedOperationException if the parameters set on the + * {@code Builder} are incompatible. + */ + public @NonNull TunerConfiguration build() { + if (mContentId < 1 || mSyncId < 1) { + throw new UnsupportedOperationException( + "contentId " + mContentId + + " syncId " + mSyncId + + " must be set"); + } + return new TunerConfiguration(mContentId, mSyncId); + } + } + } + + /** * Builder class for {@link AudioTrack} objects. * Use this class to configure and create an <code>AudioTrack</code> instance. By setting audio * attributes and audio format parameters, you indicate which of those vary from the default @@ -799,10 +928,12 @@ public class AudioTrack extends PlayerBase private AudioAttributes mAttributes; private AudioFormat mFormat; private int mBufferSizeInBytes; + private int mEncapsulationMode = ENCAPSULATION_MODE_NONE; private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE; private int mMode = MODE_STREAM; private int mPerformanceMode = PERFORMANCE_MODE_NONE; private boolean mOffload = false; + private TunerConfiguration mTunerConfiguration; /** * Constructs a new Builder with the default values as described above. @@ -869,6 +1000,34 @@ public class AudioTrack extends PlayerBase } /** + * Sets the encapsulation mode. + * + * Encapsulation mode allows metadata to be sent together with + * the audio data payload in a {@code ByteBuffer}. + * The data format is specified in the Android developers site. + * + * TODO(b/147778408) Link to doc page. + * + * @param encapsulationMode one of {@link AudioTrack#ENCAPSULATION_MODE_NONE}, + * {@link AudioTrack#ENCAPSULATION_MODE_ELEMENTARY_STREAM}, + * {@link AudioTrack#ENCAPSULATION_MODE_HANDLE}. + * @return the same Builder instance. + */ + public @NonNull Builder setEncapsulationMode(@EncapsulationMode int encapsulationMode) { + switch (encapsulationMode) { + case ENCAPSULATION_MODE_NONE: + case ENCAPSULATION_MODE_ELEMENTARY_STREAM: + case ENCAPSULATION_MODE_HANDLE: + mEncapsulationMode = encapsulationMode; + break; + default: + throw new IllegalArgumentException( + "Invalid encapsulation mode " + encapsulationMode); + } + return this; + } + + /** * Sets the mode under which buffers of audio data are transferred from the * {@link AudioTrack} to the framework. * @param mode one of {@link AudioTrack#MODE_STREAM}, {@link AudioTrack#MODE_STATIC}. @@ -949,6 +1108,25 @@ public class AudioTrack extends PlayerBase } /** + * Sets the tuner configuration for the {@code AudioTrack}. + * + * The {@link AudioTrack.TunerConfiguration} consists of parameters obtained from + * the Android TV tuner API which indicate the audio content stream id and the + * synchronization id for the {@code AudioTrack}. + * + * @param tunerConfiguration obtained by {@link AudioTrack.TunerConfiguration.Builder}. + * @return the same Builder instance. + */ + public @NonNull Builder setTunerConfiguration( + @NonNull TunerConfiguration tunerConfiguration) { + if (tunerConfiguration == null) { + throw new IllegalArgumentException("tunerConfiguration is null"); + } + mTunerConfiguration = tunerConfiguration; + return this; + } + + /** * Builds an {@link AudioTrack} instance initialized with all the parameters set * on this <code>Builder</code>. * @return a new successfully initialized {@link AudioTrack} instance. @@ -1003,6 +1181,8 @@ public class AudioTrack extends PlayerBase } } + // TODO: Check mEncapsulationMode compatibility with MODE_STATIC, etc? + try { // If the buffer size is not specified in streaming mode, // use a single frame for the buffer size and let the @@ -1012,7 +1192,8 @@ public class AudioTrack extends PlayerBase * mFormat.getBytesPerSample(mFormat.getEncoding()); } final AudioTrack track = new AudioTrack( - mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload); + mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload, + mEncapsulationMode, mTunerConfiguration); if (track.getState() == STATE_UNINITIALIZED) { // release is not necessary throw new UnsupportedOperationException("Cannot create AudioTrack"); @@ -3595,7 +3776,7 @@ public class AudioTrack extends PlayerBase Object /*AudioAttributes*/ attributes, int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat, int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack, - boolean offload); + boolean offload, int encapsulationMode, Object tunerConfiguration); private native final void native_finalize(); diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java index e4bab7466a70..f89893136804 100644 --- a/media/java/android/media/CamcorderProfile.java +++ b/media/java/android/media/CamcorderProfile.java @@ -99,25 +99,21 @@ public class CamcorderProfile /** * Quality level corresponding to the VGA (640 x 480) resolution. - * @hide */ public static final int QUALITY_VGA = 9; /** * Quality level corresponding to 4k-DCI (4096 x 2160) resolution. - * @hide */ public static final int QUALITY_4KDCI = 10; /** * Quality level corresponding to QHD (2560 x 1440) resolution - * @hide */ public static final int QUALITY_QHD = 11; /** * Quality level corresponding to 2K (2048 x 1080) resolution - * @hide */ public static final int QUALITY_2K = 12; @@ -172,25 +168,21 @@ public class CamcorderProfile /** * Time lapse quality level corresponding to the VGA (640 x 480) resolution. - * @hide */ public static final int QUALITY_TIME_LAPSE_VGA = 1009; /** * Time lapse quality level corresponding to the 4k-DCI (4096 x 2160) resolution. - * @hide */ public static final int QUALITY_TIME_LAPSE_4KDCI = 1010; /** * Time lapse quality level corresponding to the QHD (2560 x 1440) resolution. - * @hide */ public static final int QUALITY_TIME_LAPSE_QHD = 1011; /** * Time lapse quality level corresponding to the 2K (2048 x 1080) resolution. - * @hide */ public static final int QUALITY_TIME_LAPSE_2K = 1012; @@ -255,19 +247,16 @@ public class CamcorderProfile /** * High speed ( >= 100fps) quality level corresponding to the CIF (352 x 288) - * @hide */ public static final int QUALITY_HIGH_SPEED_CIF = 2006; /** * High speed ( >= 100fps) quality level corresponding to the VGA (640 x 480) - * @hide */ public static final int QUALITY_HIGH_SPEED_VGA = 2007; /** * High speed ( >= 100fps) quality level corresponding to the 4K-DCI (4096 x 2160) - * @hide */ public static final int QUALITY_HIGH_SPEED_4KDCI = 2008; diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl index 5dd0b1c915bd..a25aff6611ca 100644 --- a/media/java/android/media/IMediaRoute2Provider.aidl +++ b/media/java/android/media/IMediaRoute2Provider.aidl @@ -18,14 +18,15 @@ package android.media; import android.content.Intent; import android.media.IMediaRoute2ProviderClient; +import android.os.Bundle; /** * {@hide} */ oneway interface IMediaRoute2Provider { void setClient(IMediaRoute2ProviderClient client); - void requestCreateSession(String packageName, String routeId, - String routeFeature, long requestId); + void requestCreateSession(String packageName, String routeId, long requestId, + in @nullable Bundle sessionHints); void releaseSession(String sessionId); void selectRoute(String sessionId, String routeId); diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl index e9add1756224..ffad6592e902 100644 --- a/media/java/android/media/IMediaRouter2Manager.aidl +++ b/media/java/android/media/IMediaRouter2Manager.aidl @@ -18,12 +18,14 @@ package android.media; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2Info; +import android.media.RoutingSessionInfo; /** * {@hide} */ oneway interface IMediaRouter2Manager { - void notifyRouteSelected(String packageName, in MediaRoute2Info route); + void notifySessionCreated(in RoutingSessionInfo sessionInfo); + void notifySessionsUpdated(); void notifyPreferredFeaturesChanged(String packageName, in List<String> preferredFeatures); void notifyRoutesAdded(in List<MediaRoute2Info> routes); void notifyRoutesRemoved(in List<MediaRoute2Info> routes); diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index 3cdaa0794b31..281e7c6b6f68 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -24,6 +24,7 @@ import android.media.MediaRoute2Info; import android.media.MediaRouterClientState; import android.media.RouteDiscoveryPreference; import android.media.RoutingSessionInfo; +import android.os.Bundle; /** * {@hide} @@ -47,12 +48,13 @@ interface IMediaRouterService { List<MediaRoute2Info> getSystemRoutes(); void registerClient2(IMediaRouter2Client client, String packageName); void unregisterClient2(IMediaRouter2Client client); - void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request); + void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, + in Intent request); void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume); void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction); - void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, - String routeFeature, int requestId); + void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId, + in @nullable Bundle sessionHints); void setDiscoveryRequest2(IMediaRouter2Client client, in RouteDiscoveryPreference preference); void selectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route); void deselectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route); @@ -71,4 +73,12 @@ interface IMediaRouterService { in MediaRoute2Info route, int direction); List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager); + void selectClientRoute(IMediaRouter2Manager manager, + String sessionId, in MediaRoute2Info route); + void deselectClientRoute(IMediaRouter2Manager manager, + String sessionId, in MediaRoute2Info route); + void transferToClientRoute(IMediaRouter2Manager manager, + String sessionId, in MediaRoute2Info route); + void releaseClientSession(IMediaRouter2Manager manager, String sessionId); + } diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index 6d9aea512f01..6bfa851804a1 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -161,8 +161,8 @@ public abstract class MediaRoute2ProviderService extends Service { * @param sessionInfo information of the new session. * The {@link RoutingSessionInfo#getId() id} of the session must be unique. * @param requestId id of the previous request to create this session provided in - * {@link #onCreateSession(String, String, String, long)} - * @see #onCreateSession(String, String, String, long) + * {@link #onCreateSession(String, String, long, Bundle)} + * @see #onCreateSession(String, String, long, Bundle) * @hide */ public final void notifySessionCreated(@NonNull RoutingSessionInfo sessionInfo, @@ -177,7 +177,6 @@ public abstract class MediaRoute2ProviderService extends Service { } mSessionInfo.put(sessionInfo.getId(), sessionInfo); } - schedulePublishState(); if (mClient == null) { return; @@ -196,8 +195,8 @@ public abstract class MediaRoute2ProviderService extends Service { * Notifies clients of that the session could not be created. * * @param requestId id of the previous request to create the session provided in - * {@link #onCreateSession(String, String, String, long)}. - * @see #onCreateSession(String, String, String, long) + * {@link #onCreateSession(String, String, long, Bundle)}. + * @see #onCreateSession(String, String, long, Bundle) * @hide */ public final void notifySessionCreationFailed(long requestId) { @@ -289,16 +288,18 @@ public abstract class MediaRoute2ProviderService extends Service { * * @param packageName the package name of the application that selected the route * @param routeId the id of the route initially being connected - * @param routeFeature the route feature of the new session * @param requestId the id of this session creation request + * @param sessionHints an optional bundle of app-specific arguments sent by + * {@link MediaRouter2}, or null if none. The contents of this bundle + * may affect the result of session creation. * - * @see RoutingSessionInfo.Builder#Builder(String, String, String) + * @see RoutingSessionInfo.Builder#Builder(String, String) * @see RoutingSessionInfo.Builder#addSelectedRoute(String) * @see RoutingSessionInfo.Builder#setControlHints(Bundle) * @hide */ public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId, - @NonNull String routeFeature, long requestId); + long requestId, @Nullable Bundle sessionHints); /** * Called when the session should be released. A client of the session or system can request @@ -433,14 +434,14 @@ public abstract class MediaRoute2ProviderService extends Service { } @Override - public void requestCreateSession(String packageName, String routeId, - String routeFeature, long requestId) { + public void requestCreateSession(String packageName, String routeId, long requestId, + @Nullable Bundle requestCreateSession) { if (!checkCallerisSystem()) { return; } mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession, - MediaRoute2ProviderService.this, packageName, routeId, routeFeature, - requestId)); + MediaRoute2ProviderService.this, packageName, routeId, requestId, + requestCreateSession)); } @Override diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index bc4da10ac3b1..51d08ec96e6f 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -92,6 +92,7 @@ public class MediaRouter2 { @GuardedBy("sRouterLock") private boolean mShouldUpdateRoutes; private volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList(); + private volatile OnCreateSessionListener mOnCreateSessionListener; /** * Gets an instance of the media router associated with the context. @@ -281,38 +282,56 @@ public class MediaRouter2 { } /** + * Sets an {@link OnCreateSessionListener} to send hints when creating a session. + * To send the hints, listener should be set <em>BEFORE</em> calling + * {@link #requestCreateSession(MediaRoute2Info)}. + * + * @param listener A listener to send optional app-specific hints when creating a session. + * {@code null} for unset. + * @hide + */ + public void setOnCreateSessionListener(@Nullable OnCreateSessionListener listener) { + mOnCreateSessionListener = listener; + } + + /** * Requests the media route provider service to create a session with the given route. * * @param route the route you want to create a session with. - * @param routeFeature the route feature of the session. Should not be empty. * * @see SessionCallback#onSessionCreated * @see SessionCallback#onSessionCreationFailed * @hide */ @NonNull - public void requestCreateSession(@NonNull MediaRoute2Info route, - @NonNull String routeFeature) { + public void requestCreateSession(@NonNull MediaRoute2Info route) { Objects.requireNonNull(route, "route must not be null"); - if (TextUtils.isEmpty(routeFeature)) { - throw new IllegalArgumentException("routeFeature must not be empty"); - } // TODO: Check the given route exists - // TODO: Check the route supports the given routeFeature final int requestId; requestId = mSessionCreationRequestCnt.getAndIncrement(); - SessionCreationRequest request = new SessionCreationRequest(requestId, route, routeFeature); + SessionCreationRequest request = new SessionCreationRequest(requestId, route); mSessionCreationRequests.add(request); + + OnCreateSessionListener listener = mOnCreateSessionListener; + Bundle sessionHints = null; + if (listener != null) { + sessionHints = listener.onCreateSession(route); + if (sessionHints != null) { + sessionHints = new Bundle(sessionHints); + } + } + Client2 client; synchronized (sRouterLock) { client = mClient; } if (client != null) { try { - mMediaRouterService.requestCreateSession(client, route, routeFeature, requestId); + mMediaRouterService.requestCreateSession(client, route, requestId, + sessionHints); } catch (RemoteException ex) { Log.e(TAG, "Unable to request to create session.", ex); mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler, @@ -468,27 +487,18 @@ public class MediaRouter2 { mSessionCreationRequests.remove(matchingRequest); MediaRoute2Info requestedRoute = matchingRequest.mRoute; - String requestedRouteFeature = matchingRequest.mRouteFeature; if (sessionInfo == null) { // TODO: We may need to distinguish between failure and rejection. // One way can be introducing 'reason'. - notifySessionCreationFailed(requestedRoute, requestedRouteFeature); - return; - } else if (!TextUtils.equals(requestedRouteFeature, - sessionInfo.getRouteFeature())) { - Log.w(TAG, "The session has different route feature from what we requested. " - + "(requested=" + requestedRouteFeature - + ", actual=" + sessionInfo.getRouteFeature() - + ")"); - notifySessionCreationFailed(requestedRoute, requestedRouteFeature); + notifySessionCreationFailed(requestedRoute); return; } else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) { Log.w(TAG, "The session does not contain the requested route. " + "(requestedRouteId=" + requestedRoute.getId() + ", actualRoutes=" + sessionInfo.getSelectedRoutes() + ")"); - notifySessionCreationFailed(requestedRoute, requestedRouteFeature); + notifySessionCreationFailed(requestedRoute); return; } else if (!TextUtils.equals(requestedRoute.getProviderId(), sessionInfo.getProviderId())) { @@ -496,7 +506,7 @@ public class MediaRouter2 { + "(requested route's providerId=" + requestedRoute.getProviderId() + ", actual providerId=" + sessionInfo.getProviderId() + ")"); - notifySessionCreationFailed(requestedRoute, requestedRouteFeature); + notifySessionCreationFailed(requestedRoute); return; } } @@ -617,10 +627,10 @@ public class MediaRouter2 { } } - private void notifySessionCreationFailed(MediaRoute2Info route, String routeFeature) { + private void notifySessionCreationFailed(MediaRoute2Info route) { for (SessionCallbackRecord record: mSessionCallbackRecords) { record.mExecutor.execute( - () -> record.mSessionCallback.onSessionCreationFailed(route, routeFeature)); + () -> record.mSessionCallback.onSessionCreationFailed(route)); } } @@ -688,10 +698,8 @@ public class MediaRouter2 { * Called when the session creation request failed. * * @param requestedRoute the route info which was used for the request - * @param requestedRouteFeature the route feature which was used for the request */ - public void onSessionCreationFailed(@NonNull MediaRoute2Info requestedRoute, - @NonNull String requestedRouteFeature) {} + public void onSessionCreationFailed(@NonNull MediaRoute2Info requestedRoute) {} /** * Called when the session info has changed. @@ -724,6 +732,34 @@ public class MediaRouter2 { } /** + * A listener interface to send an optional app-specific hints when creating a session. + * + * @hide + */ + public interface OnCreateSessionListener { + /** + * Called when the {@link MediaRouter2} is about to request + * the media route provider service to create a session with the given route. + * The {@link Bundle} returned here will be sent to media route provider service as a hint + * for creating a session. + * <p> + * To send hints when creating the session, set this listener before calling + * {@link #requestCreateSession(MediaRoute2Info)}. + * <p> + * This will be called on the same thread which calls + * {@link #requestCreateSession(MediaRoute2Info)}. + * + * @param route The route to create session with + * @return An optional bundle of app-specific arguments to send to the provider, + * or null if none. The contents of this bundle may affect the result of + * session creation. + * @see MediaRoute2ProviderService#onCreateSession(String, String, long, Bundle) + */ + @Nullable + Bundle onCreateSession(@NonNull MediaRoute2Info route); + } + + /** * A class to control media routing session in media route provider. * For example, selecting/deselcting/transferring routes to session can be done through this * class. Instances are created by {@link MediaRouter2}. @@ -753,16 +789,6 @@ public class MediaRouter2 { } /** - * @return the feature which is used by the session mainly. - */ - @NonNull - public String getRouteFeature() { - synchronized (mControllerLock) { - return mSessionInfo.getRouteFeature(); - } - } - - /** * @return the control hints used to control routing session if available. */ @Nullable @@ -1012,7 +1038,6 @@ public class MediaRouter2 { StringBuilder result = new StringBuilder() .append("RoutingController{ ") .append("sessionId=").append(getSessionId()) - .append(", routeFeature=").append(getRouteFeature()) .append(", selectedRoutes={") .append(selectedRoutes) .append("}") @@ -1122,13 +1147,10 @@ public class MediaRouter2 { final class SessionCreationRequest { public final MediaRoute2Info mRoute; - public final String mRouteFeature; public final int mRequestId; - SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route, - @NonNull String routeFeature) { + SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route) { mRoute = route; - mRouteFeature = routeFeature; mRequestId = requestId; } } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 70229335905b..5cb32d6ab550 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -22,6 +22,7 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; @@ -131,7 +132,7 @@ public class MediaRouter2Manager { Objects.requireNonNull(callback, "callback must not be null"); if (!mCallbackRecords.remove(new CallbackRecord(null, callback))) { - Log.w(TAG, "Ignore removing unknown callback. " + callback); + Log.w(TAG, "unregisterCallback: Ignore unknown callback. " + callback); return; } @@ -175,6 +176,29 @@ public class MediaRouter2Manager { return routes; } + /** + * Gets routing controllers of an application with the given package name. + * If the application isn't running or it doesn't use {@link MediaRouter2}, an empty list + * will be returned. + */ + @NonNull + public List<RoutingController> getRoutingControllers(@NonNull String packageName) { + Objects.requireNonNull(packageName, "packageName must not be null"); + + List<RoutingController> controllers = new ArrayList<>(); + + for (RoutingSessionInfo sessionInfo : getActiveSessions()) { + if (TextUtils.equals(sessionInfo.getClientPackageName(), packageName)) { + controllers.add(new RoutingController(sessionInfo)); + } + } + return controllers; + } + + /** + * Gets the list of all active routing sessions. It doesn't include default routing sessions + * of applications. + */ @NonNull public List<RoutingSessionInfo> getActiveSessions() { Client client; @@ -192,23 +216,7 @@ public class MediaRouter2Manager { } /** - * Gets the list of routes that are actively used by {@link MediaRouter2}. - */ - @NonNull - public List<MediaRoute2Info> getActiveRoutes() { - List<MediaRoute2Info> routes = new ArrayList<>(); - synchronized (mRoutesLock) { - for (MediaRoute2Info route : mRoutes.values()) { - if (!TextUtils.isEmpty(route.getClientPackageName())) { - routes.add(route); - } - } - } - return routes; - } - - /** - * Gets the list of discovered routes + * Gets the list of all discovered routes */ @NonNull public List<MediaRoute2Info> getAllRoutes() { @@ -222,6 +230,10 @@ public class MediaRouter2Manager { /** * Selects media route for the specified package name. * + * If the given route is {@link RoutingController#getTransferrableRoutes() a transferrable + * route} of a routing session of the application, the session will be transferred to + * the route. If not, a new routing session will be created. + * * @param packageName the package name of the application that should change it's media route * @param route the route to be selected. */ @@ -229,6 +241,13 @@ public class MediaRouter2Manager { Objects.requireNonNull(packageName, "packageName must not be null"); Objects.requireNonNull(route, "route must not be null"); + for (RoutingController controller : getRoutingControllers(packageName)) { + if (controller.getSessionInfo().getTransferrableRoutes().contains(route.getId())) { + controller.transferToRoute(route); + return; + } + } + Client client; synchronized (sLock) { client = mClient; @@ -238,6 +257,7 @@ public class MediaRouter2Manager { int requestId = mNextRequestId.getAndIncrement(); mMediaRouterService.requestCreateClientSession( client, packageName, route, requestId); + //TODO: release the previous session? } catch (RemoteException ex) { Log.e(TAG, "Unable to select media route", ex); } @@ -245,7 +265,7 @@ public class MediaRouter2Manager { } /** - * Requests a volume change for the route asynchronously. + * Requests a volume change for a route asynchronously. * <p> * It may have no effect if the route is currently not selected. * </p> @@ -346,9 +366,16 @@ public class MediaRouter2Manager { } } - void notifyRouteSelected(String packageName, MediaRoute2Info route) { + void notifySessionCreated(RoutingSessionInfo sessionInfo) { for (CallbackRecord record : mCallbackRecords) { - record.mExecutor.execute(() -> record.mCallback.onRouteSelected(packageName, route)); + record.mExecutor.execute(() -> record.mCallback.onSessionCreated( + new RoutingController(sessionInfo))); + } + } + + void notifySessionInfosChanged() { + for (CallbackRecord record : mCallbackRecords) { + record.mExecutor.execute(() -> record.mCallback.onSessionsUpdated()); } } @@ -365,6 +392,275 @@ public class MediaRouter2Manager { } /** + * @hide + */ + public RoutingController getControllerForSession(@NonNull RoutingSessionInfo sessionInfo) { + return new RoutingController(sessionInfo); + } + + /** + * A class to control media routing session in media route provider. + * With routing controller, an application can select a route into the session or deselect + * a route in the session. + */ + public final class RoutingController { + private final Object mControllerLock = new Object(); + @GuardedBy("mControllerLock") + private RoutingSessionInfo mSessionInfo; + + RoutingController(@NonNull RoutingSessionInfo sessionInfo) { + mSessionInfo = sessionInfo; + } + + /** + * Gets the ID of the session + */ + @NonNull + public String getSessionId() { + synchronized (mControllerLock) { + return mSessionInfo.getId(); + } + } + + /** + * Gets the client package name of the session + */ + @NonNull + public String getClientPackageName() { + synchronized (mControllerLock) { + return mSessionInfo.getClientPackageName(); + } + } + + /** + * @return the control hints used to control route session if available. + */ + @Nullable + public Bundle getControlHints() { + synchronized (mControllerLock) { + return mSessionInfo.getControlHints(); + } + } + + /** + * @return the unmodifiable list of currently selected routes + */ + @NonNull + public List<MediaRoute2Info> getSelectedRoutes() { + List<String> routeIds; + synchronized (mControllerLock) { + routeIds = mSessionInfo.getSelectedRoutes(); + } + return getRoutesWithIds(routeIds); + } + + /** + * @return the unmodifiable list of selectable routes for the session. + */ + @NonNull + public List<MediaRoute2Info> getSelectableRoutes() { + List<String> routeIds; + synchronized (mControllerLock) { + routeIds = mSessionInfo.getSelectableRoutes(); + } + return getRoutesWithIds(routeIds); + } + + /** + * @return the unmodifiable list of deselectable routes for the session. + */ + @NonNull + public List<MediaRoute2Info> getDeselectableRoutes() { + List<String> routeIds; + synchronized (mControllerLock) { + routeIds = mSessionInfo.getDeselectableRoutes(); + } + return getRoutesWithIds(routeIds); + } + + /** + * @return the unmodifiable list of transferrable routes for the session. + */ + @NonNull + public List<MediaRoute2Info> getTransferrableRoutes() { + List<String> routeIds; + synchronized (mControllerLock) { + routeIds = mSessionInfo.getTransferrableRoutes(); + } + return getRoutesWithIds(routeIds); + } + + /** + * Selects a route for the remote session. The given route must satisfy all of the + * following conditions: + * <ul> + * <li>ID should not be included in {@link #getSelectedRoutes()}</li> + * <li>ID should be included in {@link #getSelectableRoutes()}</li> + * </ul> + * If the route doesn't meet any of above conditions, it will be ignored. + * + * @see #getSelectedRoutes() + * @see #getSelectableRoutes() + */ + public void selectRoute(@NonNull MediaRoute2Info route) { + Objects.requireNonNull(route, "route must not be null"); + + RoutingSessionInfo sessionInfo; + synchronized (mControllerLock) { + sessionInfo = mSessionInfo; + } + if (sessionInfo.getSelectedRoutes().contains(route.getId())) { + Log.w(TAG, "Ignoring selecting a route that is already selected. route=" + route); + return; + } + + if (!sessionInfo.getSelectableRoutes().contains(route.getId())) { + Log.w(TAG, "Ignoring selecting a non-selectable route=" + route); + return; + } + + Client client; + synchronized (sLock) { + client = mClient; + } + if (client != null) { + try { + mMediaRouterService.selectClientRoute(mClient, getSessionId(), route); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to select route for session.", ex); + } + } + } + + /** + * Deselects a route from the remote session. The given route must satisfy all of the + * following conditions: + * <ul> + * <li>ID should be included in {@link #getSelectedRoutes()}</li> + * <li>ID should be included in {@link #getDeselectableRoutes()}</li> + * </ul> + * If the route doesn't meet any of above conditions, it will be ignored. + * + * @see #getSelectedRoutes() + * @see #getDeselectableRoutes() + */ + public void deselectRoute(@NonNull MediaRoute2Info route) { + Objects.requireNonNull(route, "route must not be null"); + RoutingSessionInfo sessionInfo; + synchronized (mControllerLock) { + sessionInfo = mSessionInfo; + } + + if (!sessionInfo.getSelectedRoutes().contains(route.getId())) { + Log.w(TAG, "Ignoring deselecting a route that is not selected. route=" + route); + return; + } + + if (!sessionInfo.getDeselectableRoutes().contains(route.getId())) { + Log.w(TAG, "Ignoring deselecting a non-deselectable route=" + route); + return; + } + + Client client; + synchronized (sLock) { + client = mClient; + } + if (client != null) { + try { + mMediaRouterService.deselectClientRoute(mClient, getSessionId(), route); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to remove route from session.", ex); + } + } + } + + /** + * Transfers to a given route for the remote session. The given route must satisfy + * all of the following conditions: + * <ul> + * <li>ID should not be included in {@link #getSelectedRoutes()}</li> + * <li>ID should be included in {@link #getTransferrableRoutes()}</li> + * </ul> + * If the route doesn't meet any of above conditions, it will be ignored. + * + * @see #getSelectedRoutes() + * @see #getTransferrableRoutes() + */ + public void transferToRoute(@NonNull MediaRoute2Info route) { + Objects.requireNonNull(route, "route must not be null"); + RoutingSessionInfo sessionInfo; + synchronized (mControllerLock) { + sessionInfo = mSessionInfo; + } + + if (sessionInfo.getSelectedRoutes().contains(route.getId())) { + Log.w(TAG, "Ignoring transferring to a route that is already added. route=" + + route); + return; + } + + if (!sessionInfo.getTransferrableRoutes().contains(route.getId())) { + Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route); + return; + } + + Client client; + synchronized (sLock) { + client = mClient; + } + if (client != null) { + try { + mMediaRouterService.transferToClientRoute(mClient, getSessionId(), route); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to transfer to route for session.", ex); + } + } + } + + /** + * Release this session. + * Any operation on this session after calling this method will be ignored. + */ + public void release() { + Client client; + synchronized (sLock) { + client = mClient; + } + if (client != null) { + try { + mMediaRouterService.releaseClientSession(mClient, getSessionId()); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to notify of controller release", ex); + } + } + } + + /** + * Gets the session info of the session + * @hide + */ + @NonNull + public RoutingSessionInfo getSessionInfo() { + synchronized (mControllerLock) { + return mSessionInfo; + } + } + + private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) { + List<MediaRoute2Info> routes = new ArrayList<>(); + synchronized (mRoutesLock) { + for (String routeId : routeIds) { + MediaRoute2Info route = mRoutes.get(routeId); + if (route != null) { + routes.add(route); + } + } + } + return Collections.unmodifiableList(routes); + } + } + + /** * Interface for receiving events about media routing changes. */ public static class Callback { @@ -388,14 +684,17 @@ public class MediaRouter2Manager { public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} /** - * Called when a route is selected for an application. + * Called when a routing session is created. * - * @param packageName the package name of the application - * @param route the selected route of the application. - * It is null if the application has no selected route. + * @param controller the controller to control the created session */ - public void onRouteSelected(@NonNull String packageName, @Nullable MediaRoute2Info route) {} + public void onSessionCreated(@NonNull RoutingController controller) {} + /** + * Called when at least one session info is changed. + * Call {@link #getActiveSessions()} to get current active session info. + */ + public void onSessionsUpdated() {} /** * Called when the preferred route features of an app is changed. @@ -435,11 +734,19 @@ public class MediaRouter2Manager { class Client extends IMediaRouter2Manager.Stub { @Override - public void notifyRouteSelected(String packageName, MediaRoute2Info route) { - mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyRouteSelected, - MediaRouter2Manager.this, packageName, route)); + public void notifySessionCreated(RoutingSessionInfo sessionInfo) { + mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifySessionCreated, + MediaRouter2Manager.this, sessionInfo)); } + @Override + public void notifySessionsUpdated() { + mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifySessionInfosChanged, + MediaRouter2Manager.this)); + // do nothing + } + + @Override public void notifyPreferredFeaturesChanged(String packageName, List<String> features) { mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updatePreferredFeatures, MediaRouter2Manager.this, packageName, features)); diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java index 96acf6cec5b4..228addebe029 100644 --- a/media/java/android/media/RoutingSessionInfo.java +++ b/media/java/android/media/RoutingSessionInfo.java @@ -51,7 +51,6 @@ public final class RoutingSessionInfo implements Parcelable { final String mId; final String mClientPackageName; - final String mRouteFeature; @Nullable final String mProviderId; final List<String> mSelectedRoutes; @@ -66,7 +65,6 @@ public final class RoutingSessionInfo implements Parcelable { mId = builder.mId; mClientPackageName = builder.mClientPackageName; - mRouteFeature = builder.mRouteFeature; mProviderId = builder.mProviderId; // TODO: Needs to check that the routes already have unique IDs. @@ -87,7 +85,6 @@ public final class RoutingSessionInfo implements Parcelable { mId = ensureString(src.readString()); mClientPackageName = ensureString(src.readString()); - mRouteFeature = ensureString(src.readString()); mProviderId = src.readString(); mSelectedRoutes = ensureList(src.createStringArrayList()); @@ -119,7 +116,7 @@ public final class RoutingSessionInfo implements Parcelable { * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method * can be different from what was set in {@link MediaRoute2ProviderService}. * - * @see Builder#Builder(String, String, String) + * @see Builder#Builder(String, String) */ @NonNull public String getId() { @@ -131,7 +128,7 @@ public final class RoutingSessionInfo implements Parcelable { } /** - * Gets the original id set by {@link Builder#Builder(String, String, String)}. + * Gets the original id set by {@link Builder#Builder(String, String)}. * @hide */ @NonNull @@ -148,15 +145,6 @@ public final class RoutingSessionInfo implements Parcelable { } /** - * Gets the route feature of the session. - * Routes that don't have the feature can't be selected into the session. - */ - @NonNull - public String getRouteFeature() { - return mRouteFeature; - } - - /** * Gets the provider id of the session. * @hide */ @@ -214,7 +202,6 @@ public final class RoutingSessionInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mId); dest.writeString(mClientPackageName); - dest.writeString(mRouteFeature); dest.writeString(mProviderId); dest.writeStringList(mSelectedRoutes); dest.writeStringList(mSelectableRoutes); @@ -235,7 +222,6 @@ public final class RoutingSessionInfo implements Parcelable { RoutingSessionInfo other = (RoutingSessionInfo) obj; return Objects.equals(mId, other.mId) && Objects.equals(mClientPackageName, other.mClientPackageName) - && Objects.equals(mRouteFeature, other.mRouteFeature) && Objects.equals(mProviderId, other.mProviderId) && Objects.equals(mSelectedRoutes, other.mSelectedRoutes) && Objects.equals(mSelectableRoutes, other.mSelectableRoutes) @@ -245,7 +231,7 @@ public final class RoutingSessionInfo implements Parcelable { @Override public int hashCode() { - return Objects.hash(mId, mClientPackageName, mRouteFeature, mProviderId, + return Objects.hash(mId, mClientPackageName, mProviderId, mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferrableRoutes); } @@ -254,7 +240,6 @@ public final class RoutingSessionInfo implements Parcelable { StringBuilder result = new StringBuilder() .append("RoutingSessionInfo{ ") .append("sessionId=").append(mId) - .append(", routeFeature=").append(mRouteFeature) .append(", selectedRoutes={") .append(String.join(",", mSelectedRoutes)) .append("}") @@ -295,7 +280,6 @@ public final class RoutingSessionInfo implements Parcelable { public static final class Builder { final String mId; final String mClientPackageName; - final String mRouteFeature; String mProviderId; final List<String> mSelectedRoutes; final List<String> mSelectableRoutes; @@ -314,22 +298,16 @@ public final class RoutingSessionInfo implements Parcelable { * @param id ID of the session. Must not be empty. * @param clientPackageName package name of the client app which uses this session. * If is is unknown, then just use an empty string. - * @param routeFeature the route feature of session. Must not be empty. * @see MediaRoute2Info#getId() */ - public Builder(@NonNull String id, @NonNull String clientPackageName, - @NonNull String routeFeature) { + public Builder(@NonNull String id, @NonNull String clientPackageName) { if (TextUtils.isEmpty(id)) { throw new IllegalArgumentException("id must not be empty"); } Objects.requireNonNull(clientPackageName, "clientPackageName must not be null"); - if (TextUtils.isEmpty(routeFeature)) { - throw new IllegalArgumentException("routeFeature must not be empty"); - } mId = id; mClientPackageName = clientPackageName; - mRouteFeature = routeFeature; mSelectedRoutes = new ArrayList<>(); mSelectableRoutes = new ArrayList<>(); mDeselectableRoutes = new ArrayList<>(); @@ -347,7 +325,6 @@ public final class RoutingSessionInfo implements Parcelable { mId = sessionInfo.mId; mClientPackageName = sessionInfo.mClientPackageName; - mRouteFeature = sessionInfo.mRouteFeature; mProviderId = sessionInfo.mProviderId; mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes); diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java index 83abf8616468..aa0649a5cd8b 100644 --- a/media/java/android/media/tv/tuner/DemuxCapabilities.java +++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java @@ -16,9 +16,11 @@ package android.media.tv.tuner; +import android.annotation.BytesLong; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.Size; +import android.annotation.SystemApi; import android.media.tv.tuner.filter.FilterConfiguration; import java.lang.annotation.Retention; @@ -29,6 +31,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@SystemApi public class DemuxCapabilities { /** @hide */ @@ -42,33 +45,33 @@ public class DemuxCapabilities { @Retention(RetentionPolicy.SOURCE) public @interface FilterCapabilities {} - private final int mNumDemux; - private final int mNumRecord; - private final int mNumPlayback; - private final int mNumTsFilter; - private final int mNumSectionFilter; - private final int mNumAudioFilter; - private final int mNumVideoFilter; - private final int mNumPesFilter; - private final int mNumPcrFilter; - private final int mNumBytesInSectionFilter; + private final int mDemuxCount; + private final int mRecordCount; + private final int mPlaybackCount; + private final int mTsFilterCount; + private final int mSectionFilterCount; + private final int mAudioFilterCount; + private final int mVideoFilterCount; + private final int mPesFilterCount; + private final int mPcrFilterCount; + private final long mSectionFilterLength; private final int mFilterCaps; private final int[] mLinkCaps; // Used by JNI - private DemuxCapabilities(int numDemux, int numRecord, int numPlayback, int numTsFilter, - int numSectionFilter, int numAudioFilter, int numVideoFilter, int numPesFilter, - int numPcrFilter, int numBytesInSectionFilter, int filterCaps, int[] linkCaps) { - mNumDemux = numDemux; - mNumRecord = numRecord; - mNumPlayback = numPlayback; - mNumTsFilter = numTsFilter; - mNumSectionFilter = numSectionFilter; - mNumAudioFilter = numAudioFilter; - mNumVideoFilter = numVideoFilter; - mNumPesFilter = numPesFilter; - mNumPcrFilter = numPcrFilter; - mNumBytesInSectionFilter = numBytesInSectionFilter; + private DemuxCapabilities(int demuxCount, int recordCount, int playbackCount, int tsFilterCount, + int sectionFilterCount, int audioFilterCount, int videoFilterCount, int pesFilterCount, + int pcrFilterCount, long sectionFilterLength, int filterCaps, int[] linkCaps) { + mDemuxCount = demuxCount; + mRecordCount = recordCount; + mPlaybackCount = playbackCount; + mTsFilterCount = tsFilterCount; + mSectionFilterCount = sectionFilterCount; + mAudioFilterCount = audioFilterCount; + mVideoFilterCount = videoFilterCount; + mPesFilterCount = pesFilterCount; + mPcrFilterCount = pcrFilterCount; + mSectionFilterLength = sectionFilterLength; mFilterCaps = filterCaps; mLinkCaps = linkCaps; } @@ -76,62 +79,63 @@ public class DemuxCapabilities { /** * Gets total number of demuxes. */ - public int getNumDemux() { - return mNumDemux; + public int getDemuxCount() { + return mDemuxCount; } /** * Gets max number of recordings at a time. */ - public int getNumRecord() { - return mNumRecord; + public int getRecordCount() { + return mRecordCount; } /** * Gets max number of playbacks at a time. */ - public int getNumPlayback() { - return mNumPlayback; + public int getPlaybackCount() { + return mPlaybackCount; } /** * Gets number of TS filters. */ - public int getNumTsFilter() { - return mNumTsFilter; + public int getTsFilterCount() { + return mTsFilterCount; } /** * Gets number of section filters. */ - public int getNumSectionFilter() { - return mNumSectionFilter; + public int getSectionFilterCount() { + return mSectionFilterCount; } /** * Gets number of audio filters. */ - public int getNumAudioFilter() { - return mNumAudioFilter; + public int getAudioFilterCount() { + return mAudioFilterCount; } /** * Gets number of video filters. */ - public int getNumVideoFilter() { - return mNumVideoFilter; + public int getVideoFilterCount() { + return mVideoFilterCount; } /** * Gets number of PES filters. */ - public int getNumPesFilter() { - return mNumPesFilter; + public int getPesFilterCount() { + return mPesFilterCount; } /** * Gets number of PCR filters. */ - public int getNumPcrFilter() { - return mNumPcrFilter; + public int getPcrFilterCount() { + return mPcrFilterCount; } /** * Gets number of bytes in the mask of a section filter. */ - public int getNumBytesInSectionFilter() { - return mNumBytesInSectionFilter; + @BytesLong + public long getSectionFilterLength() { + return mSectionFilterLength; } /** * Gets filter capabilities in bit field. diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java index 0143582ab815..23016e9239ca 100644 --- a/media/java/android/media/tv/tuner/Descrambler.java +++ b/media/java/android/media/tv/tuner/Descrambler.java @@ -34,7 +34,7 @@ import java.lang.annotation.RetentionPolicy; */ public class Descrambler implements AutoCloseable { /** @hide */ - @IntDef(prefix = "PID_TYPE_", value = {PID_TYPE_T, PID_TYPE_MMPT}) + @IntDef(prefix = "PID_TYPE_", value = {PID_TYPE_T, PID_TYPE_MMTP}) @Retention(RetentionPolicy.SOURCE) public @interface PidType {} @@ -45,7 +45,7 @@ public class Descrambler implements AutoCloseable { /** * Packet ID is used to specify packets in MMTP. */ - public static final int PID_TYPE_MMPT = 2; + public static final int PID_TYPE_MMTP = 2; private long mNativeContext; diff --git a/media/java/android/media/tv/tuner/FrontendSettings.java b/media/java/android/media/tv/tuner/FrontendSettings.java index ad8422caaa02..7f9b9826c94b 100644 --- a/media/java/android/media/tv/tuner/FrontendSettings.java +++ b/media/java/android/media/tv/tuner/FrontendSettings.java @@ -16,33 +16,10 @@ package android.media.tv.tuner; -import android.annotation.SystemApi; - /** * Frontend settings for tune and scan operations. + * TODO: remove * @hide */ -@SystemApi public abstract class FrontendSettings { - private final int mFrequency; - - /** @hide */ - public FrontendSettings(int frequency) { - mFrequency = frequency; - } - - /** - * Returns the frontend type. - */ - public abstract int getType(); - - /** - * Gets the frequency setting. - * - * @return the frequency in Hz. - */ - public final int getFrequency() { - return mFrequency; - } - } diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java index 8e579bf897dd..a9a15d97e859 100644 --- a/media/java/android/media/tv/tuner/Lnb.java +++ b/media/java/android/media/tv/tuner/Lnb.java @@ -40,7 +40,8 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class Lnb implements AutoCloseable { /** @hide */ - @IntDef({VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V, + @IntDef(prefix = "VOLTAGE_", + value = {VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V, VOLTAGE_15V, VOLTAGE_18V, VOLTAGE_19V}) @Retention(RetentionPolicy.SOURCE) public @interface Voltage {} @@ -83,7 +84,8 @@ public class Lnb implements AutoCloseable { public static final int VOLTAGE_19V = Constants.LnbVoltage.VOLTAGE_19V; /** @hide */ - @IntDef({TONE_NONE, TONE_CONTINUOUS}) + @IntDef(prefix = "TONE_", + value = {TONE_NONE, TONE_CONTINUOUS}) @Retention(RetentionPolicy.SOURCE) public @interface Tone {} @@ -97,7 +99,8 @@ public class Lnb implements AutoCloseable { public static final int TONE_CONTINUOUS = Constants.LnbTone.CONTINUOUS; /** @hide */ - @IntDef({POSITION_UNDEFINED, POSITION_A, POSITION_B}) + @IntDef(prefix = "POSITION_", + value = {POSITION_UNDEFINED, POSITION_A, POSITION_B}) @Retention(RetentionPolicy.SOURCE) public @interface Position {} @@ -114,6 +117,37 @@ public class Lnb implements AutoCloseable { */ public static final int POSITION_B = Constants.LnbPosition.POSITION_B; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "EVENT_TYPE_", + value = {EVENT_TYPE_DISEQC_RX_OVERFLOW, EVENT_TYPE_DISEQC_RX_TIMEOUT, + EVENT_TYPE_DISEQC_RX_PARITY_ERROR, EVENT_TYPE_LNB_OVERLOAD}) + public @interface EventType {} + + /** + * Outgoing Diseqc message overflow. + * @hide + */ + public static final int EVENT_TYPE_DISEQC_RX_OVERFLOW = + Constants.LnbEventType.DISEQC_RX_OVERFLOW; + /** + * Outgoing Diseqc message isn't delivered on time. + * @hide + */ + public static final int EVENT_TYPE_DISEQC_RX_TIMEOUT = + Constants.LnbEventType.DISEQC_RX_TIMEOUT; + /** + * Incoming Diseqc message has parity error. + * @hide + */ + public static final int EVENT_TYPE_DISEQC_RX_PARITY_ERROR = + Constants.LnbEventType.DISEQC_RX_PARITY_ERROR; + /** + * LNB is overload. + * @hide + */ + public static final int EVENT_TYPE_LNB_OVERLOAD = Constants.LnbEventType.LNB_OVERLOAD; + int mId; LnbCallback mCallback; Context mContext; diff --git a/media/java/android/media/tv/tuner/LnbCallback.java b/media/java/android/media/tv/tuner/LnbCallback.java index 99bbf866aa69..5155f604b77b 100644 --- a/media/java/android/media/tv/tuner/LnbCallback.java +++ b/media/java/android/media/tv/tuner/LnbCallback.java @@ -17,6 +17,8 @@ package android.media.tv.tuner; +import android.media.tv.tuner.Lnb.EventType; + /** * Callback interface for receiving information from LNBs. * @@ -26,7 +28,7 @@ public interface LnbCallback { /** * Invoked when there is a LNB event. */ - void onEvent(int lnbEventType); + void onEvent(@EventType int lnbEventType); /** * Invoked when there is a new DiSEqC message. diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 18969ae3279c..44579540b0ce 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -24,18 +24,18 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; import android.media.tv.tuner.TunerConstants.FilterStatus; -import android.media.tv.tuner.TunerConstants.FilterSubtype; -import android.media.tv.tuner.TunerConstants.FrontendScanType; import android.media.tv.tuner.TunerConstants.Result; import android.media.tv.tuner.dvr.Dvr; import android.media.tv.tuner.dvr.DvrCallback; import android.media.tv.tuner.dvr.DvrSettings; -import android.media.tv.tuner.filter.FilterConfiguration.FilterType; +import android.media.tv.tuner.filter.Filter.Subtype; +import android.media.tv.tuner.filter.Filter.Type; import android.media.tv.tuner.filter.FilterEvent; import android.media.tv.tuner.filter.TimeFilter; -import android.media.tv.tuner.frontend.FrontendCallback; import android.media.tv.tuner.frontend.FrontendInfo; +import android.media.tv.tuner.frontend.FrontendSettings; import android.media.tv.tuner.frontend.FrontendStatus; +import android.media.tv.tuner.frontend.OnTuneEventListener; import android.media.tv.tuner.frontend.ScanCallback; import android.os.Handler; import android.os.Looper; @@ -58,7 +58,6 @@ public final class Tuner implements AutoCloseable { private static final String TAG = "MediaTvTuner"; private static final boolean DEBUG = false; - private static final int MSG_ON_FRONTEND_EVENT = 1; private static final int MSG_ON_FILTER_EVENT = 2; private static final int MSG_ON_FILTER_STATUS = 3; private static final int MSG_ON_LNB_EVENT = 4; @@ -77,6 +76,10 @@ public final class Tuner implements AutoCloseable { private List<Integer> mLnbIds; private Lnb mLnb; @Nullable + private OnTuneEventListener mOnTuneEventListener; + @Nullable + private Executor mOnTunerEventExecutor; + @Nullable private ScanCallback mScanCallback; @Nullable private Executor mScanCallbackExecutor; @@ -102,7 +105,8 @@ public final class Tuner implements AutoCloseable { * TODO: replace the other constructor */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) - public Tuner(@NonNull Context context, @NonNull String tvInputSessionId, int useCase) { + public Tuner(@NonNull Context context, @NonNull String tvInputSessionId, int useCase, + @Nullable OnResourceLostListener listener) { mContext = context; } @@ -113,7 +117,8 @@ public final class Tuner implements AutoCloseable { * * @hide */ - public void shareFrontend(@NonNull Tuner tuner) { } + public void shareFrontendFromTuner(@NonNull Tuner tuner) { + } private long mNativeContext; // used by native jMediaTuner @@ -221,11 +226,6 @@ public final class Tuner implements AutoCloseable { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_ON_FRONTEND_EVENT: - if (mFrontend != null && mFrontend.mCallback != null) { - mFrontend.mCallback.onEvent(msg.arg1); - } - break; case MSG_ON_FILTER_STATUS: { Filter filter = (Filter) msg.obj; if (filter.mCallback != null) { @@ -241,7 +241,6 @@ public final class Tuner implements AutoCloseable { private class Frontend { private int mId; - private FrontendCallback mCallback; private Frontend(int id) { mId = id; @@ -249,13 +248,59 @@ public final class Tuner implements AutoCloseable { } /** - * Tunes the frontend to the settings given. + * Listens for tune events. + * + * <p> + * Tuner events are started when {@link #tune(FrontendSettings)} is called and end when {@link + * #stopTune()} is called. + * + * @param eventListener receives tune events. + * @throws SecurityException if the caller does not have appropriate permissions. + * @see #tune(FrontendSettings) + */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) + public void setOnTuneEventListener(@NonNull @CallbackExecutor Executor executor, + @NonNull OnTuneEventListener eventListener) { + TunerUtils.checkTunerPermission(mContext); + mOnTuneEventListener = eventListener; + mOnTunerEventExecutor = executor; + } + + /** + * Clears the {@link OnTuneEventListener} and its associated {@link Executor}. + * + * @throws SecurityException if the caller does not have appropriate permissions. + * @see #setOnTuneEventListener(Executor, OnTuneEventListener) + */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) + public void clearOnTuneEventListener() { + TunerUtils.checkTunerPermission(mContext); + mOnTuneEventListener = null; + mOnTunerEventExecutor = null; + + } + + /** + * Tunes the frontend to using the settings given. + * + * <p> + * This locks the frontend to a frequency by providing signal + * delivery information. If previous tuning isn't completed, this stop the previous tuning, and + * start a new tuning. + * + * <p> + * Tune is an async call, with {@link OnTuneEventListener#LOCKED LOCKED} and {@link + * OnTuneEventListener#NO_SIGNAL NO_SIGNAL} events sent to the {@link OnTuneEventListener} + * specified in {@link #setOnTuneEventListener(Executor, OnTuneEventListener)}. * + * @param settings Signal delivery information the frontend uses to + * search and lock the signal. * @return result status of tune operation. * @throws SecurityException if the caller does not have appropriate permissions. - * TODO: add result constants or throw exceptions. + * @see #setOnTuneEventListener(Executor, OnTuneEventListener) */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) + @Result public int tune(@NonNull FrontendSettings settings) { TunerUtils.checkTunerPermission(mContext); return nativeTune(settings.getType(), settings); @@ -268,8 +313,6 @@ public final class Tuner implements AutoCloseable { * will be sent to attached filters. * * @return result status of the operation. - * - * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Result @@ -279,17 +322,26 @@ public final class Tuner implements AutoCloseable { } /** - * Scan channels. + * Scan for channels. + * + * <p>Details for channels found are returned via {@link ScanCallback}. * * @param settings A {@link FrontendSettings} to configure the frontend. * @param scanType The scan type. - * + * @throws SecurityException if the caller does not have appropriate permissions. + * @throws IllegalStateException if {@code scan} is called again before {@link #stopScan()} is + * called. * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) - public int scan(@NonNull FrontendSettings settings, @FrontendScanType int scanType, + public int scan(@NonNull FrontendSettings settings, @ScanCallback.ScanType int scanType, @NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) { TunerUtils.checkTunerPermission(mContext); + if (mScanCallback != null || mScanCallbackExecutor != null) { + throw new IllegalStateException( + "Scan already in progress. stopScan must be called before a new scan can be " + + "started."); + } mScanCallback = scanCallback; mScanCallbackExecutor = executor; return nativeScan(settings.getType(), settings, scanType); @@ -304,6 +356,7 @@ public final class Tuner implements AutoCloseable { * <p> * If the method completes successfully, the frontend stopped previous scanning. * + * @throws SecurityException if the caller does not have appropriate permissions. * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @@ -490,8 +543,8 @@ public final class Tuner implements AutoCloseable { } private void onFrontendEvent(int eventType) { - if (mHandler != null) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_FRONTEND_EVENT, eventType, 0)); + if (mOnTunerEventExecutor != null && mOnTuneEventListener != null) { + mOnTunerEventExecutor.execute(() -> mOnTuneEventListener.onTuneEvent(eventType)); } } @@ -513,18 +566,18 @@ public final class Tuner implements AutoCloseable { * @param subType the subtype of the filter. * @param bufferSize the buffer size of the filter to be opened in bytes. The buffer holds the * data output from the filter. - * @param cb the callback to receive notifications from filter. * @param executor the executor on which callback will be invoked. The default event handler * executor is used if it's {@code null}. + * @param cb the callback to receive notifications from filter. * @return the opened filter. {@code null} if the operation failed. * * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Nullable - public Filter openFilter(@FilterType int mainType, @FilterSubtype int subType, - @BytesLong long bufferSize, @Nullable FilterCallback cb, - @CallbackExecutor @Nullable Executor executor) { + public Filter openFilter(@Type int mainType, @Subtype int subType, + @BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor, + @Nullable FilterCallback cb) { TunerUtils.checkTunerPermission(mContext); Filter filter = nativeOpenFilter( mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize); @@ -540,16 +593,34 @@ public final class Tuner implements AutoCloseable { /** * Opens an LNB (low-noise block downconverter) object. * + * @param executor the executor on which callback will be invoked. The default event handler + * executor is used if it's {@code null}. * @param cb the callback to receive notifications from LNB. + * @return the opened LNB object. {@code null} if the operation failed. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) + @Nullable + public Lnb openLnb(@CallbackExecutor @Nullable Executor executor, LnbCallback cb) { + return openLnbByName(null, executor, cb); + } + + /** + * Opens an LNB (low-noise block downconverter) object. + * + * @param name the LNB name. * @param executor the executor on which callback will be invoked. The default event handler * executor is used if it's {@code null}. + * @param cb the callback to receive notifications from LNB. * @return the opened LNB object. {@code null} if the operation failed. * * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Nullable - public Lnb openLnb(LnbCallback cb, @CallbackExecutor @Nullable Executor executor) { + public Lnb openLnbByName(@Nullable String name, @CallbackExecutor @Nullable Executor executor, + LnbCallback cb) { TunerUtils.checkTunerPermission(mContext); // TODO: use resource manager to get LNB ID. return new Lnb(0); @@ -608,17 +679,17 @@ public final class Tuner implements AutoCloseable { * @param type the DVR type to be opened. * @param bufferSize the buffer size of the output in bytes. It's used to hold output data of * the attached filters. - * @param cb the callback to receive notifications from DVR. * @param executor the executor on which callback will be invoked. The default event handler * executor is used if it's {@code null}. + * @param cb the callback to receive notifications from DVR. * @return the opened DVR object. {@code null} if the operation failed. * * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Nullable - public Dvr openDvr(@DvrSettings.Type int type, @BytesLong long bufferSize, DvrCallback cb, - @CallbackExecutor @Nullable Executor executor) { + public Dvr openDvr(@DvrSettings.Type int type, @BytesLong long bufferSize, + @CallbackExecutor @Nullable Executor executor, DvrCallback cb) { TunerUtils.checkTunerPermission(mContext); Dvr dvr = nativeOpenDvr(type, bufferSize); return dvr; diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java index fa8f550867b8..20b77e663586 100644 --- a/media/java/android/media/tv/tuner/TunerConstants.java +++ b/media/java/android/media/tv/tuner/TunerConstants.java @@ -36,68 +36,17 @@ import java.lang.annotation.RetentionPolicy; */ @SystemApi public final class TunerConstants { - /** @hide */ + /** + * Invalid TS packet ID. + * @hide + */ public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID; - /** @hide */ + /** + * Invalid stream ID. + * @hide + */ public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID; - - /** @hide */ - @IntDef({FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL, - FRONTEND_EVENT_TYPE_LOST_LOCK}) - @Retention(RetentionPolicy.SOURCE) - public @interface FrontendEventType {} - /** @hide */ - public static final int FRONTEND_EVENT_TYPE_LOCKED = Constants.FrontendEventType.LOCKED; - /** @hide */ - public static final int FRONTEND_EVENT_TYPE_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL; - /** @hide */ - public static final int FRONTEND_EVENT_TYPE_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK; - - - /** @hide */ - @IntDef({FILTER_SUBTYPE_UNDEFINED, FILTER_SUBTYPE_SECTION, FILTER_SUBTYPE_PES, - FILTER_SUBTYPE_AUDIO, FILTER_SUBTYPE_VIDEO, FILTER_SUBTYPE_DOWNLOAD, - FILTER_SUBTYPE_RECORD, FILTER_SUBTYPE_TS, FILTER_SUBTYPE_PCR, FILTER_SUBTYPE_TEMI, - FILTER_SUBTYPE_MMPT, FILTER_SUBTYPE_NTP, FILTER_SUBTYPE_IP_PAYLOAD, FILTER_SUBTYPE_IP, - FILTER_SUBTYPE_PAYLOAD_THROUGH, FILTER_SUBTYPE_TLV, FILTER_SUBTYPE_PTP, }) - @Retention(RetentionPolicy.SOURCE) - public @interface FilterSubtype {} - /** @hide */ - public static final int FILTER_SUBTYPE_UNDEFINED = 0; - /** @hide */ - public static final int FILTER_SUBTYPE_SECTION = 1; - /** @hide */ - public static final int FILTER_SUBTYPE_PES = 2; - /** @hide */ - public static final int FILTER_SUBTYPE_AUDIO = 3; - /** @hide */ - public static final int FILTER_SUBTYPE_VIDEO = 4; - /** @hide */ - public static final int FILTER_SUBTYPE_DOWNLOAD = 5; - /** @hide */ - public static final int FILTER_SUBTYPE_RECORD = 6; - /** @hide */ - public static final int FILTER_SUBTYPE_TS = 7; - /** @hide */ - public static final int FILTER_SUBTYPE_PCR = 8; - /** @hide */ - public static final int FILTER_SUBTYPE_TEMI = 9; - /** @hide */ - public static final int FILTER_SUBTYPE_MMPT = 10; - /** @hide */ - public static final int FILTER_SUBTYPE_NTP = 11; - /** @hide */ - public static final int FILTER_SUBTYPE_IP_PAYLOAD = 12; - /** @hide */ - public static final int FILTER_SUBTYPE_IP = 13; - /** @hide */ - public static final int FILTER_SUBTYPE_PAYLOAD_THROUGH = 14; - /** @hide */ - public static final int FILTER_SUBTYPE_TLV = 15; - /** @hide */ - public static final int FILTER_SUBTYPE_PTP = 16; - /** @hide */ @IntDef(flag = true, prefix = "FILTER_STATUS_", value = {FILTER_STATUS_DATA_READY, FILTER_STATUS_LOW_WATER, FILTER_STATUS_HIGH_WATER, FILTER_STATUS_OVERFLOW}) @@ -245,20 +194,6 @@ public final class TunerConstants { public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA = Constants.DemuxScHevcIndex.SLICE_TRAIL_CRA; - - /** @hide */ - @IntDef({FRONTEND_SCAN_UNDEFINED, FRONTEND_SCAN_AUTO, FRONTEND_SCAN_BLIND}) - @Retention(RetentionPolicy.SOURCE) - public @interface FrontendScanType {} - /** @hide */ - public static final int FRONTEND_SCAN_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED; - /** @hide */ - public static final int FRONTEND_SCAN_AUTO = Constants.FrontendScanType.SCAN_AUTO; - /** @hide */ - public static final int FRONTEND_SCAN_BLIND = Constants.FrontendScanType.SCAN_BLIND; - - - /** @hide */ @LongDef({FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5, FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8, @@ -502,23 +437,6 @@ public final class TunerConstants { /** @hide */ - @IntDef({FILTER_SETTINGS_TS, FILTER_SETTINGS_MMTP, FILTER_SETTINGS_IP, FILTER_SETTINGS_TLV, - FILTER_SETTINGS_ALP}) - @Retention(RetentionPolicy.SOURCE) - public @interface FilterSettingsType {} - /** @hide */ - public static final int FILTER_SETTINGS_TS = Constants.DemuxFilterMainType.TS; - /** @hide */ - public static final int FILTER_SETTINGS_MMTP = Constants.DemuxFilterMainType.MMTP; - /** @hide */ - public static final int FILTER_SETTINGS_IP = Constants.DemuxFilterMainType.IP; - /** @hide */ - public static final int FILTER_SETTINGS_TLV = Constants.DemuxFilterMainType.TLV; - /** @hide */ - public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP; - - - /** @hide */ @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE, RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java index 8780b726e3b2..30aaa0271eb0 100644 --- a/media/java/android/media/tv/tuner/TunerUtils.java +++ b/media/java/android/media/tv/tuner/TunerUtils.java @@ -19,9 +19,7 @@ package android.media.tv.tuner; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.tv.tuner.V1_0.Constants; -import android.media.tv.tuner.TunerConstants.FilterSubtype; -import android.media.tv.tuner.filter.FilterConfiguration; -import android.media.tv.tuner.filter.FilterConfiguration.FilterType; +import android.media.tv.tuner.filter.Filter; /** * Utility class for tuner framework. @@ -50,91 +48,91 @@ public final class TunerUtils { * @param mainType filter main type. * @param subtype filter subtype. */ - public static int getFilterSubtype(@FilterType int mainType, @FilterSubtype int subtype) { - if (mainType == FilterConfiguration.FILTER_TYPE_TS) { + public static int getFilterSubtype(@Filter.Type int mainType, @Filter.Subtype int subtype) { + if (mainType == Filter.TYPE_TS) { switch (subtype) { - case TunerConstants.FILTER_SUBTYPE_UNDEFINED: + case Filter.SUBTYPE_UNDEFINED: return Constants.DemuxTsFilterType.UNDEFINED; - case TunerConstants.FILTER_SUBTYPE_SECTION: + case Filter.SUBTYPE_SECTION: return Constants.DemuxTsFilterType.SECTION; - case TunerConstants.FILTER_SUBTYPE_PES: + case Filter.SUBTYPE_PES: return Constants.DemuxTsFilterType.PES; - case TunerConstants.FILTER_SUBTYPE_TS: + case Filter.SUBTYPE_TS: return Constants.DemuxTsFilterType.TS; - case TunerConstants.FILTER_SUBTYPE_AUDIO: + case Filter.SUBTYPE_AUDIO: return Constants.DemuxTsFilterType.AUDIO; - case TunerConstants.FILTER_SUBTYPE_VIDEO: + case Filter.SUBTYPE_VIDEO: return Constants.DemuxTsFilterType.VIDEO; - case TunerConstants.FILTER_SUBTYPE_PCR: + case Filter.SUBTYPE_PCR: return Constants.DemuxTsFilterType.PCR; - case TunerConstants.FILTER_SUBTYPE_RECORD: + case Filter.SUBTYPE_RECORD: return Constants.DemuxTsFilterType.RECORD; - case TunerConstants.FILTER_SUBTYPE_TEMI: + case Filter.SUBTYPE_TEMI: return Constants.DemuxTsFilterType.TEMI; default: break; } - } else if (mainType == FilterConfiguration.FILTER_TYPE_MMTP) { + } else if (mainType == Filter.TYPE_MMTP) { switch (subtype) { - case TunerConstants.FILTER_SUBTYPE_UNDEFINED: + case Filter.SUBTYPE_UNDEFINED: return Constants.DemuxMmtpFilterType.UNDEFINED; - case TunerConstants.FILTER_SUBTYPE_SECTION: + case Filter.SUBTYPE_SECTION: return Constants.DemuxMmtpFilterType.SECTION; - case TunerConstants.FILTER_SUBTYPE_PES: + case Filter.SUBTYPE_PES: return Constants.DemuxMmtpFilterType.PES; - case TunerConstants.FILTER_SUBTYPE_MMPT: + case Filter.SUBTYPE_MMTP: return Constants.DemuxMmtpFilterType.MMTP; - case TunerConstants.FILTER_SUBTYPE_AUDIO: + case Filter.SUBTYPE_AUDIO: return Constants.DemuxMmtpFilterType.AUDIO; - case TunerConstants.FILTER_SUBTYPE_VIDEO: + case Filter.SUBTYPE_VIDEO: return Constants.DemuxMmtpFilterType.VIDEO; - case TunerConstants.FILTER_SUBTYPE_RECORD: + case Filter.SUBTYPE_RECORD: return Constants.DemuxMmtpFilterType.RECORD; - case TunerConstants.FILTER_SUBTYPE_DOWNLOAD: + case Filter.SUBTYPE_DOWNLOAD: return Constants.DemuxMmtpFilterType.DOWNLOAD; default: break; } - } else if (mainType == FilterConfiguration.FILTER_TYPE_IP) { + } else if (mainType == Filter.TYPE_IP) { switch (subtype) { - case TunerConstants.FILTER_SUBTYPE_UNDEFINED: + case Filter.SUBTYPE_UNDEFINED: return Constants.DemuxIpFilterType.UNDEFINED; - case TunerConstants.FILTER_SUBTYPE_SECTION: + case Filter.SUBTYPE_SECTION: return Constants.DemuxIpFilterType.SECTION; - case TunerConstants.FILTER_SUBTYPE_NTP: + case Filter.SUBTYPE_NTP: return Constants.DemuxIpFilterType.NTP; - case TunerConstants.FILTER_SUBTYPE_IP_PAYLOAD: + case Filter.SUBTYPE_IP_PAYLOAD: return Constants.DemuxIpFilterType.IP_PAYLOAD; - case TunerConstants.FILTER_SUBTYPE_IP: + case Filter.SUBTYPE_IP: return Constants.DemuxIpFilterType.IP; - case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH: + case Filter.SUBTYPE_PAYLOAD_THROUGH: return Constants.DemuxIpFilterType.PAYLOAD_THROUGH; default: break; } - } else if (mainType == FilterConfiguration.FILTER_TYPE_TLV) { + } else if (mainType == Filter.TYPE_TLV) { switch (subtype) { - case TunerConstants.FILTER_SUBTYPE_UNDEFINED: + case Filter.SUBTYPE_UNDEFINED: return Constants.DemuxTlvFilterType.UNDEFINED; - case TunerConstants.FILTER_SUBTYPE_SECTION: + case Filter.SUBTYPE_SECTION: return Constants.DemuxTlvFilterType.SECTION; - case TunerConstants.FILTER_SUBTYPE_TLV: + case Filter.SUBTYPE_TLV: return Constants.DemuxTlvFilterType.TLV; - case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH: + case Filter.SUBTYPE_PAYLOAD_THROUGH: return Constants.DemuxTlvFilterType.PAYLOAD_THROUGH; default: break; } - } else if (mainType == FilterConfiguration.FILTER_TYPE_ALP) { + } else if (mainType == Filter.TYPE_ALP) { switch (subtype) { - case TunerConstants.FILTER_SUBTYPE_UNDEFINED: + case Filter.SUBTYPE_UNDEFINED: return Constants.DemuxAlpFilterType.UNDEFINED; - case TunerConstants.FILTER_SUBTYPE_SECTION: + case Filter.SUBTYPE_SECTION: return Constants.DemuxAlpFilterType.SECTION; - case TunerConstants.FILTER_SUBTYPE_PTP: + case Filter.SUBTYPE_PTP: return Constants.DemuxAlpFilterType.PTP; - case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH: + case Filter.SUBTYPE_PAYLOAD_THROUGH: return Constants.DemuxAlpFilterType.PAYLOAD_THROUGH; default: break; diff --git a/media/java/android/media/tv/tuner/dvr/Dvr.java b/media/java/android/media/tv/tuner/dvr/Dvr.java index 95508d3b366d..a17773c08a63 100644 --- a/media/java/android/media/tv/tuner/dvr/Dvr.java +++ b/media/java/android/media/tv/tuner/dvr/Dvr.java @@ -17,11 +17,16 @@ package android.media.tv.tuner.dvr; import android.annotation.BytesLong; +import android.annotation.IntDef; import android.annotation.NonNull; +import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.Tuner.Filter; import android.media.tv.tuner.TunerConstants.Result; import android.os.ParcelFileDescriptor; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Digital Video Record (DVR) interface provides record control on Demux's output buffer and * playback control on Demux's input buffer. @@ -29,6 +34,37 @@ import android.os.ParcelFileDescriptor; * @hide */ public class Dvr { + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "PLAYBACK_STATUS_", + value = {PLAYBACK_STATUS_EMPTY, PLAYBACK_STATUS_ALMOST_EMPTY, + PLAYBACK_STATUS_ALMOST_FULL, PLAYBACK_STATUS_FULL}) + @interface PlaybackStatus {} + + /** + * The space of the playback is empty. + */ + public static final int PLAYBACK_STATUS_EMPTY = Constants.PlaybackStatus.SPACE_EMPTY; + /** + * The space of the playback is almost empty. + * + * <p> the threshold is set in {@link DvrSettings}. + */ + public static final int PLAYBACK_STATUS_ALMOST_EMPTY = + Constants.PlaybackStatus.SPACE_ALMOST_EMPTY; + /** + * The space of the playback is almost full. + * + * <p> the threshold is set in {@link DvrSettings}. + */ + public static final int PLAYBACK_STATUS_ALMOST_FULL = + Constants.PlaybackStatus.SPACE_ALMOST_FULL; + /** + * The space of the playback is full. + */ + public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL; + + private long mNativeContext; private DvrCallback mCallback; diff --git a/media/java/android/media/tv/tuner/dvr/DvrCallback.java b/media/java/android/media/tv/tuner/dvr/DvrCallback.java index 3d140f0accac..ee0cfa7ddc2e 100644 --- a/media/java/android/media/tv/tuner/dvr/DvrCallback.java +++ b/media/java/android/media/tv/tuner/dvr/DvrCallback.java @@ -16,6 +16,9 @@ package android.media.tv.tuner.dvr; +import android.media.tv.tuner.TunerConstants.FilterStatus; +import android.media.tv.tuner.dvr.Dvr.PlaybackStatus; + /** * Callback interface for receiving information from DVR interfaces. * @@ -25,9 +28,9 @@ public interface DvrCallback { /** * Invoked when record status changed. */ - void onRecordStatusChanged(int status); + void onRecordStatusChanged(@FilterStatus int status); /** * Invoked when playback status changed. */ - void onPlaybackStatusChanged(int status); + void onPlaybackStatusChanged(@PlaybackStatus int status); } diff --git a/media/java/android/media/tv/tuner/dvr/DvrSettings.java b/media/java/android/media/tv/tuner/dvr/DvrSettings.java index 46efd7a33e90..49e875afc5ac 100644 --- a/media/java/android/media/tv/tuner/dvr/DvrSettings.java +++ b/media/java/android/media/tv/tuner/dvr/DvrSettings.java @@ -19,8 +19,11 @@ package android.media.tv.tuner.dvr; import android.annotation.BytesLong; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.TunerConstants.FilterStatus; +import android.media.tv.tuner.TunerUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -94,9 +97,13 @@ public class DvrSettings { /** * Creates a builder for {@link DvrSettings}. + * + * @param context the context of the caller. */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @NonNull - public static Builder newBuilder() { + public static Builder builder(Context context) { + TunerUtils.checkTunerPermission(context); return new Builder(); } diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java index 940b5ae44a74..93eaaa47d186 100644 --- a/media/java/android/media/tv/tuner/filter/AvSettings.java +++ b/media/java/android/media/tv/tuner/filter/AvSettings.java @@ -19,9 +19,7 @@ package android.media.tv.tuner.filter; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.content.Context; -import android.media.tv.tuner.TunerConstants; import android.media.tv.tuner.TunerUtils; -import android.media.tv.tuner.filter.FilterConfiguration.FilterType; /** * Filter Settings for a Video and Audio. @@ -35,8 +33,8 @@ public class AvSettings extends Settings { super(TunerUtils.getFilterSubtype( mainType, isAudio - ? TunerConstants.FILTER_SUBTYPE_AUDIO - : TunerConstants.FILTER_SUBTYPE_VIDEO)); + ? Filter.SUBTYPE_AUDIO + : Filter.SUBTYPE_VIDEO)); mIsPassthrough = isPassthrough; } @@ -57,7 +55,7 @@ public class AvSettings extends Settings { @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @NonNull public static Builder builder( - @NonNull Context context, @FilterType int mainType, boolean isAudio) { + @NonNull Context context, @Filter.Type int mainType, boolean isAudio) { TunerUtils.checkTunerPermission(context); return new Builder(mainType, isAudio); } diff --git a/media/java/android/media/tv/tuner/filter/DownloadSettings.java b/media/java/android/media/tv/tuner/filter/DownloadSettings.java index e3e1df064238..fa7744a00e4f 100644 --- a/media/java/android/media/tv/tuner/filter/DownloadSettings.java +++ b/media/java/android/media/tv/tuner/filter/DownloadSettings.java @@ -19,9 +19,7 @@ package android.media.tv.tuner.filter; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.content.Context; -import android.media.tv.tuner.TunerConstants; import android.media.tv.tuner.TunerUtils; -import android.media.tv.tuner.filter.FilterConfiguration.FilterType; /** * Filter Settings for a Download. @@ -31,7 +29,7 @@ public class DownloadSettings extends Settings { private final int mDownloadId; private DownloadSettings(int mainType, int downloadId) { - super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_DOWNLOAD)); + super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_DOWNLOAD)); mDownloadId = downloadId; } @@ -50,7 +48,7 @@ public class DownloadSettings extends Settings { */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @NonNull - public static Builder builder(@NonNull Context context, @FilterType int mainType) { + public static Builder builder(@NonNull Context context, @Filter.Type int mainType) { TunerUtils.checkTunerPermission(context); return new Builder(mainType); } diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 804c0c53982f..3f6154be6af6 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -17,9 +17,15 @@ package android.media.tv.tuner.filter; import android.annotation.BytesLong; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.Tuner.FilterCallback; +import android.media.tv.tuner.TunerConstants.Result; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Tuner data filter. @@ -29,6 +35,128 @@ import android.media.tv.tuner.Tuner.FilterCallback; * @hide */ public class Filter implements AutoCloseable { + /** @hide */ + @IntDef(prefix = "TYPE_", + value = {TYPE_TS, TYPE_MMTP, TYPE_IP, TYPE_TLV, TYPE_ALP}) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + /** + * TS filter type. + */ + public static final int TYPE_TS = Constants.DemuxFilterMainType.TS; + /** + * MMTP filter type. + */ + public static final int TYPE_MMTP = Constants.DemuxFilterMainType.MMTP; + /** + * IP filter type. + */ + public static final int TYPE_IP = Constants.DemuxFilterMainType.IP; + /** + * TLV filter type. + */ + public static final int TYPE_TLV = Constants.DemuxFilterMainType.TLV; + /** + * ALP filter type. + */ + public static final int TYPE_ALP = Constants.DemuxFilterMainType.ALP; + + /** @hide */ + @IntDef(prefix = "SUBTYPE_", + value = {SUBTYPE_UNDEFINED, SUBTYPE_SECTION, SUBTYPE_PES, SUBTYPE_AUDIO, SUBTYPE_VIDEO, + SUBTYPE_DOWNLOAD, SUBTYPE_RECORD, SUBTYPE_TS, SUBTYPE_PCR, SUBTYPE_TEMI, + SUBTYPE_MMTP, SUBTYPE_NTP, SUBTYPE_IP_PAYLOAD, SUBTYPE_IP, + SUBTYPE_PAYLOAD_THROUGH, SUBTYPE_TLV, SUBTYPE_PTP, }) + @Retention(RetentionPolicy.SOURCE) + public @interface Subtype {} + /** + * Filter subtype undefined. + * @hide + */ + public static final int SUBTYPE_UNDEFINED = 0; + /** + * Section filter subtype. + * @hide + */ + public static final int SUBTYPE_SECTION = 1; + /** + * PES filter subtype. + * @hide + */ + public static final int SUBTYPE_PES = 2; + /** + * Audio filter subtype. + * @hide + */ + public static final int SUBTYPE_AUDIO = 3; + /** + * Video filter subtype. + * @hide + */ + public static final int SUBTYPE_VIDEO = 4; + /** + * Download filter subtype. + * @hide + */ + public static final int SUBTYPE_DOWNLOAD = 5; + /** + * Record filter subtype. + * @hide + */ + public static final int SUBTYPE_RECORD = 6; + /** + * TS filter subtype. + * @hide + */ + public static final int SUBTYPE_TS = 7; + /** + * PCR filter subtype. + * @hide + */ + public static final int SUBTYPE_PCR = 8; + /** + * TEMI filter subtype. + * @hide + */ + public static final int SUBTYPE_TEMI = 9; + /** + * MMTP filter subtype. + * @hide + */ + public static final int SUBTYPE_MMTP = 10; + /** + * NTP filter subtype. + * @hide + */ + public static final int SUBTYPE_NTP = 11; + /** + * Payload filter subtype. + * @hide + */ + public static final int SUBTYPE_IP_PAYLOAD = 12; + /** + * IP filter subtype. + * @hide + */ + public static final int SUBTYPE_IP = 13; + /** + * Payload through filter subtype. + * @hide + */ + public static final int SUBTYPE_PAYLOAD_THROUGH = 14; + /** + * TLV filter subtype. + * @hide + */ + public static final int SUBTYPE_TLV = 15; + /** + * PTP filter subtype. + * @hide + */ + public static final int SUBTYPE_PTP = 16; + + private long mNativeContext; private FilterCallback mCallback; private final int mId; @@ -56,6 +184,7 @@ public class Filter implements AutoCloseable { * @param config the configuration of the filter. * @return result status of the operation. */ + @Result public int configure(@NonNull FilterConfiguration config) { int subType = -1; Settings s = config.getSettings(); @@ -68,6 +197,7 @@ public class Filter implements AutoCloseable { /** * Gets the filter Id. */ + @Result public int getId() { return nativeGetId(); } @@ -84,6 +214,7 @@ public class Filter implements AutoCloseable { * use demux as data source if the filter instance is NULL. * @return result status of the operation. */ + @Result public int setDataSource(@Nullable Filter source) { return nativeSetDataSource(source); } @@ -93,6 +224,7 @@ public class Filter implements AutoCloseable { * * @return result status of the operation. */ + @Result public int start() { return nativeStartFilter(); } @@ -103,6 +235,7 @@ public class Filter implements AutoCloseable { * * @return result status of the operation. */ + @Result public int stop() { return nativeStopFilter(); } @@ -112,6 +245,7 @@ public class Filter implements AutoCloseable { * * @return result status of the operation. */ + @Result public int flush() { return nativeFlushFilter(); } @@ -124,6 +258,7 @@ public class Filter implements AutoCloseable { * @param size the maximum number of bytes to read. * @return the number of bytes read. */ + @Result public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) { size = Math.min(size, buffer.length - offset); return nativeRead(buffer, offset, size); diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java index 68c722f244d4..c901e2b59185 100644 --- a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java @@ -32,7 +32,10 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public abstract class FilterConfiguration { - /** @hide */ + /** + * TODO: moved to Filter. Remove it here. + * @hide + */ @IntDef(prefix = "FILTER_TYPE_", value = {FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java index 37f94ae377ae..0b5c56ba4429 100644 --- a/media/java/android/media/tv/tuner/filter/MediaEvent.java +++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java @@ -16,6 +16,7 @@ package android.media.tv.tuner.filter; +import android.annotation.BytesLong; import android.annotation.Nullable; import android.media.tv.tuner.Tuner.Filter; @@ -28,23 +29,27 @@ public class MediaEvent extends FilterEvent{ private final int mStreamId; private final boolean mIsPtsPresent; private final long mPts; - private final int mDataLength; + private final long mDataLength; + private final long mOffset; private final Object mLinearBuffer; private final boolean mIsSecureMemory; + private final long mDataId; private final int mMpuSequenceNumber; private final boolean mIsPrivateData; private final AudioDescriptor mExtraMetaData; // This constructor is used by JNI code only - private MediaEvent(int streamId, boolean isPtsPresent, long pts, int dataLength, Object buffer, - boolean isSecureMemory, int mpuSequenceNumber, boolean isPrivateData, - AudioDescriptor extraMetaData) { + private MediaEvent(int streamId, boolean isPtsPresent, long pts, long dataLength, long offset, + Object buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber, + boolean isPrivateData, AudioDescriptor extraMetaData) { mStreamId = streamId; mIsPtsPresent = isPtsPresent; mPts = pts; mDataLength = dataLength; + mOffset = offset; mLinearBuffer = buffer; mIsSecureMemory = isSecureMemory; + mDataId = dataId; mMpuSequenceNumber = mpuSequenceNumber; mIsPrivateData = isPrivateData; mExtraMetaData = extraMetaData; @@ -76,11 +81,20 @@ public class MediaEvent extends FilterEvent{ /** * Gets data size in bytes of audio or video frame. */ - public int getDataLength() { + @BytesLong + public long getDataLength() { return mDataLength; } /** + * The offset in the memory block which is shared among multiple Media Events. + */ + @BytesLong + public long getOffset() { + return mOffset; + } + + /** * Gets a linear buffer associated to the memory where audio or video data stays. * TODO: use LinearBuffer when it's ready. * @@ -101,6 +115,15 @@ public class MediaEvent extends FilterEvent{ } /** + * Gets the ID which is used by HAL to provide additional information for AV data. + * + * <p>For secure audio, it's the audio handle used by Audio Track. + */ + public long getAvDataId() { + return mDataId; + } + + /** * Gets MPU sequence number of filtered data. */ public int getMpuSequenceNumber() { diff --git a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java index 83246e51c8b6..248f23a1797b 100644 --- a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java @@ -39,7 +39,7 @@ public class MmtpFilterConfiguration extends FilterConfiguration { } /** - * Gets MMPT PID. + * Gets MMTP PID. * * <p>Packet ID is used to specify packets in MMTP. */ @@ -69,7 +69,7 @@ public class MmtpFilterConfiguration extends FilterConfiguration { } /** - * Sets MMPT PID. + * Sets MMTP PID. */ @NonNull public Builder setMmtpPid(int mmtpPid) { diff --git a/media/java/android/media/tv/tuner/filter/PesSettings.java b/media/java/android/media/tv/tuner/filter/PesSettings.java index bfa1f8c67d97..0f83597ff43f 100644 --- a/media/java/android/media/tv/tuner/filter/PesSettings.java +++ b/media/java/android/media/tv/tuner/filter/PesSettings.java @@ -20,9 +20,7 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; -import android.media.tv.tuner.TunerConstants; import android.media.tv.tuner.TunerUtils; -import android.media.tv.tuner.filter.FilterConfiguration.FilterType; /** * Filter Settings for a PES Data. @@ -34,8 +32,8 @@ public class PesSettings extends Settings { private final int mStreamId; private final boolean mIsRaw; - private PesSettings(@FilterType int mainType, int streamId, boolean isRaw) { - super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_PES)); + private PesSettings(@Filter.Type int mainType, int streamId, boolean isRaw) { + super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_PES)); mStreamId = streamId; mIsRaw = isRaw; } @@ -65,7 +63,7 @@ public class PesSettings extends Settings { */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @NonNull - public static Builder builder(@NonNull Context context, @FilterType int mainType) { + public static Builder builder(@NonNull Context context, @Filter.Type int mainType) { TunerUtils.checkTunerPermission(context); return new Builder(mainType); } diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java index 483370935352..4e9d67f60db4 100644 --- a/media/java/android/media/tv/tuner/filter/RecordSettings.java +++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java @@ -24,7 +24,6 @@ import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.TunerConstants; import android.media.tv.tuner.TunerConstants.ScIndexType; import android.media.tv.tuner.TunerUtils; -import android.media.tv.tuner.filter.FilterConfiguration.FilterType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -151,7 +150,7 @@ public class RecordSettings extends Settings { private final int mScIndexMask; private RecordSettings(int mainType, int tsIndexType, int scIndexType, int scIndexMask) { - super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_RECORD)); + super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_RECORD)); mTsIndexMask = tsIndexType; mScIndexType = scIndexType; mScIndexMask = scIndexMask; @@ -187,7 +186,7 @@ public class RecordSettings extends Settings { */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @NonNull - public static Builder builder(@NonNull Context context, @FilterType int mainType) { + public static Builder builder(@NonNull Context context, @Filter.Type int mainType) { TunerUtils.checkTunerPermission(context); return new Builder(mainType); } diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java index 36e3d7cfee43..b8d0fad5e06c 100644 --- a/media/java/android/media/tv/tuner/filter/SectionSettings.java +++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java @@ -16,7 +16,6 @@ package android.media.tv.tuner.filter; -import android.media.tv.tuner.TunerConstants; import android.media.tv.tuner.TunerUtils; /** @@ -26,6 +25,6 @@ import android.media.tv.tuner.TunerUtils; public class SectionSettings extends Settings { SectionSettings(int mainType) { - super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_SECTION)); + super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_SECTION)); } } diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java index 0fa982e3dd01..a2d42d8cfe50 100644 --- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java +++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.content.Context; import android.media.tv.tuner.TunerUtils; -import android.media.tv.tuner.filter.FilterConfiguration.FilterType; /** * Bits Settings for Section Filters. @@ -73,7 +72,7 @@ public class SectionSettingsWithSectionBits extends SectionSettings { */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @NonNull - public static Builder builder(@NonNull Context context, @FilterType int mainType) { + public static Builder builder(@NonNull Context context, @Filter.Type int mainType) { TunerUtils.checkTunerPermission(context); return new Builder(mainType); } diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java index 6542b89478b2..0c9cd2bc9e56 100644 --- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java +++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.content.Context; import android.media.tv.tuner.TunerUtils; -import android.media.tv.tuner.filter.FilterConfiguration.FilterType; /** * Table information for Section Filter. @@ -57,7 +56,7 @@ public class SectionSettingsWithTableInfo extends SectionSettings { */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @NonNull - public static Builder builder(@NonNull Context context, @FilterType int mainType) { + public static Builder builder(@NonNull Context context, @Filter.Type int mainType) { TunerUtils.checkTunerPermission(context); return new Builder(mainType); } diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java index a30ddc73ac45..614d79ce99c5 100644 --- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java @@ -18,7 +18,11 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; +import android.media.tv.tuner.TunerUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -28,6 +32,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@SystemApi public class AnalogFrontendSettings extends FrontendSettings { /** @hide */ @IntDef(flag = true, @@ -137,7 +142,7 @@ public class AnalogFrontendSettings extends FrontendSettings { public static final int SIF_L_PRIME = Constants.FrontendAnalogSifStandard.L_PRIME; - private final int mAnalogType; + private final int mSignalType; private final int mSifStandard; @Override @@ -150,8 +155,8 @@ public class AnalogFrontendSettings extends FrontendSettings { * Gets analog signal type. */ @SignalType - public int getAnalogType() { - return mAnalogType; + public int getSignalType() { + return mSignalType; } /** @@ -164,43 +169,37 @@ public class AnalogFrontendSettings extends FrontendSettings { /** * Creates a builder for {@link AnalogFrontendSettings}. + * + * @param the context of the caller. */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @NonNull - public static Builder newBuilder() { + public static Builder builder(@NonNull Context context) { + TunerUtils.checkTunerPermission(context); return new Builder(); } - private AnalogFrontendSettings(int frequency, int analogType, int sifStandard) { + private AnalogFrontendSettings(int frequency, int signalType, int sifStandard) { super(frequency); - mAnalogType = analogType; + mSignalType = signalType; mSifStandard = sifStandard; } /** * Builder for {@link AnalogFrontendSettings}. */ - public static class Builder { - private int mFrequency; - private int mAnalogType; + public static class Builder extends FrontendSettings.Builder<Builder> { + private int mSignalType; private int mSifStandard; private Builder() {} /** - * Sets frequency in Hz. + * Sets analog signal type. */ @NonNull - public Builder setFrequency(int frequency) { - mFrequency = frequency; - return this; - } - - /** - * Sets analog type. - */ - @NonNull - public Builder setAnalogType(@SignalType int analogType) { - mAnalogType = analogType; + public Builder setASignalType(@SignalType int signalType) { + mSignalType = signalType; return this; } @@ -218,7 +217,12 @@ public class AnalogFrontendSettings extends FrontendSettings { */ @NonNull public AnalogFrontendSettings build() { - return new AnalogFrontendSettings(mFrequency, mAnalogType, mSifStandard); + return new AnalogFrontendSettings(mFrequency, mSignalType, mSifStandard); + } + + @Override + Builder self() { + return this; } } } diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java index 5b3bffc44741..44dbcc01b16e 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java @@ -112,31 +112,31 @@ public class DvbsFrontendSettings extends FrontendSettings { public @interface Rolloff {} /** - * Roll Off undefined. + * Rolloff range undefined. */ public static final int ROLLOFF_UNDEFINED = Constants.FrontendDvbsRolloff.UNDEFINED; /** - * Roll Off 0_35. + * Rolloff range 0,35. */ public static final int ROLLOFF_0_35 = Constants.FrontendDvbsRolloff.ROLLOFF_0_35; /** - * Roll Off 0_25. + * Rolloff range 0,25. */ public static final int ROLLOFF_0_25 = Constants.FrontendDvbsRolloff.ROLLOFF_0_25; /** - * Roll Off 0_2. + * Rolloff range 0,20. */ public static final int ROLLOFF_0_20 = Constants.FrontendDvbsRolloff.ROLLOFF_0_20; /** - * Roll Off 0_15. + * Rolloff range 0,15. */ public static final int ROLLOFF_0_15 = Constants.FrontendDvbsRolloff.ROLLOFF_0_15; /** - * Roll Off 0_1. + * Rolloff range 0,10. */ public static final int ROLLOFF_0_10 = Constants.FrontendDvbsRolloff.ROLLOFF_0_10; /** - * Roll Off 0_5. + * Rolloff range 0,5. */ public static final int ROLLOFF_0_5 = Constants.FrontendDvbsRolloff.ROLLOFF_0_5; @@ -188,6 +188,25 @@ public class DvbsFrontendSettings extends FrontendSettings { */ public static final int STANDARD_S2X = Constants.FrontendDvbsStandard.S2X; + /** @hide */ + @IntDef(prefix = "VCM_MODE_", + value = {VCM_MODE_UNDEFINED, VCM_MODE_AUTO, VCM_MODE_MANUAL}) + @Retention(RetentionPolicy.SOURCE) + public @interface VcmMode {} + + /** + * VCM mode undefined. + */ + public static final int VCM_MODE_UNDEFINED = Constants.FrontendDvbsVcmMode.UNDEFINED; + /** + * Auto VCM mode. + */ + public static final int VCM_MODE_AUTO = Constants.FrontendDvbsVcmMode.AUTO; + /** + * Manual VCM mode. + */ + public static final int VCM_MODE_MANUAL = Constants.FrontendDvbsVcmMode.MANUAL; + private final int mModulation; private final DvbsCodeRate mCoderate; @@ -196,9 +215,10 @@ public class DvbsFrontendSettings extends FrontendSettings { private final int mPilot; private final int mInputStreamId; private final int mStandard; + private final int mVcmMode; private DvbsFrontendSettings(int frequency, int modulation, DvbsCodeRate coderate, - int symbolRate, int rolloff, int pilot, int inputStreamId, int standard) { + int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm) { super(frequency); mModulation = modulation; mCoderate = coderate; @@ -207,6 +227,7 @@ public class DvbsFrontendSettings extends FrontendSettings { mPilot = pilot; mInputStreamId = inputStreamId; mStandard = standard; + mVcmMode = vcm; } /** @@ -256,6 +277,13 @@ public class DvbsFrontendSettings extends FrontendSettings { public int getStandard() { return mStandard; } + /** + * Gets VCM mode. + */ + @VcmMode + public int getVcmMode() { + return mVcmMode; + } /** * Creates a builder for {@link DvbsFrontendSettings}. @@ -280,6 +308,7 @@ public class DvbsFrontendSettings extends FrontendSettings { private int mPilot; private int mInputStreamId; private int mStandard; + private int mVcmMode; private Builder() { } @@ -340,6 +369,14 @@ public class DvbsFrontendSettings extends FrontendSettings { mStandard = standard; return this; } + /** + * Sets VCM mode. + */ + @NonNull + public Builder setVcmMode(@VcmMode int vcm) { + mVcmMode = vcm; + return this; + } /** * Builds a {@link DvbsFrontendSettings} object. @@ -347,7 +384,7 @@ public class DvbsFrontendSettings extends FrontendSettings { @NonNull public DvbsFrontendSettings build() { return new DvbsFrontendSettings(mFrequency, mModulation, mCoderate, mSymbolRate, - mRolloff, mPilot, mInputStreamId, mStandard); + mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode); } @Override diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java index f0469b71f32c..9a82de0b6db3 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java @@ -110,7 +110,7 @@ public class DvbtFrontendSettings extends FrontendSettings { */ public static final int BANDWIDTH_5MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_5MHZ; /** - * 1.7 MHz bandwidth. + * 1,7 MHz bandwidth. */ public static final int BANDWIDTH_1_7MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_1_7MHZ; /** @@ -232,39 +232,39 @@ public class DvbtFrontendSettings extends FrontendSettings { */ public static final int CODERATE_AUTO = Constants.FrontendDvbtCoderate.AUTO; /** - * 1_2 code rate. + * 1/2 code rate. */ public static final int CODERATE_1_2 = Constants.FrontendDvbtCoderate.CODERATE_1_2; /** - * 2_3 code rate. + * 2/3 code rate. */ public static final int CODERATE_2_3 = Constants.FrontendDvbtCoderate.CODERATE_2_3; /** - * 3_4 code rate. + * 3/4 code rate. */ public static final int CODERATE_3_4 = Constants.FrontendDvbtCoderate.CODERATE_3_4; /** - * 5_6 code rate. + * 5/6 code rate. */ public static final int CODERATE_5_6 = Constants.FrontendDvbtCoderate.CODERATE_5_6; /** - * 7_8 code rate. + * 7/8 code rate. */ public static final int CODERATE_7_8 = Constants.FrontendDvbtCoderate.CODERATE_7_8; /** - * 4_5 code rate. + * 4/5 code rate. */ public static final int CODERATE_3_5 = Constants.FrontendDvbtCoderate.CODERATE_3_5; /** - * 4_5 code rate. + * 4/5 code rate. */ public static final int CODERATE_4_5 = Constants.FrontendDvbtCoderate.CODERATE_4_5; /** - * 6_7 code rate. + * 6/7 code rate. */ public static final int CODERATE_6_7 = Constants.FrontendDvbtCoderate.CODERATE_6_7; /** - * 8_9 code rate. + * 8/9 code rate. */ public static final int CODERATE_8_9 = Constants.FrontendDvbtCoderate.CODERATE_8_9; diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java index 617d6089a610..b80b7cd5212a 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java @@ -19,6 +19,7 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; import java.lang.annotation.Retention; @@ -29,6 +30,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@SystemApi public abstract class FrontendSettings { /** @hide */ @IntDef({TYPE_UNDEFINED, TYPE_ANALOG, TYPE_ATSC, TYPE_ATSC3, TYPE_DVBC, TYPE_DVBS, TYPE_DVBT, diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java index 7e6f1888cc4e..a83d771af1a2 100644 --- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java @@ -87,47 +87,47 @@ public class Isdbs3FrontendSettings extends FrontendSettings { */ public static final int CODERATE_AUTO = Constants.FrontendIsdbs3Coderate.AUTO; /** - * 1_3 code rate. + * 1/3 code rate. */ public static final int CODERATE_1_3 = Constants.FrontendIsdbs3Coderate.CODERATE_1_3; /** - * 2_5 code rate. + * 2/5 code rate. */ public static final int CODERATE_2_5 = Constants.FrontendIsdbs3Coderate.CODERATE_2_5; /** - * 1_2 code rate. + * 1/2 code rate. */ public static final int CODERATE_1_2 = Constants.FrontendIsdbs3Coderate.CODERATE_1_2; /** - * 3_5 code rate. + * 3/5 code rate. */ public static final int CODERATE_3_5 = Constants.FrontendIsdbs3Coderate.CODERATE_3_5; /** - * 2_3 code rate. + * 2/3 code rate. */ public static final int CODERATE_2_3 = Constants.FrontendIsdbs3Coderate.CODERATE_2_3; /** - * 3_4 code rate. + * 3/4 code rate. */ public static final int CODERATE_3_4 = Constants.FrontendIsdbs3Coderate.CODERATE_3_4; /** - * 7_9 code rate. + * 7/9 code rate. */ public static final int CODERATE_7_9 = Constants.FrontendIsdbs3Coderate.CODERATE_7_9; /** - * 4_5 code rate. + * 4/5 code rate. */ public static final int CODERATE_4_5 = Constants.FrontendIsdbs3Coderate.CODERATE_4_5; /** - * 5_6 code rate. + * 5/6 code rate. */ public static final int CODERATE_5_6 = Constants.FrontendIsdbs3Coderate.CODERATE_5_6; /** - * 7_8 code rate. + * 7/8 code rate. */ public static final int CODERATE_7_8 = Constants.FrontendIsdbs3Coderate.CODERATE_7_8; /** - * 9_10 code rate. + * 9/10 code rate. */ public static final int CODERATE_9_10 = Constants.FrontendIsdbs3Coderate.CODERATE_9_10; @@ -138,11 +138,11 @@ public class Isdbs3FrontendSettings extends FrontendSettings { public @interface Rolloff {} /** - * Roll off type undefined. + * Rolloff type undefined. */ public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbs3Rolloff.UNDEFINED; /** - * 0.03 roll off type. + * 0,03 Rolloff. */ public static final int ROLLOFF_0_03 = Constants.FrontendIsdbs3Rolloff.ROLLOFF_0_03; diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java index fe100f8ecdd3..bb809bf8d455 100644 --- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java @@ -96,23 +96,23 @@ public class IsdbsFrontendSettings extends FrontendSettings { */ public static final int CODERATE_AUTO = Constants.FrontendIsdbsCoderate.AUTO; /** - * 1_2 code rate. + * 1/2 code rate. */ public static final int CODERATE_1_2 = Constants.FrontendIsdbsCoderate.CODERATE_1_2; /** - * 2_3 code rate. + * 2/3 code rate. */ public static final int CODERATE_2_3 = Constants.FrontendIsdbsCoderate.CODERATE_2_3; /** - * 3_4 code rate. + * 3/4 code rate. */ public static final int CODERATE_3_4 = Constants.FrontendIsdbsCoderate.CODERATE_3_4; /** - * 5_6 code rate. + * 5/6 code rate. */ public static final int CODERATE_5_6 = Constants.FrontendIsdbsCoderate.CODERATE_5_6; /** - * 7_8 code rate. + * 7/8 code rate. */ public static final int CODERATE_7_8 = Constants.FrontendIsdbsCoderate.CODERATE_7_8; @@ -123,11 +123,11 @@ public class IsdbsFrontendSettings extends FrontendSettings { public @interface Rolloff {} /** - * Roll off type undefined. + * Rolloff type undefined. */ public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbs3Rolloff.UNDEFINED; /** - * 0.35 roll off type. + * 0,35 rolloff. */ public static final int ROLLOFF_0_35 = Constants.FrontendIsdbsRolloff.ROLLOFF_0_35; diff --git a/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java b/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java new file mode 100644 index 000000000000..5cf0d319c7c9 --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.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.media.tv.tuner.frontend; + +import android.annotation.IntDef; +import android.annotation.SystemApi; +import android.hardware.tv.tuner.V1_0.Constants; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Listens for tune events. + * + * @hide + */ +@SystemApi +public interface OnTuneEventListener { + + /** @hide */ + @IntDef(prefix = "SIGNAL_", value = {SIGNAL_LOCKED, SIGNAL_NO_SIGNAL, SIGNAL_LOST_LOCK}) + @Retention(RetentionPolicy.SOURCE) + @interface TuneEvent {} + + /** The frontend has locked to the signal specified by the tune method. */ + int SIGNAL_LOCKED = Constants.FrontendEventType.LOCKED; + /** The frontend is unable to lock to the signal specified by the tune method. */ + int SIGNAL_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL; + /** The frontend has lost the lock to the signal specified by the tune method. */ + int SIGNAL_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK; + + /** Tune Event from the frontend */ + void onTuneEvent(@TuneEvent int tuneEvent); +} diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java index 5e7d2189706c..a825d6d486ae 100644 --- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java +++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java @@ -16,26 +16,55 @@ package android.media.tv.tuner.frontend; +import android.annotation.IntDef; +import android.hardware.tv.tuner.V1_0.Constants; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Scan callback. * * @hide */ public interface ScanCallback { + + /** @hide */ + @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND}) + @Retention(RetentionPolicy.SOURCE) + @interface ScanType {} + /** + * Scan type undefined. + */ + int SCAN_TYPE_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED; + /** + * Scan type auto. + * + * <p> Tuner will send {@link #onLocked} + */ + int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO; + /** + * Blind scan. + * + * <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an + * implementation specific range. + */ + int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND; + /** Scan locked the signal. */ - void onLocked(boolean isLocked); + void onLocked(); /** Scan stopped. */ - void onEnd(boolean isEnd); + void onScanStopped(); /** scan progress percent (0..100) */ void onProgress(int percent); - /** Signal frequency in Hertz */ - void onFrequencyReport(int frequency); + /** Signal frequencies in Hertz */ + void onFrequenciesReport(int[] frequency); /** Symbols per second */ - void onSymbolRate(int rate); + void onSymbolRates(int[] rate); /** Locked Plp Ids for DVBT2 frontend. */ void onPlpIds(int[] plpIds); @@ -46,15 +75,24 @@ public interface ScanCallback { /** Stream Ids. */ void onInputStreamIds(int[] inputStreamIds); - /** Locked signal standard. */ + /** Locked signal standard for DVBS. */ void onDvbsStandard(@DvbsFrontendSettings.Standard int dvbsStandandard); - /** Locked signal standard. */ + /** Locked signal standard. for DVBT */ void onDvbtStandard(@DvbtFrontendSettings.Standard int dvbtStandard); + /** Locked signal SIF standard for Analog. */ + void onAnalogSifStandard(@AnalogFrontendSettings.SifStandard int sif); + /** PLP status in a tuned frequency band for ATSC3 frontend. */ void onAtsc3PlpInfos(Atsc3PlpInfo[] atsc3PlpInfos); + /** Frontend hierarchy. */ + void onHierarchy(@DvbtFrontendSettings.Hierarchy int hierarchy); + + /** Frontend hierarchy. */ + void onSignalType(@AnalogFrontendSettings.SignalType int signalType); + /** PLP information for ATSC3. */ class Atsc3PlpInfo { private final int mPlpId; diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java index ed93112d5288..6595cae3c028 100644 --- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java @@ -19,10 +19,12 @@ package com.android.mediarouteprovider.example; import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER; import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV; +import android.annotation.Nullable; import android.content.Intent; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderService; import android.media.RoutingSessionInfo; +import android.os.Bundle; import android.os.IBinder; import android.text.TextUtils; @@ -167,8 +169,8 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onCreateSession(String packageName, String routeId, String routeFeature, - long requestId) { + public void onCreateSession(String packageName, String routeId, long requestId, + @Nullable Bundle sessionHints) { MediaRoute2Info route = mRoutes.get(routeId); if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) { // Tell the router that session cannot be created by passing null as sessionInfo. @@ -185,11 +187,12 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .build()); mRouteIdToSessionId.put(routeId, sessionId); - RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( - sessionId, packageName, routeFeature) + RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(sessionId, packageName) .addSelectedRoute(routeId) .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT) .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO) + // Set control hints with given sessionHints + .setControlHints(sessionHints) .build(); notifySessionCreated(sessionInfo, requestId); publishRoutes(); @@ -212,6 +215,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } } notifySessionReleased(sessionId); + publishRoutes(); } @Override @@ -267,6 +271,27 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService @Override public void onTransferToRoute(String sessionId, String routeId) { RoutingSessionInfo sessionInfo = getSessionInfo(sessionId); + MediaRoute2Info route = mRoutes.get(routeId); + + if (sessionInfo == null || route == null) { + return; + } + + for (String selectedRouteId : sessionInfo.getSelectedRoutes()) { + mRouteIdToSessionId.remove(selectedRouteId); + MediaRoute2Info selectedRoute = mRoutes.get(selectedRouteId); + if (selectedRoute != null) { + mRoutes.put(selectedRouteId, new MediaRoute2Info.Builder(selectedRoute) + .setClientPackageName(null) + .build()); + } + } + + mRoutes.put(routeId, new MediaRoute2Info.Builder(route) + .setClientPackageName(sessionInfo.getClientPackageName()) + .build()); + mRouteIdToSessionId.put(routeId, sessionId); + RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo) .clearSelectedRoutes() .addSelectedRoute(routeId) @@ -274,6 +299,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService .removeTransferrableRoute(routeId) .build(); notifySessionUpdated(newSessionInfo); + publishRoutes(); } void maybeDeselectRoute(String routeId) { diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java index 59e1122707a5..32d03db6f7ee 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java @@ -43,12 +43,14 @@ import android.annotation.NonNull; import android.content.Context; import android.media.MediaRoute2Info; import android.media.MediaRouter2; +import android.media.MediaRouter2.OnCreateSessionListener; import android.media.MediaRouter2.RouteCallback; import android.media.MediaRouter2.RoutingController; import android.media.MediaRouter2.SessionCallback; import android.media.RouteDiscoveryPreference; import android.media.RoutingSessionInfo; import android.net.Uri; +import android.os.Bundle; import android.os.Parcel; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; @@ -80,6 +82,9 @@ public class MediaRouter2Test { private static final int TIMEOUT_MS = 5000; + private static final String TEST_KEY = "test_key"; + private static final String TEST_VALUE = "test_value"; + @Before public void setUp() throws Exception { mContext = InstrumentationRegistry.getTargetContext(); @@ -172,21 +177,8 @@ public class MediaRouter2Test { } @Test - public void testRequestCreateSessionWithInvalidArguments() { - String routeFeature = "routeFeature"; - MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name") - .addFeature(routeFeature) - .build(); - - // Tests null route - assertThrows(NullPointerException.class, - () -> mRouter2.requestCreateSession(null, routeFeature)); - - // Tests null or empty route feature - assertThrows(IllegalArgumentException.class, - () -> mRouter2.requestCreateSession(route, null)); - assertThrows(IllegalArgumentException.class, - () -> mRouter2.requestCreateSession(route, "")); + public void testRequestCreateSessionWithNullRoute() { + assertThrows(NullPointerException.class, () -> mRouter2.requestCreateSession(null)); } @Test @@ -208,14 +200,12 @@ public class MediaRouter2Test { public void onSessionCreated(RoutingController controller) { assertNotNull(controller); assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)); - assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature())); controllers.add(controller); successLatch.countDown(); } @Override - public void onSessionCreationFailed(MediaRoute2Info requestedRoute, - String requestedRouteFeature) { + public void onSessionCreationFailed(MediaRoute2Info requestedRoute) { failureLatch.countDown(); } }; @@ -226,7 +216,7 @@ public class MediaRouter2Test { try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(route, FEATURE_SAMPLE); + mRouter2.requestCreateSession(route); assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); // onSessionCreationFailed should not be called. @@ -260,10 +250,8 @@ public class MediaRouter2Test { } @Override - public void onSessionCreationFailed(MediaRoute2Info requestedRoute, - String requestedRouteFeature) { + public void onSessionCreationFailed(MediaRoute2Info requestedRoute) { assertEquals(route, requestedRoute); - assertTrue(TextUtils.equals(FEATURE_SAMPLE, requestedRouteFeature)); failureLatch.countDown(); } }; @@ -274,7 +262,7 @@ public class MediaRouter2Test { try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(route, FEATURE_SAMPLE); + mRouter2.requestCreateSession(route); assertTrue(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); // onSessionCreated should not be called. @@ -304,8 +292,7 @@ public class MediaRouter2Test { } @Override - public void onSessionCreationFailed(MediaRoute2Info requestedRoute, - String requestedRouteFeature) { + public void onSessionCreationFailed(MediaRoute2Info requestedRoute) { failureLatch.countDown(); } }; @@ -322,8 +309,8 @@ public class MediaRouter2Test { try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(route1, FEATURE_SAMPLE); - mRouter2.requestCreateSession(route2, FEATURE_SAMPLE); + mRouter2.requestCreateSession(route1); + mRouter2.requestCreateSession(route2); assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); // onSessionCreationFailed should not be called. @@ -337,8 +324,6 @@ public class MediaRouter2Test { assertNotEquals(controller1.getSessionId(), controller2.getSessionId()); assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(ROUTE_ID1)); assertTrue(createRouteMap(controller2.getSelectedRoutes()).containsKey(ROUTE_ID2)); - assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller1.getRouteFeature())); - assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller2.getRouteFeature())); } finally { releaseControllers(createdControllers); @@ -348,6 +333,74 @@ public class MediaRouter2Test { } @Test + public void testSetOnCreateSessionListener() throws Exception { + final List<String> sampleRouteFeature = new ArrayList<>(); + sampleRouteFeature.add(FEATURE_SAMPLE); + + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteFeature); + MediaRoute2Info route = routes.get(ROUTE_ID1); + assertNotNull(route); + + final Bundle createSessionHints = new Bundle(); + createSessionHints.putString(TEST_KEY, TEST_VALUE); + final OnCreateSessionListener listener = new OnCreateSessionListener() { + @Override + public Bundle onCreateSession(MediaRoute2Info route) { + return createSessionHints; + } + }; + + final CountDownLatch successLatch = new CountDownLatch(1); + final CountDownLatch failureLatch = new CountDownLatch(1); + final List<RoutingController> controllers = new ArrayList<>(); + + // Create session with this route + SessionCallback sessionCallback = new SessionCallback() { + @Override + public void onSessionCreated(RoutingController controller) { + assertNotNull(controller); + assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)); + + // The SampleMediaRoute2ProviderService supposed to set control hints + // with the given creationSessionHints. + Bundle controlHints = controller.getControlHints(); + assertNotNull(controlHints); + assertTrue(controlHints.containsKey(TEST_KEY)); + assertEquals(TEST_VALUE, controlHints.getString(TEST_KEY)); + + controllers.add(controller); + successLatch.countDown(); + } + + @Override + public void onSessionCreationFailed(MediaRoute2Info requestedRoute) { + failureLatch.countDown(); + } + }; + + // TODO: Remove this once the MediaRouter2 becomes always connected to the service. + RouteCallback routeCallback = new RouteCallback(); + mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY); + + try { + mRouter2.registerSessionCallback(mExecutor, sessionCallback); + + // The SampleMediaRoute2ProviderService supposed to set control hints + // with the given creationSessionHints. + mRouter2.setOnCreateSessionListener(listener); + mRouter2.requestCreateSession(route); + assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + + // onSessionCreationFailed should not be called. + assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } finally { + releaseControllers(controllers); + mRouter2.unregisterRouteCallback(routeCallback); + mRouter2.unregisterSessionCallback(sessionCallback); + } + } + + @Test public void testSessionCallbackIsNotCalledAfterUnregistered() throws Exception { final List<String> sampleRouteType = new ArrayList<>(); sampleRouteType.add(FEATURE_SAMPLE); @@ -369,8 +422,7 @@ public class MediaRouter2Test { } @Override - public void onSessionCreationFailed(MediaRoute2Info requestedRoute, - String requestedRouteFeature) { + public void onSessionCreationFailed(MediaRoute2Info requestedRoute) { failureLatch.countDown(); } }; @@ -381,7 +433,7 @@ public class MediaRouter2Test { try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(route, FEATURE_SAMPLE); + mRouter2.requestCreateSession(route); // Unregisters session callback mRouter2.unregisterSessionCallback(sessionCallback); @@ -417,7 +469,6 @@ public class MediaRouter2Test { public void onSessionCreated(RoutingController controller) { assertNotNull(controller); assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1)); - assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature())); controllers.add(controller); onSessionCreatedLatch.countDown(); } @@ -470,7 +521,7 @@ public class MediaRouter2Test { try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE); + mRouter2.requestCreateSession(routeToCreateSessionWith); assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); assertEquals(1, controllers.size()); @@ -515,7 +566,6 @@ public class MediaRouter2Test { public void onSessionCreated(RoutingController controller) { assertNotNull(controller); assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1)); - assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature())); controllers.add(controller); onSessionCreatedLatch.countDown(); } @@ -552,7 +602,7 @@ public class MediaRouter2Test { try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE); + mRouter2.requestCreateSession(routeToCreateSessionWith); assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); assertEquals(1, controllers.size()); @@ -594,7 +644,6 @@ public class MediaRouter2Test { public void onSessionCreated(RoutingController controller) { assertNotNull(controller); assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1)); - assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature())); controllers.add(controller); onSessionCreatedLatch.countDown(); } @@ -617,7 +666,7 @@ public class MediaRouter2Test { try { mRouter2.registerSessionCallback(mExecutor, sessionCallback); - mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE); + mRouter2.requestCreateSession(routeToCreateSessionWith); assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); assertEquals(1, controllers.size()); diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index 83dd0c0fd301..cba8452fe9c2 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -31,6 +31,7 @@ import android.media.MediaRouter2.RouteCallback; import android.media.MediaRouter2.SessionCallback; import android.media.MediaRouter2Manager; import android.media.RouteDiscoveryPreference; +import android.media.RoutingSessionInfo; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -38,7 +39,6 @@ import android.text.TextUtils; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -138,6 +138,8 @@ public class MediaRouterManagerTest { @After public void tearDown() { + // order matters (callbacks should be cleared at the last) + releaseAllSessions(); // unregister callbacks clearCallbacks(); } @@ -223,110 +225,83 @@ public class MediaRouterManagerTest { MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1); assertNotNull(routeToSelect); - try { - mManager.selectRoute(mPackageName, routeToSelect); - assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - } finally { - //TODO: release the session - //mManager.selectRoute(mPackageName, null); - } - } - - /** - * Tests if MR2Manager.Callback.onRouteSelected is called - * when a route is selected by MR2Manager. - */ - @Test - @Ignore("TODO: test session created callback instead of onRouteSelected") - public void testManagerOnRouteSelected() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL); - - addRouterCallback(new RouteCallback()); - addManagerCallback(new MediaRouter2Manager.Callback() { - @Override - public void onRouteSelected(String packageName, MediaRoute2Info route) { - if (TextUtils.equals(mPackageName, packageName) - && route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) { - latch.countDown(); - } - } - }); - - MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1); - assertNotNull(routeToSelect); - - try { - mManager.selectRoute(mPackageName, routeToSelect); - assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - } finally { - //TODO: release the session - //mManager.selectRoute(mPackageName, null); - } + mManager.selectRoute(mPackageName, routeToSelect); + assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertEquals(1, mManager.getActiveSessions().size()); } @Test - @Ignore("TODO: enable this when 'releasing session' is implemented") - public void testGetActiveRoutes() throws Exception { + public void testGetRoutingControllers() throws Exception { CountDownLatch latch = new CountDownLatch(1); Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL); addRouterCallback(new RouteCallback()); addManagerCallback(new MediaRouter2Manager.Callback() { @Override - public void onRouteSelected(String packageName, MediaRoute2Info route) { - if (TextUtils.equals(mPackageName, packageName) - && route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) { + public void onSessionCreated(MediaRouter2Manager.RoutingController controller) { + if (TextUtils.equals(mPackageName, controller.getClientPackageName()) + && createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)) { latch.countDown(); } } }); - //TODO: it fails due to not releasing session - assertEquals(0, mManager.getActiveSessions().size()); + assertEquals(0, mManager.getRoutingControllers(mPackageName).size()); mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)); latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); - assertEquals(1, mManager.getActiveSessions().size()); + List<MediaRouter2Manager.RoutingController> controllers = + mManager.getRoutingControllers(mPackageName); - //TODO: release the session - /* + assertEquals(1, controllers.size()); + + MediaRouter2Manager.RoutingController routingController = controllers.get(0); awaitOnRouteChangedManager( - () -> mManager.selectRoute(mPackageName, null), + () -> routingController.release(), ROUTE_ID1, route -> TextUtils.equals(route.getClientPackageName(), null)); - assertEquals(0, mManager.getActiveRoutes().size()); - */ + assertEquals(0, mManager.getRoutingControllers(mPackageName).size()); } /** - * Tests selecting and unselecting routes of a single provider. + * Tests select, transfer, release of routes of a provider */ @Test - @Ignore("TODO: enable when session is released") - public void testSingleProviderSelect() throws Exception { + public void testSelectAndTransferAndRelease() throws Exception { Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL); addRouterCallback(new RouteCallback()); + CountDownLatch onSessionCreatedLatch = new CountDownLatch(1); + + addManagerCallback(new MediaRouter2Manager.Callback() { + @Override + public void onSessionCreated(MediaRouter2Manager.RoutingController controller) { + assertNotNull(controller); + onSessionCreatedLatch.countDown(); + } + }); awaitOnRouteChangedManager( () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)), ROUTE_ID1, route -> TextUtils.equals(route.getClientPackageName(), mPackageName)); + assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + + List<MediaRouter2Manager.RoutingController> controllers = + mManager.getRoutingControllers(mPackageName); + + assertEquals(1, controllers.size()); + MediaRouter2Manager.RoutingController routingController = controllers.get(0); awaitOnRouteChangedManager( - () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID2)), - ROUTE_ID2, + () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)), + ROUTE_ID5_TO_TRANSFER_TO, route -> TextUtils.equals(route.getClientPackageName(), mPackageName)); - //TODO: release the session - /* awaitOnRouteChangedManager( - () -> mManager.selectRoute(mPackageName, null), - ROUTE_ID2, + () -> routingController.release(), + ROUTE_ID5_TO_TRANSFER_TO, route -> TextUtils.equals(route.getClientPackageName(), null)); - - */ } @Test @@ -460,4 +435,13 @@ public class MediaRouterManagerTest { } mSessionCallbacks.clear(); } + + private void releaseAllSessions() { + // ensure ManagerRecord in MediaRouter2ServiceImpl + addManagerCallback(new MediaRouter2Manager.Callback()); + + for (RoutingSessionInfo session : mManager.getActiveSessions()) { + mManager.getControllerForSession(session).release(); + } + } } diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java index 3f5973615e32..704dca0427ed 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java @@ -57,65 +57,32 @@ public class RoutingSessionInfoTest { public void testBuilderConstructorWithInvalidValues() { final String nullId = null; final String nullClientPackageName = null; - final String nullRouteFeature = null; final String emptyId = ""; // Note: An empty string as client package name is valid. - final String emptyRouteFeature = ""; final String validId = TEST_ID; final String validClientPackageName = TEST_CLIENT_PACKAGE_NAME; - final String validRouteFeature = TEST_ROUTE_FEATURE; // ID is invalid assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - nullId, validClientPackageName, validRouteFeature)); + nullId, validClientPackageName)); assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - emptyId, validClientPackageName, validRouteFeature)); + emptyId, validClientPackageName)); // client package name is invalid (null) assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder( - validId, nullClientPackageName, validRouteFeature)); + validId, nullClientPackageName)); - // route feature is invalid + // Both are invalid assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - validId, validClientPackageName, nullRouteFeature)); + nullId, nullClientPackageName)); assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - validId, validClientPackageName, emptyRouteFeature)); - - // Two arguments are invalid - (1) ID and clientPackageName - assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - nullId, nullClientPackageName, validRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - emptyId, nullClientPackageName, validRouteFeature)); - - // Two arguments are invalid - (2) ID and routeFeature - assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - nullId, validClientPackageName, nullRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - nullId, validClientPackageName, emptyRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - emptyId, validClientPackageName, nullRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - emptyId, validClientPackageName, emptyRouteFeature)); - - // Two arguments are invalid - (3) clientPackageName and routeFeature - // Note that this throws NullPointerException. - assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder( - validId, nullClientPackageName, nullRouteFeature)); - assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder( - validId, nullClientPackageName, emptyRouteFeature)); - - // All arguments are invalid - assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - nullId, nullClientPackageName, nullRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - nullId, nullClientPackageName, emptyRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - emptyId, nullClientPackageName, nullRouteFeature)); - assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder( - emptyId, nullClientPackageName, emptyRouteFeature)); + emptyId, nullClientPackageName)); + } + @Test + public void testBuilderCopyConstructorWithNull() { // Null RouteInfo (1-argument constructor) final RoutingSessionInfo nullRoutingSessionInfo = null; assertThrows(NullPointerException.class, @@ -127,13 +94,13 @@ public class RoutingSessionInfoTest { // An empty string for client package name is valid. (for unknown cases) // Creating builder with it should not throw any exception. RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( - TEST_ID, "" /* clientPackageName*/, TEST_ROUTE_FEATURE); + TEST_ID, "" /* clientPackageName*/); } @Test public void testBuilderBuildWithEmptySelectedRoutesThrowsIAE() { RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE); + TEST_ID, TEST_CLIENT_PACKAGE_NAME); // Note: Calling build() without adding any selected routes. assertThrows(IllegalArgumentException.class, () -> builder.build()); } @@ -141,7 +108,7 @@ public class RoutingSessionInfoTest { @Test public void testBuilderAddRouteMethodsWithIllegalArgumentsThrowsIAE() { RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE); + TEST_ID, TEST_CLIENT_PACKAGE_NAME); final String nullRouteId = null; final String emptyRouteId = ""; @@ -168,7 +135,7 @@ public class RoutingSessionInfoTest { @Test public void testBuilderRemoveRouteMethodsWithIllegalArgumentsThrowsIAE() { RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE); + TEST_ID, TEST_CLIENT_PACKAGE_NAME); final String nullRouteId = null; final String emptyRouteId = ""; @@ -198,7 +165,7 @@ public class RoutingSessionInfoTest { controlHints.putString(TEST_KEY, TEST_VALUE); RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) + TEST_ID, TEST_CLIENT_PACKAGE_NAME) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) .addSelectableRoute(TEST_ROUTE_ID_2) @@ -212,7 +179,6 @@ public class RoutingSessionInfoTest { assertEquals(TEST_ID, sessionInfo.getId()); assertEquals(TEST_CLIENT_PACKAGE_NAME, sessionInfo.getClientPackageName()); - assertEquals(TEST_ROUTE_FEATURE, sessionInfo.getRouteFeature()); assertEquals(2, sessionInfo.getSelectedRoutes().size()); assertEquals(TEST_ROUTE_ID_0, sessionInfo.getSelectedRoutes().get(0)); @@ -239,7 +205,7 @@ public class RoutingSessionInfoTest { @Test public void testBuilderAddRouteMethodsWithBuilderCopyConstructor() { RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) + TEST_ID, TEST_CLIENT_PACKAGE_NAME) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectableRoute(TEST_ROUTE_ID_2) .addDeselectableRoute(TEST_ROUTE_ID_4) @@ -273,7 +239,7 @@ public class RoutingSessionInfoTest { @Test public void testBuilderRemoveRouteMethods() { RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) + TEST_ID, TEST_CLIENT_PACKAGE_NAME) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) .removeSelectedRoute(TEST_ROUTE_ID_1) @@ -308,7 +274,7 @@ public class RoutingSessionInfoTest { @Test public void testBuilderRemoveRouteMethodsWithBuilderCopyConstructor() { RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) + TEST_ID, TEST_CLIENT_PACKAGE_NAME) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) .addSelectableRoute(TEST_ROUTE_ID_2) @@ -342,7 +308,7 @@ public class RoutingSessionInfoTest { @Test public void testBuilderClearRouteMethods() { RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) + TEST_ID, TEST_CLIENT_PACKAGE_NAME) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) .clearSelectedRoutes() @@ -374,7 +340,7 @@ public class RoutingSessionInfoTest { @Test public void testBuilderClearRouteMethodsWithBuilderCopyConstructor() { RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) + TEST_ID, TEST_CLIENT_PACKAGE_NAME) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) .addSelectableRoute(TEST_ROUTE_ID_2) @@ -408,7 +374,7 @@ public class RoutingSessionInfoTest { controlHints.putString(TEST_KEY, TEST_VALUE); RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) + TEST_ID, TEST_CLIENT_PACKAGE_NAME) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) .addSelectableRoute(TEST_ROUTE_ID_2) @@ -421,7 +387,7 @@ public class RoutingSessionInfoTest { .build(); RoutingSessionInfo sessionInfo2 = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) + TEST_ID, TEST_CLIENT_PACKAGE_NAME) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) .addSelectableRoute(TEST_ROUTE_ID_2) @@ -443,7 +409,7 @@ public class RoutingSessionInfoTest { controlHints.putString(TEST_KEY, TEST_VALUE); RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) + TEST_ID, TEST_CLIENT_PACKAGE_NAME) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) .addSelectableRoute(TEST_ROUTE_ID_2) @@ -467,7 +433,7 @@ public class RoutingSessionInfoTest { controlHints.putString(TEST_KEY, TEST_VALUE); RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) + TEST_ID, TEST_CLIENT_PACKAGE_NAME) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) .addSelectableRoute(TEST_ROUTE_ID_2) @@ -530,7 +496,7 @@ public class RoutingSessionInfoTest { controlHints.putString(TEST_KEY, TEST_VALUE); RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder( - TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE) + TEST_ID, TEST_CLIENT_PACKAGE_NAME) .addSelectedRoute(TEST_ROUTE_ID_0) .addSelectedRoute(TEST_ROUTE_ID_1) .addSelectableRoute(TEST_ROUTE_ID_2) diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp index 26c7f8d709e7..ea8a521c9d5f 100644 --- a/native/graphics/jni/bitmap.cpp +++ b/native/graphics/jni/bitmap.cpp @@ -15,6 +15,7 @@ */ #include <android/bitmap.h> +#include <android/data_space.h> #include <android/graphics/bitmap.h> #include <android/data_space.h> @@ -74,3 +75,20 @@ int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap) { ABitmap_releaseRef(bitmap.get()); return ANDROID_BITMAP_RESULT_SUCCESS; } + +int AndroidBitmap_compress(const AndroidBitmapInfo* info, + int32_t dataSpace, + const void* pixels, + int32_t format, int32_t quality, + void* userContext, + AndroidBitmap_compress_write_fn fn) { + if (NULL == info || NULL == pixels || NULL == fn) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + if (quality < 0 || quality > 100) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + return ABitmap_compress(info, (ADataSpace) dataSpace, pixels, + (AndroidBitmapCompressFormat) format, quality, userContext, fn); +} diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp index 2ef203dd466f..51439672d404 100644 --- a/native/graphics/jni/imagedecoder.cpp +++ b/native/graphics/jni/imagedecoder.cpp @@ -225,13 +225,12 @@ AndroidBitmapFormat AImageDecoderHeaderInfo_getAndroidBitmapFormat( int AImageDecoderHeaderInfo_getAlphaFlags(const AImageDecoderHeaderInfo* info) { if (!info) { - // FIXME: Better invalid? - return -1; + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; } switch (toDecoder(info)->mCodec->getInfo().alphaType()) { case kUnknown_SkAlphaType: LOG_ALWAYS_FATAL("Invalid alpha type"); - return -1; + return ANDROID_IMAGE_DECODER_INTERNAL_ERROR; case kUnpremul_SkAlphaType: // fall through. premul is the default. case kPremul_SkAlphaType: @@ -241,26 +240,12 @@ int AImageDecoderHeaderInfo_getAlphaFlags(const AImageDecoderHeaderInfo* info) { } } -SkAlphaType toAlphaType(int androidBitmapFlags) { - switch (androidBitmapFlags) { - case ANDROID_BITMAP_FLAGS_ALPHA_PREMUL: - return kPremul_SkAlphaType; - case ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL: - return kUnpremul_SkAlphaType; - case ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE: - return kOpaque_SkAlphaType; - default: - return kUnknown_SkAlphaType; - } -} - -int AImageDecoder_setAlphaFlags(AImageDecoder* decoder, int alphaFlag) { - if (!decoder || alphaFlag < ANDROID_BITMAP_FLAGS_ALPHA_PREMUL - || alphaFlag > ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL) { +int AImageDecoder_setUnpremultipliedRequired(AImageDecoder* decoder, bool required) { + if (!decoder) { return ANDROID_IMAGE_DECODER_BAD_PARAMETER; } - return toDecoder(decoder)->setOutAlphaType(toAlphaType(alphaFlag)) + return toDecoder(decoder)->setUnpremultipliedRequired(required) ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION; } diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt index 832770ffb97e..6843e7a8552f 100644 --- a/native/graphics/jni/libjnigraphics.map.txt +++ b/native/graphics/jni/libjnigraphics.map.txt @@ -1,26 +1,27 @@ LIBJNIGRAPHICS { global: - AImageDecoder_createFromAAsset; - AImageDecoder_createFromFd; - AImageDecoder_createFromBuffer; - AImageDecoder_delete; - AImageDecoder_setAndroidBitmapFormat; - AImageDecoder_setAlphaFlags; - AImageDecoder_getHeaderInfo; - AImageDecoder_getMinimumStride; - AImageDecoder_decodeImage; - AImageDecoder_setTargetSize; - AImageDecoder_setCrop; - AImageDecoderHeaderInfo_getWidth; - AImageDecoderHeaderInfo_getHeight; - AImageDecoderHeaderInfo_getMimeType; - AImageDecoderHeaderInfo_getAlphaFlags; - AImageDecoderHeaderInfo_isAnimated; - AImageDecoderHeaderInfo_getAndroidBitmapFormat; + AImageDecoder_createFromAAsset; # introduced=30 + AImageDecoder_createFromFd; # introduced=30 + AImageDecoder_createFromBuffer; # introduced=30 + AImageDecoder_delete; # introduced=30 + AImageDecoder_setAndroidBitmapFormat; # introduced=30 + AImageDecoder_setUnpremultipliedRequired; # introduced=30 + AImageDecoder_getHeaderInfo; # introduced=30 + AImageDecoder_getMinimumStride; # introduced=30 + AImageDecoder_decodeImage; # introduced=30 + AImageDecoder_setTargetSize; # introduced=30 + AImageDecoder_setCrop; # introduced=30 + AImageDecoderHeaderInfo_getWidth; # introduced=30 + AImageDecoderHeaderInfo_getHeight; # introduced=30 + AImageDecoderHeaderInfo_getMimeType; # introduced=30 + AImageDecoderHeaderInfo_getAlphaFlags; # introduced=30 + AImageDecoderHeaderInfo_isAnimated; # introduced=30 + AImageDecoderHeaderInfo_getAndroidBitmapFormat; # introduced=30 AndroidBitmap_getInfo; AndroidBitmap_getDataSpace; AndroidBitmap_lockPixels; AndroidBitmap_unlockPixels; + AndroidBitmap_compress; # introduced=30 local: *; }; diff --git a/packages/CarSystemUI/res/xml/overlayable.xml b/packages/CarSystemUI/res/xml/overlayable.xml new file mode 100644 index 000000000000..2b6e66e28b5f --- /dev/null +++ b/packages/CarSystemUI/res/xml/overlayable.xml @@ -0,0 +1,23 @@ +<?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> + <overlayable name="SystemBarsLayouts"> + <policy type="product|signature"> + <item type="layout" name="car_navigation_bar" /> + </policy> + </overlayable> +</resources>
\ No newline at end of file diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 6ea2c741cc35..a337570829e4 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -195,6 +195,7 @@ public class SettingsBackupTest { Settings.Global.CERT_PIN_UPDATE_CONTENT_URL, Settings.Global.CERT_PIN_UPDATE_METADATA_URL, Settings.Global.COMPATIBILITY_MODE, + Settings.Global.COMMON_CRITERIA_MODE, Settings.Global.CONNECTIVITY_CHANGE_DELAY, Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE, Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS, diff --git a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml index f04226e7ceaf..cd90efe9a215 100644 --- a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml +++ b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml @@ -32,8 +32,8 @@ android:orientation="horizontal"> <FrameLayout - android:layout_width="90dp" - android:layout_height="94dp"> + android:layout_width="45dp" + android:layout_height="47dp"> <View android:id="@+id/icon_container_bg" @@ -43,35 +43,34 @@ <FrameLayout android:id="@+id/icon_mic" - android:layout_width="70dp" - android:layout_height="70dp" - android:layout_marginLeft="12dp" - android:layout_marginTop="12dp" - android:layout_marginRight="8dp" - android:layout_marginBottom="12dp"> + android:layout_width="35dp" + android:layout_height="35dp" + android:layout_marginLeft="6dp" + android:layout_marginTop="6dp" + android:layout_marginBottom="6dp"> <View - android:layout_width="54dp" - android:layout_height="54dp" + android:layout_width="27dp" + android:layout_height="27dp" android:layout_gravity="center" android:background="@drawable/tv_circle_dark"/> <ImageView android:id="@+id/pulsating_circle" - android:layout_width="54dp" - android:layout_height="54dp" + android:layout_width="27dp" + android:layout_height="27dp" android:layout_gravity="center" android:background="@drawable/tv_circle_white_translucent"/> <ImageView - android:layout_width="54dp" - android:layout_height="54dp" + android:layout_width="27dp" + android:layout_height="27dp" android:layout_gravity="center" android:src="@drawable/tv_ring_white"/> <ImageView - android:layout_width="32dp" - android:layout_height="32dp" + android:layout_width="16dp" + android:layout_height="16dp" android:layout_gravity="center" android:background="@drawable/tv_ic_mic_white"/> </FrameLayout> @@ -81,29 +80,30 @@ <LinearLayout android:id="@+id/texts_container" android:layout_width="wrap_content" - android:layout_height="94dp" + android:layout_height="47dp" android:background="@color/tv_audio_recording_indicator_background" - android:gravity="center_vertical" android:orientation="vertical" android:visibility="visible"> <TextView android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_height="14dp" + android:layout_marginTop="10dp" + android:layout_marginBottom="1dp" android:text="@string/mic_active" android:textColor="@android:color/white" android:fontFamily="sans-serif" - android:textSize="20dp"/> + android:textSize="10dp"/> <TextView android:id="@+id/text" android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_height="14dp" android:singleLine="true" android:text="SomeApplication accessed your microphone" android:textColor="@android:color/white" android:fontFamily="sans-serif" - android:textSize="16dp"/> + android:textSize="8dp"/> </LinearLayout> @@ -113,8 +113,8 @@ <View android:id="@+id/bg_right" - android:layout_width="24dp" - android:layout_height="94dp" + android:layout_width="12dp" + android:layout_height="47dp" android:background="@drawable/tv_rect_dark_right_rounded" android:visibility="visible"/> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 896315725644..e52010600ab3 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -484,4 +484,13 @@ <!-- Package name for controls plugin --> <string name="config_controlsPluginPackageName" translatable="false">com.android.systemui.controls.panel</string> + <!-- Defines the blacklist for system icons. That is to say, the icons in the status bar that + are part of the blacklist are never displayed. Each item in the blacklist must be a string + defined in core/res/res/config.xml to properly blacklist the icon. + --> + <string-array name="config_statusBarIconBlackList" translatable="false"> + <item>@*android:string/status_bar_rotate</item> + <item>@*android:string/status_bar_headset</item> + </string-array> + </resources> diff --git a/packages/SystemUI/scripts/update_shared_lib.sh b/packages/SystemUI/scripts/update_shared_lib.sh new file mode 100755 index 000000000000..05374934be2a --- /dev/null +++ b/packages/SystemUI/scripts/update_shared_lib.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +NUM_ARGS=$# + +has_croot() { + declare -F croot > /dev/null + return $? +} + +check_environment() { + if ! has_croot; then + echo "Run script in a shell that has had envsetup run. Run '. update_shared_lib.sh' from scripts directory" + return 1 + fi + + if [ $NUM_ARGS -ne 1 ]; then + echo "Usage: . update_shared_lib PATH_TO_UNBUNDLED_LAUNCER e.g. . update_shared_lib ~/src/ub-launcher3-master" + return 1 + fi + return 0 +} + +main() { + if check_environment ; then + pushd . + croot + mma -j16 SystemUISharedLib + JAR_DESTINATION="$1/prebuilts/framework_intermediates/quickstep/libs/sysui_shared.jar" + cp out/target/product/$TARGET_PRODUCT/obj/JAVA_LIBRARIES/SystemUISharedLib_intermediates/javalib.jar $JAR_DESTINATION + popd + fi +} + +main + diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java new file mode 100644 index 000000000000..70a464dd254c --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java @@ -0,0 +1,67 @@ +/* + * 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.shared.system; + +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.view.SurfaceView; + +/** Utility class that is shared between SysUI and Launcher for Universal Smartspace features. */ +public final class UniversalSmartspaceUtils { + public static final String ACTION_REQUEST_SMARTSPACE_VIEW = + "com.android.systemui.REQUEST_SMARTSPACE_VIEW"; + + private static final String SYSUI_PACKAGE = "com.android.systemui"; + private static final String INTENT_KEY_INPUT_BUNDLE = "input_bundle"; + private static final String BUNDLE_KEY_INPUT_TOKEN = "input_token"; + private static final String INTENT_KEY_SURFACE_CONTROL = "surface_control"; + + /** Creates an intent to request that sysui draws the Smartspace to the SurfaceView. */ + public static Intent createRequestSmartspaceIntent(SurfaceView surfaceView) { + Intent intent = new Intent(ACTION_REQUEST_SMARTSPACE_VIEW); + + Bundle inputBundle = new Bundle(); + inputBundle.putBinder(BUNDLE_KEY_INPUT_TOKEN, surfaceView.getInputToken()); + return intent + .putExtra(INTENT_KEY_SURFACE_CONTROL, surfaceView.getSurfaceControl()) + .putExtra(INTENT_KEY_INPUT_BUNDLE, inputBundle) + .setPackage(SYSUI_PACKAGE) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND); + } + + /** + * Retrieves the SurfaceControl from an Intent created by + * {@link #createRequestSmartspaceIntent(SurfaceView)}. + **/ + public static SurfaceControl getSurfaceControl(Intent intent) { + return intent.getParcelableExtra(INTENT_KEY_SURFACE_CONTROL); + } + + /** + * Retrieves the input token from an Intent created by + * {@link #createRequestSmartspaceIntent(SurfaceView)}. + **/ + public static IBinder getInputToken(Intent intent) { + Bundle inputBundle = intent.getBundleExtra(INTENT_KEY_INPUT_BUNDLE); + return inputBundle == null ? null : inputBundle.getBinder(BUNDLE_KEY_INPUT_TOKEN); + } + + private UniversalSmartspaceUtils() {} +} diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index d68fe15844c1..a46ab3a9e35b 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -288,7 +288,8 @@ public class BatteryMeterView extends LinearLayout implements @Override public void onTuningChanged(String key, String newValue) { if (StatusBarIconController.ICON_BLACKLIST.equals(key)) { - ArraySet<String> icons = StatusBarIconController.getIconBlacklist(newValue); + ArraySet<String> icons = StatusBarIconController.getIconBlacklist( + getContext(), newValue); setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE); } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index 895207d37816..898cd1363d2b 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -17,6 +17,8 @@ package com.android.systemui.accessibility; import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.database.ContentObserver; import android.os.Handler; import android.provider.Settings; @@ -35,10 +37,25 @@ public class WindowMagnification extends SystemUI { private WindowMagnificationController mWindowMagnificationController; private final Handler mHandler; + private Configuration mLastConfiguration; + @Inject public WindowMagnification(Context context, @Main Handler mainHandler) { super(context); mHandler = mainHandler; + mLastConfiguration = new Configuration(context.getResources().getConfiguration()); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + final int configDiff = newConfig.diff(mLastConfiguration); + if ((configDiff & ActivityInfo.CONFIG_DENSITY) == 0) { + return; + } + mLastConfiguration.setTo(newConfig); + if (mWindowMagnificationController != null) { + mWindowMagnificationController.onConfigurationChanged(configDiff); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index bfac4fce92eb..c243309d960a 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -113,6 +113,7 @@ public class WindowMagnificationController implements View.OnClickListener, if (mMirrorView != null) { return; } + setInitialStartBounds(); createOverlayWindow(); } @@ -178,9 +179,20 @@ public class WindowMagnificationController implements View.OnClickListener, } } - private void createMirrorWindow() { - setInitialStartBounds(); + /** + * Called when the configuration has changed, and it updates window magnification UI. + * + * @param configDiff a bit mask of the differences between the configurations + */ + void onConfigurationChanged(int configDiff) { + // TODO(b/145780606): update toggle button UI. + if (mMirrorView != null) { + mWm.removeView(mMirrorView); + createMirrorWindow(); + } + } + private void createMirrorWindow() { // The window should be the size the mirrored surface will be but also add room for the // border and the drag handle. int dragViewHeight = (int) mContext.getResources().getDimension( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index e0ca1ace2ac4..875619a71a18 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -338,7 +338,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @Override public void hideAuthenticationDialog() { - if (DEBUG) Log.d(TAG, "hideAuthenticationDialog"); + if (DEBUG) Log.d(TAG, "hideAuthenticationDialog: " + mCurrentDialog); + + if (mCurrentDialog == null) { + // Could be possible if the caller canceled authentication after credential success + // but before the client was notified. + return; + } mCurrentDialog.dismissFromSystemServer(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 86ed274e9b5c..f71150fc348c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -366,7 +366,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D protected static List<String> loadTileSpecs(Context context, String tileList) { final Resources res = context.getResources(); - final String defaultTileList = res.getString(R.string.quick_settings_tiles_default); + if (TextUtils.isEmpty(tileList)) { tileList = res.getString(R.string.quick_settings_tiles); if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList); @@ -380,11 +380,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D if (tile.isEmpty()) continue; if (tile.equals("default")) { if (!addedDefault) { - tiles.addAll(Arrays.asList(defaultTileList.split(","))); - if (Build.IS_DEBUGGABLE - && GarbageMonitor.MemoryTile.ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) { - tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC); - } + tiles.addAll(getDefaultSpecs(context)); addedDefault = true; } } else { @@ -394,6 +390,28 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D return tiles; } + /** + * Returns the default QS tiles for the context. + * @param context the context to obtain the resources from + * @return a list of specs of the default tiles + */ + public static List<String> getDefaultSpecs(Context context) { + final ArrayList<String> tiles = new ArrayList<String>(); + + final Resources res = context.getResources(); + final String defaultTileList = res.getString(R.string.quick_settings_tiles_default); + final String extraTileList = res.getString( + com.android.internal.R.string.config_defaultExtraQuickSettingsTiles); + + tiles.addAll(Arrays.asList(defaultTileList.split(","))); + tiles.addAll(Arrays.asList(extraTileList.split(","))); + if (Build.IS_DEBUGGABLE + && GarbageMonitor.MemoryTile.ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) { + tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC); + } + return tiles; + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("QSTileHost:"); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index 1de63550a1be..fb89ed264628 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -266,12 +266,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene } private void reset() { - ArrayList<String> tiles = new ArrayList<>(); - String defTiles = mContext.getString(R.string.quick_settings_tiles_default); - for (String tile : defTiles.split(",")) { - tiles.add(tile); - } - mTileAdapter.resetTileSpecs(mHost, tiles); + mTileAdapter.resetTileSpecs(mHost, QSTileHost.getDefaultSpecs(mContext)); } private void setTileSpecs() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index 3b106cbe10d1..64bdd9799ad6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.notification.row; import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION; -import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; @@ -54,6 +53,7 @@ import android.os.Handler; import android.os.Parcelable; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.AttributeSet; @@ -102,7 +102,6 @@ public class NotificationConversationInfo extends LinearLayout implements private NotificationEntry mEntry; private StatusBarNotification mSbn; private boolean mIsDeviceProvisioned; - private int mStartingChannelImportance; private boolean mStartedAsBubble; private boolean mIsBubbleable; @@ -127,6 +126,8 @@ public class NotificationConversationInfo extends LinearLayout implements mBubbleController.onUserDemotedBubbleFromNotification(mEntry); } else { mBubbleController.onUserCreatedBubbleFromNotification(mEntry); + Settings.Global.putInt( + mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES, 1); } closeControls(v, true); }; @@ -209,11 +210,10 @@ public class NotificationConversationInfo extends LinearLayout implements mLauncherApps = launcherApps; mConversationId = mNotificationChannel.getConversationId(); if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) { - mConversationId = mSbn.getNotification().getShortcutId(); + mConversationId = mSbn.getShortcutId(mContext); } - // TODO: flag this when flag exists if (TextUtils.isEmpty(mConversationId)) { - mConversationId = mSbn.getId() + mSbn.getTag() + PLACEHOLDER_CONVERSATION_ID; + throw new IllegalArgumentException("Does not have required information"); } // TODO: consider querying this earlier in the notification pipeline and passing it in LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery() @@ -240,10 +240,9 @@ public class NotificationConversationInfo extends LinearLayout implements // a custom channel if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) { try { - // TODO: associate this key with this channel service side so the customization - // isn't forgotten on the next update mINotificationManager.createConversationNotificationChannelForPackage( - mPackageName, mAppUid, mNotificationChannel, mConversationId); + mPackageName, mAppUid, mSbn.getKey(), mNotificationChannel, + mConversationId); mNotificationChannel = mINotificationManager.getConversationNotificationChannel( mContext.getOpPackageName(), UserHandle.getUserId(mAppUid), mPackageName, mNotificationChannel.getId(), false, mConversationId); @@ -360,7 +359,6 @@ public class NotificationConversationInfo extends LinearLayout implements image.setImageDrawable(mLauncherApps.getShortcutBadgedIconDrawable(mShortcutInfo, mContext.getResources().getDisplayMetrics().densityDpi)); } else { - // TODO: flag this behavior if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) { // TODO: maybe use a generic group icon, or a composite of recent senders image.setImageDrawable(mPm.getDefaultActivityIcon()); @@ -388,7 +386,6 @@ public class NotificationConversationInfo extends LinearLayout implements if (mShortcutInfo != null) { name.setText(mShortcutInfo.getShortLabel()); } else { - // TODO: flag this behavior Bundle extras = mSbn.getNotification().extras; String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE); if (TextUtils.isEmpty(nameString)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index a9a4804a2be4..6b4511d31669 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -65,8 +65,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.bubbles.BubbleController; -import com.android.systemui.bubbles.BubbleExperimentConfig; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationCounters; @@ -102,7 +100,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G // standard controls private static final int ACTION_ALERT = 5; - private TextView mBubbleDescriptionView; private TextView mPriorityDescriptionView; private TextView mSilentDescriptionView; @@ -120,7 +117,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private Set<NotificationChannel> mUniqueChannelsInRow; private NotificationChannel mSingleNotificationChannel; private int mStartingChannelImportance; - private boolean mStartedAsBubble; private boolean mWasShownHighPriority; private boolean mPressedApply; private boolean mPresentingChannelEditorDialog = false; @@ -130,14 +126,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G * level; non-null once the user takes an action which indicates an explicit preference. */ @Nullable private Integer mChosenImportance; - /** - * The last bubble setting chosen by the user. Null if the user has not chosen a bubble level; - * non-null once the user takes an action which indicates an explicit preference. - */ - @Nullable private Boolean mChosenBubbleEnabled; private boolean mIsSingleDefaultChannel; private boolean mIsNonblockable; - private boolean mIsBubbleable; private NotificationEntry mEntry; private StatusBarNotification mSbn; private AnimatorSet mExpandAnimation; @@ -149,8 +139,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private NotificationGuts mGutsContainer; private Drawable mPkgIcon; - private BubbleController mBubbleController; - /** Whether this view is being shown as part of the blocking helper. */ private boolean mIsForBlockingHelper; @@ -167,9 +155,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private OnClickListener mOnAlert = v -> { mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING; mChosenImportance = IMPORTANCE_DEFAULT; - if (mStartedAsBubble) { - mChosenBubbleEnabled = false; - } applyAlertingBehavior(BEHAVIOR_ALERTING, true /* userTriggered */); }; @@ -177,18 +162,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private OnClickListener mOnSilent = v -> { mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY; mChosenImportance = IMPORTANCE_LOW; - if (mStartedAsBubble) { - mChosenBubbleEnabled = false; - } applyAlertingBehavior(BEHAVIOR_SILENT, true /* userTriggered */); }; - /** Used by standard ui (in an experiment) {@see BubbleExperimentConfig#allowNotifBubbleMenu} */ - private OnClickListener mOnBubble = v -> { - mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING; - mChosenBubbleEnabled = true; - applyAlertingBehavior(BEHAVIOR_BUBBLE, true /* userTriggered */); - }; // used by standard ui private OnClickListener mOnDismissSettings = v -> { @@ -255,7 +231,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G protected void onFinishInflate() { super.onFinishInflate(); - mBubbleDescriptionView = findViewById(R.id.bubble_summary); mPriorityDescriptionView = findViewById(R.id.alert_summary); mSilentDescriptionView = findViewById(R.id.silence_summary); } @@ -320,7 +295,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mMetricsLogger = Dependency.get(MetricsLogger.class); mVisualStabilityManager = visualStabilityManager; mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class); - mBubbleController = Dependency.get(BubbleController.class); mPackageName = pkg; mUniqueChannelsInRow = uniqueChannelsInRow; mNumUniqueChannelsInRow = uniqueChannelsInRow.size(); @@ -352,9 +326,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G && numTotalChannels == 1; } - mIsBubbleable = mEntry.getBubbleMetadata() != null; - mStartedAsBubble = mEntry.isBubble(); - bindHeader(); bindChannelDetails(); @@ -402,7 +373,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G findViewById(R.id.non_configurable_text).setVisibility(GONE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE); - findViewById(R.id.bubble).setVisibility(mIsBubbleable ? VISIBLE : GONE); } View turnOffButton = findViewById(R.id.turn_off_notifications); @@ -416,14 +386,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G View silent = findViewById(R.id.silence); View alert = findViewById(R.id.alert); - View bubble = findViewById(R.id.bubble); silent.setOnClickListener(mOnSilent); alert.setOnClickListener(mOnAlert); - bubble.setOnClickListener(mOnBubble); - int behavior = mStartedAsBubble - ? BEHAVIOR_BUBBLE - : mWasShownHighPriority + int behavior = mWasShownHighPriority ? BEHAVIOR_ALERTING : BEHAVIOR_SILENT; applyAlertingBehavior(behavior, false /* userTriggered */); @@ -587,14 +553,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } - if (mChosenBubbleEnabled != null && mStartedAsBubble != mChosenBubbleEnabled) { - if (mChosenBubbleEnabled) { - mBubbleController.onUserCreatedBubbleFromNotification(mEntry); - } else { - mBubbleController.onUserDemotedBubbleFromNotification(mEntry); - } - } - Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); bgHandler.post( new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid, @@ -630,7 +588,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G TransitionManager.beginDelayedTransition(this, transition); } - View bubble = findViewById(R.id.bubble); View alert = findViewById(R.id.alert); View silence = findViewById(R.id.silence); @@ -638,33 +595,18 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G case BEHAVIOR_ALERTING: mPriorityDescriptionView.setVisibility(VISIBLE); mSilentDescriptionView.setVisibility(GONE); - mBubbleDescriptionView.setVisibility(GONE); post(() -> { alert.setSelected(true); silence.setSelected(false); - bubble.setSelected(false); }); break; case BEHAVIOR_SILENT: mSilentDescriptionView.setVisibility(VISIBLE); mPriorityDescriptionView.setVisibility(GONE); - mBubbleDescriptionView.setVisibility(GONE); post(() -> { alert.setSelected(false); silence.setSelected(true); - bubble.setSelected(false); - }); - break; - - case BEHAVIOR_BUBBLE: - mBubbleDescriptionView.setVisibility(VISIBLE); - mSilentDescriptionView.setVisibility(GONE); - mPriorityDescriptionView.setVisibility(GONE); - post(() -> { - alert.setSelected(false); - silence.setSelected(false); - bubble.setSelected(true); }); break; @@ -673,9 +615,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } boolean isAChange = mWasShownHighPriority != (behavior == BEHAVIOR_ALERTING); - boolean isABubbleChange = mStartedAsBubble != (behavior == BEHAVIOR_BUBBLE); TextView done = findViewById(R.id.done); - done.setText((isAChange || isABubbleChange) + done.setText(isAChange ? R.string.inline_ok_button : R.string.inline_done_button); } @@ -684,7 +625,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G switch (action) { case ACTION_UNDO: mChosenImportance = mStartingChannelImportance; - mChosenBubbleEnabled = mStartedAsBubble; break; case ACTION_DELIVER_SILENTLY: mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY; @@ -767,9 +707,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G if (mChosenImportance != null) { mStartingChannelImportance = mChosenImportance; } - if (mChosenBubbleEnabled != null) { - mStartedAsBubble = mChosenBubbleEnabled; - } mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED; if (mIsForBlockingHelper) { @@ -969,9 +906,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } @Retention(SOURCE) - @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT, BEHAVIOR_BUBBLE}) + @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT}) private @interface AlertingBehavior {} private static final int BEHAVIOR_ALERTING = 0; private static final int BEHAVIOR_SILENT = 1; - private static final int BEHAVIOR_BUBBLE = 2; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 5daef24cbad2..75da5d123972 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -76,12 +76,12 @@ public interface StatusBarIconController { public static final String ICON_BLACKLIST = "icon_blacklist"; - public static ArraySet<String> getIconBlacklist(String blackListStr) { + /** Reads the default blacklist from config value unless blacklistStr is provided. */ + static ArraySet<String> getIconBlacklist(Context context, String blackListStr) { ArraySet<String> ret = new ArraySet<>(); - if (blackListStr == null) { - blackListStr = "rotate,headset"; - } - String[] blacklist = blackListStr.split(","); + String[] blacklist = blackListStr == null + ? context.getResources().getStringArray(R.array.config_statusBarIconBlackList) + : blackListStr.split(","); for (String slot : blacklist) { if (!TextUtils.isEmpty(slot)) { ret.add(slot); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index aa062eb2e051..bfcbceaef9af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -111,7 +111,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu return; } mIconBlacklist.clear(); - mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(newValue)); + mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(mContext, newValue)); ArrayList<Slot> currentSlots = getSlots(); ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index 8286d26e9999..d2e92629ed78 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -117,7 +117,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) { return; } - ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue); + ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(mContext, newValue); boolean blockAirplane = blockList.contains(mSlotAirplane); boolean blockMobile = blockList.contains(mSlotMobile); boolean blockWifi = blockList.contains(mSlotWifi); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 4f0af9e166c9..759bad4f77b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -298,7 +298,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C mShowSeconds = TunerService.parseIntegerSwitch(newValue, false); updateShowSeconds(); } else { - setClockVisibleByUser(!StatusBarIconController.getIconBlacklist(newValue) + setClockVisibleByUser(!StatusBarIconController.getIconBlacklist(getContext(), newValue) .contains("clock")); updateClockVisibility(); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java index 905b9a398b68..66372c311325 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java @@ -64,7 +64,7 @@ public class BatteryPreference extends DropDownPreference implements TunerServic @Override public void onTuningChanged(String key, String newValue) { if (StatusBarIconController.ICON_BLACKLIST.equals(key)) { - mBlacklist = StatusBarIconController.getIconBlacklist(newValue); + mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue); mBatteryEnabled = !mBlacklist.contains(mBattery); } if (!mHasSetValue) { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java index a526603372bd..f7d0c9fb9d86 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java @@ -61,7 +61,7 @@ public class ClockPreference extends DropDownPreference implements TunerService. public void onTuningChanged(String key, String newValue) { if (StatusBarIconController.ICON_BLACKLIST.equals(key)) { mReceivedClock = true; - mBlacklist = StatusBarIconController.getIconBlacklist(newValue); + mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue); mClockEnabled = !mBlacklist.contains(mClock); } else if (Clock.CLOCK_SECONDS.equals(key)) { mReceivedSeconds = true; diff --git a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java index 6f23e207c048..de8ccfa848e3 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java @@ -57,7 +57,7 @@ public class StatusBarSwitch extends SwitchPreference implements Tunable { if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) { return; } - mBlacklist = StatusBarIconController.getIconBlacklist(newValue); + mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue); setChecked(!mBlacklist.contains(getKey())); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 19f0ba24b0fd..142fdc21aff1 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -117,7 +117,7 @@ public class TunerServiceImpl extends TunerService { String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST); if (blacklistStr != null) { ArraySet<String> iconBlacklist = - StatusBarIconController.getIconBlacklist(blacklistStr); + StatusBarIconController.getIconBlacklist(mContext, blacklistStr); iconBlacklist.add("rotate"); iconBlacklist.add("headset"); 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 c0e92e09c486..65399bfb901e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -20,7 +20,6 @@ import static android.hardware.biometrics.BiometricManager.Authenticators; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; -import static junit.framework.TestCase.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -292,6 +291,24 @@ public class AuthControllerTest extends SysuiTestCase { // Corner case tests @Test + public void testCancelAuthentication_whenCredentialConfirmed_doesntCrash() throws Exception { + // It's possible that before the client is notified that credential is confirmed, the client + // requests to cancel authentication. + // + // Test that the following sequence of events does not crash SystemUI: + // 1) Credential is confirmed + // 2) Client cancels authentication + + showDialog(Authenticators.DEVICE_CREDENTIAL, BiometricPrompt.TYPE_NONE); + verify(mDialog1).show(any(), any()); + + mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED); + verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED); + + mAuthController.hideAuthenticationDialog(); + } + + @Test public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() { showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE); verify(mDialog1).show(any(), any()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index fad7cbd04c74..34111e2cba9b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -44,6 +44,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.shared.plugins.PluginManager; @@ -73,6 +74,7 @@ import javax.inject.Provider; public class QSTileHostTest extends SysuiTestCase { private static String MOCK_STATE_STRING = "MockState"; + private static final String CUSTOM_TILE_SPEC = "custom(TEST_PKG/.TEST_CLS)"; @Mock private StatusBarIconController mIconController; @@ -92,6 +94,8 @@ public class QSTileHostTest extends SysuiTestCase { private QSTile.State mMockState; @Mock private StatusBar mStatusBar; + @Mock + private CustomTile mCustomTile; private Handler mHandler; private TestableLooper mLooper; @@ -103,9 +107,8 @@ public class QSTileHostTest extends SysuiTestCase { mLooper = TestableLooper.get(this); mHandler = new Handler(mLooper.getLooper()); mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler, - mLooper.getLooper(), - mPluginManager, mTunerService, mAutoTiles, mDumpController, mBroadcastDispatcher, - mStatusBar); + mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpController, + mBroadcastDispatcher, mStatusBar); setUpTileFactory(); Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING, "", ActivityManager.getCurrentUser()); @@ -121,10 +124,13 @@ public class QSTileHostTest extends SysuiTestCase { return new TestTile1(mQSTileHost); case "spec2": return new TestTile2(mQSTileHost); + case CUSTOM_TILE_SPEC: + return mCustomTile; default: return null; } }); + when(mCustomTile.isAvailable()).thenReturn(true); } @Test @@ -173,6 +179,39 @@ public class QSTileHostTest extends SysuiTestCase { assertEquals(output, w.getBuffer().toString()); } + @Test + public void testDefault() { + mContext.getOrCreateTestableResources() + .addOverride(R.string.quick_settings_tiles_default, "spec1"); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default"); + assertEquals(1, mQSTileHost.getTiles().size()); + QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles()); + assertTrue(element instanceof TestTile1); + } + + @Test + public void testDefaultAndExtra() { + mContext.getOrCreateTestableResources() + .addOverride(R.string.quick_settings_tiles_default, "spec1"); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.string.config_defaultExtraQuickSettingsTiles, "spec2"); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default"); + assertEquals(2, mQSTileHost.getTiles().size()); + QSTile[] elements = mQSTileHost.getTiles().toArray(new QSTile[0]); + assertTrue(elements[0] instanceof TestTile1); + assertTrue(elements[1] instanceof TestTile2); + } + + @Test + public void testExtraCustom() { + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.string.config_defaultExtraQuickSettingsTiles, + CUSTOM_TILE_SPEC); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default"); + assertEquals(1, mQSTileHost.getTiles().size()); + assertEquals(mCustomTile, CollectionUtils.firstOrNull(mQSTileHost.getTiles())); + } + private static class TestQSTileHost extends QSTileHost { TestQSTileHost(Context context, StatusBarIconController iconController, QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 61f0b265e874..9ae477ee717d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -797,7 +797,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { null, true); verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage( - anyString(), anyInt(), any(), eq(CONVERSATION_ID)); + anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID)); } @Test @@ -817,7 +817,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { null, true); verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage( - anyString(), anyInt(), any(), eq(CONVERSATION_ID)); + anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index f513c2d1513b..c62487a830fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.row; -import static android.app.Notification.FLAG_BUBBLE; import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; @@ -50,13 +49,10 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; -import android.app.PendingIntent; -import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; -import android.graphics.drawable.Icon; import android.os.IBinder; import android.os.UserHandle; import android.provider.Settings; @@ -76,9 +72,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.bubbles.BubbleController; -import com.android.systemui.bubbles.BubblesTestActivity; -import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -116,8 +109,6 @@ public class NotificationInfoTest extends SysuiTestCase { private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>(); private StatusBarNotification mSbn; private NotificationEntry mEntry; - private StatusBarNotification mBubbleSbn; - private NotificationEntry mBubbleEntry; @Rule public MockitoRule mockito = MockitoJUnit.rule(); @@ -131,8 +122,6 @@ public class NotificationInfoTest extends SysuiTestCase { private NotificationBlockingHelperManager mBlockingHelperManager; @Mock private VisualStabilityManager mVisualStabilityManager; - @Mock - private BubbleController mBubbleController; @Before public void setUp() throws Exception { @@ -143,7 +132,6 @@ public class NotificationInfoTest extends SysuiTestCase { mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); - mDependency.injectTestDependency(BubbleController.class, mBubbleController); // Inflate the layout final LayoutInflater layoutInflater = LayoutInflater.from(mContext); mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info, @@ -185,15 +173,6 @@ public class NotificationInfoTest extends SysuiTestCase { new Notification(), UserHandle.CURRENT, null, 0); mEntry = new NotificationEntryBuilder().setSbn(mSbn).build(); - PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, - new Intent(mContext, BubblesTestActivity.class), 0); - mBubbleSbn = new SbnBuilder(mSbn).setBubbleMetadata( - new Notification.BubbleMetadata.Builder() - .setIntent(bubbleIntent) - .setIcon(Icon.createWithResource(mContext, R.drawable.android)).build()) - .build(); - mBubbleEntry = new NotificationEntryBuilder().setSbn(mBubbleSbn).build(); - Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_NEW_INTERRUPTION_MODEL, 1); } @@ -765,7 +744,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, - mBubbleEntry, + mEntry, null, null, null, @@ -785,7 +764,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, - mBubbleEntry, + mEntry, null, null, null, @@ -797,162 +776,6 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test - public void testBindNotification_bubbleIsSelected() throws Exception { - mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE; - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mBubbleEntry, - null, - null, - null, - true, - false, - IMPORTANCE_DEFAULT, - true); - - View bubbleView = mNotificationInfo.findViewById(R.id.bubble); - assertEquals(View.VISIBLE, bubbleView.getVisibility()); - assertTrue(bubbleView.isSelected()); - } - - @Test - public void testBindNotification_whenCanBubble() throws Exception { - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mBubbleEntry, - null, - null, - null, - true, - false, - IMPORTANCE_DEFAULT, - true); - - View bubbleView = mNotificationInfo.findViewById(R.id.bubble); - assertEquals(View.VISIBLE, bubbleView.getVisibility()); - assertFalse(bubbleView.isSelected()); - } - - @Test - public void testBindNotification_whenCantBubble() throws Exception { - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mEntry, - null, - null, - null, - true, - false, - IMPORTANCE_DEFAULT, - true); - View bubbleView = mNotificationInfo.findViewById(R.id.bubble); - assertEquals(View.GONE, bubbleView.getVisibility()); - } - - @Test - public void testBubble_promotesBubble() throws Exception { - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mBubbleEntry, - null, - null, - null, - true, - false, - IMPORTANCE_DEFAULT, - true); - - assertFalse(mBubbleEntry.isBubble()); - - // Promote it - mNotificationInfo.findViewById(R.id.bubble).performClick(); - mNotificationInfo.findViewById(R.id.done).performClick(); - mNotificationInfo.handleCloseControls(true, false); - - verify(mBubbleController, times(1)).onUserCreatedBubbleFromNotification(mBubbleEntry); - } - - @Test - public void testAlert_demotesBubble() throws Exception { - mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE; - - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mBubbleEntry, - null, - null, - null, - true, - false, - IMPORTANCE_DEFAULT, - true); - - assertTrue(mBubbleEntry.isBubble()); - - // Demote it - mNotificationInfo.findViewById(R.id.alert).performClick(); - mNotificationInfo.findViewById(R.id.done).performClick(); - mNotificationInfo.handleCloseControls(true, false); - - verify(mBubbleController, times(1)).onUserDemotedBubbleFromNotification(mBubbleEntry); - } - - @Test - public void testSilence_demotesBubble() throws Exception { - mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE; - - mNotificationInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mBubbleEntry, - null, - null, - null, - true, - false, - IMPORTANCE_DEFAULT, - true); - - assertTrue(mBubbleEntry.isBubble()); - - // Demote it - mNotificationInfo.findViewById(R.id.silence).performClick(); - mNotificationInfo.findViewById(R.id.done).performClick(); - mNotificationInfo.handleCloseControls(true, false); - - verify(mBubbleController, times(1)).onUserDemotedBubbleFromNotification(mBubbleEntry); - } - - @Test public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { mNotificationInfo.bindNotification( mMockPackageManager, diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp index e441fb508f79..cf2b1f06d769 100644 --- a/packages/Tethering/Android.bp +++ b/packages/Tethering/Android.bp @@ -35,7 +35,7 @@ java_defaults { "framework-tethering", "unsupportedappusage", ], - + plugins: ["java_api_finder"], manifest: "AndroidManifestBase.xml", } diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java index 4306cf0bd5f3..0491ad7c3413 100644 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ b/packages/Tethering/src/android/net/ip/IpServer.java @@ -441,7 +441,8 @@ public class IpServer extends StateMachine { } final Boolean setIfaceUp; - if (mInterfaceType == TetheringManager.TETHERING_WIFI) { + if (mInterfaceType == TetheringManager.TETHERING_WIFI + || mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) { // The WiFi stack has ownership of the interface up/down state. // It is unclear whether the Bluetooth or USB stacks will manage their own // state. diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 37e0cc107b58..8d1e0c96e300 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -207,6 +207,7 @@ public class Tethering { private Network mTetherUpstream; private TetherStatesParcel mTetherStatesParcel; private boolean mDataSaverEnabled = false; + private String mWifiP2pTetherInterface = null; public Tethering(TetheringDependencies deps) { mLog.mark("Tethering.constructed"); @@ -852,6 +853,11 @@ public class Tethering { } } + private boolean isGroupOwner(WifiP2pGroup group) { + return group != null && group.isGroupOwner() + && !TextUtils.isEmpty(group.getInterface()); + } + private void handleWifiP2pAction(Intent intent) { if (mConfig.isWifiP2pLegacyTetheringMode()) return; @@ -864,24 +870,31 @@ public class Tethering { Log.d(TAG, "WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group); } - if (p2pInfo == null) return; - // When a p2p group is disconnected, p2pInfo would be cleared. - // group is still valid for detecting whether this device is group owner. - if (group == null || !group.isGroupOwner() - || TextUtils.isEmpty(group.getInterface())) return; - synchronized (Tethering.this.mPublicSync) { - // Enter below only if this device is Group Owner with a valid interface. - if (p2pInfo.groupFormed) { - TetherState tetherState = mTetherStates.get(group.getInterface()); - if (tetherState == null - || (tetherState.lastState != IpServer.STATE_TETHERED - && tetherState.lastState != IpServer.STATE_LOCAL_ONLY)) { - enableWifiIpServingLocked(group.getInterface(), IFACE_IP_MODE_LOCAL_ONLY); - } - } else { - disableWifiP2pIpServingLocked(group.getInterface()); + // if no group is formed, bring it down if needed. + if (p2pInfo == null || !p2pInfo.groupFormed) { + disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface); + mWifiP2pTetherInterface = null; + return; } + + // If there is a group but the device is not the owner, bail out. + if (!isGroupOwner(group)) return; + + // If already serving from the correct interface, nothing to do. + if (group.getInterface().equals(mWifiP2pTetherInterface)) return; + + // If already serving from another interface, turn it down first. + if (!TextUtils.isEmpty(mWifiP2pTetherInterface)) { + mLog.w("P2P tethered interface " + mWifiP2pTetherInterface + + "is different from current interface " + + group.getInterface() + ", re-tether it"); + disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface); + } + + // Finally bring up serving on the new interface + mWifiP2pTetherInterface = group.getInterface(); + enableWifiIpServingLocked(mWifiP2pTetherInterface, IFACE_IP_MODE_LOCAL_ONLY); } } @@ -979,7 +992,9 @@ public class Tethering { disableWifiIpServingLockedCommon(TETHERING_WIFI, ifname, apState); } - private void disableWifiP2pIpServingLocked(String ifname) { + private void disableWifiP2pIpServingLockedIfNeeded(String ifname) { + if (TextUtils.isEmpty(ifname)) return; + disableWifiIpServingLockedCommon(TETHERING_WIFI_P2P, ifname, /* dummy */ 0); } diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 1f50b6bf7fb3..f29ad780b92c 100644 --- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -273,7 +273,7 @@ public class IpServerTest { dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY); InOrder inOrder = inOrder(mCallback, mNetd); inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> - IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP))); + IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP))); inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME); inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME); inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), @@ -547,4 +547,14 @@ public class IpServerTest { fail("Missing flag: " + match); return false; } + + private boolean assertNotContainsFlag(String[] flags, String match) { + for (String flag : flags) { + if (flag.equals(match)) { + fail("Unexpected flag: " + match); + return false; + } + } + return true; + } } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 59106240a08b..d6afa4744d26 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -493,13 +493,15 @@ public class TetheringTest { private void sendWifiP2pConnectionChanged( boolean isGroupFormed, boolean isGroupOwner, String ifname) { + WifiP2pGroup group = null; WifiP2pInfo p2pInfo = new WifiP2pInfo(); p2pInfo.groupFormed = isGroupFormed; - p2pInfo.isGroupOwner = isGroupOwner; - - WifiP2pGroup group = mock(WifiP2pGroup.class); - when(group.isGroupOwner()).thenReturn(isGroupOwner); - when(group.getInterface()).thenReturn(ifname); + if (isGroupFormed) { + p2pInfo.isGroupOwner = isGroupOwner; + group = mock(WifiP2pGroup.class); + when(group.isGroupOwner()).thenReturn(isGroupOwner); + when(group.getInterface()).thenReturn(ifname); + } final Intent intent = mock(Intent.class); when(intent.getAction()).thenReturn(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index 21100458adc1..ad802ff879f2 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -240,6 +240,10 @@ message SystemMessage { // Inform the user that EAP failure occurs NOTE_WIFI_EAP_FAILURE = 57; + // Notify the user that their softap disabled because auto shutdown timeout expired. + // Package: android + NOTE_SOFTAP_AUTO_DISABLED = 58; + // ADD_NEW_IDS_ABOVE_THIS_LINE // Legacy IDs with arbitrary values appear below // Legacy IDs existed as stable non-conflicting constants prior to the O release diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index c3965c44d4c0..3e74b7a92f5d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -80,6 +80,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -775,6 +776,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } @Override + public @NonNull List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions() { + synchronized (mLock) { + if (!hasRightsToCurrentUserLocked()) { + return Collections.emptyList(); + } + } + return mSystemActionPerformer.getSystemActions(); + } + + @Override public boolean isFingerprintGestureDetectionAvailable() { if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { return false; @@ -1252,6 +1263,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ gestureEvent).sendToTarget(); } + public void notifySystemActionsChangedLocked() { + mInvocationHandler.sendEmptyMessage( + InvocationHandler.MSG_ON_SYSTEM_ACTIONS_CHANGED); + } + public void notifyClearAccessibilityNodeInfoCache() { mInvocationHandler.sendEmptyMessage( InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE); @@ -1350,6 +1366,18 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } + private void notifySystemActionsChangedInternal() { + final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); + if (listener != null) { + try { + listener.onSystemActionsChanged(); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Error sending system actions change to " + mService, + re); + } + } + } + private void notifyClearAccessibilityCacheInternal() { final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { @@ -1544,6 +1572,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private static final int MSG_ON_SOFT_KEYBOARD_STATE_CHANGED = 6; private static final int MSG_ON_ACCESSIBILITY_BUTTON_CLICKED = 7; private static final int MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 8; + private static final int MSG_ON_SYSTEM_ACTIONS_CHANGED = 9; /** List of magnification callback states, mapping from displayId -> Boolean */ @GuardedBy("mlock") @@ -1591,7 +1620,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final boolean available = (message.arg1 != 0); notifyAccessibilityButtonAvailabilityChangedInternal(available); } break; - + case MSG_ON_SYSTEM_ACTIONS_CHANGED: { + notifySystemActionsChangedInternal(); + break; + } default: { throw new IllegalArgumentException("Unknown message: " + type); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index c9fdd5ae407a..bcaecea4c388 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -94,6 +94,7 @@ import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityManager; import android.view.accessibility.IAccessibilityManagerClient; +import android.view.accessibility.IWindowMagnificationConnection; import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; @@ -106,6 +107,7 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.IntPair; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.accessibility.magnification.WindowMagnificationManager; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -135,7 +137,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub implements AbstractAccessibilityServiceConnection.SystemSupport, AccessibilityUserState.ServiceInfoChangeListener, AccessibilityWindowManager.AccessibilityEventSender, - AccessibilitySecurityPolicy.AccessibilityUserManager { + AccessibilitySecurityPolicy.AccessibilityUserManager, + SystemActionPerformer.SystemActionsChangedListener { private static final boolean DEBUG = false; @@ -204,6 +207,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private AccessibilityInputFilter mInputFilter; + private WindowMagnificationManager mWindowMagnificationMgr; + private boolean mHasInputFilter; private KeyEventDispatcher mKeyEventDispatcher; @@ -290,7 +295,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class); mPackageManager = mContext.getPackageManager(); mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this); - mSystemActionPerformer = new SystemActionPerformer(mContext, mWindowManagerService); + mSystemActionPerformer = + new SystemActionPerformer(mContext, mWindowManagerService, null, this); mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler, mWindowManagerService, this, mSecurityPolicy, this); mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler); @@ -877,11 +883,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public void notifyAccessibilityButtonVisibilityChanged(boolean shown) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Caller does not hold permission " - + android.Manifest.permission.STATUS_BAR_SERVICE); - } + mSecurityPolicy.enforceCallingOrSelfPermission( + android.Manifest.permission.STATUS_BAR_SERVICE); synchronized (mLock) { notifyAccessibilityButtonVisibilityChangedLocked(shown); } @@ -903,6 +906,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + /** + * Called when the system action list is changed. + */ + @Override + public void onSystemActionsChanged() { + synchronized (mLock) { + AccessibilityUserState state = getCurrentUserStateLocked(); + notifySystemActionsChangedLocked(state); + } + } + + @VisibleForTesting + void notifySystemActionsChangedLocked(AccessibilityUserState userState) { + for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { + AccessibilityServiceConnection service = userState.mBoundServices.get(i); + service.notifySystemActionsChangedLocked(); + } + } + @VisibleForTesting public boolean notifyKeyEvent(KeyEvent event, int policyFlags) { synchronized (mLock) { @@ -1883,11 +1905,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) { + final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId); final Set<String> targetsFromSetting = new ArraySet<>(); - readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, - userState.mUserId, targetsFromSetting, str -> str); - if (targetsFromSetting.isEmpty()) { - // Fall back to device's default a11y service. + readColonDelimitedStringToSet(settingValue, targetsFromSetting, false, str -> str); + // Fall back to device's default a11y service, only when setting is never updated. + if (settingValue == null) { final String defaultService = mContext.getString( R.string.config_defaultAccessibilityService); if (!TextUtils.isEmpty(defaultService)) { @@ -2588,6 +2611,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } @Override + public void setWindowMagnificationConnection( + IWindowMagnificationConnection connection) throws RemoteException { + mSecurityPolicy.enforceCallingOrSelfPermission( + android.Manifest.permission.STATUS_BAR_SERVICE); + + getWindowMagnificationMgr().setConnection(connection); + } + + WindowMagnificationManager getWindowMagnificationMgr() { + synchronized (mLock) { + if (mWindowMagnificationMgr == null) { + mWindowMagnificationMgr = new WindowMagnificationManager(); + } + return mWindowMagnificationMgr; + } + } + + @Override public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return; synchronized (mLock) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java index 7dbec7c8c0c5..7a42cd1b3cbb 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java @@ -550,4 +550,17 @@ public class AccessibilitySecurityPolicy { Binder.restoreCallingIdentity(identityToken); } } + + /** + * Enforcing permission check to IPC caller or grant it if it's not through IPC. + * + * @param permission The permission to check + */ + public void enforceCallingOrSelfPermission(@NonNull String permission) { + if (mContext.checkCallingOrSelfPermission(permission) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Caller does not hold permission " + + permission); + } + } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java new file mode 100644 index 000000000000..351c9e08b645 --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.magnification; + +import static android.os.IBinder.DeathRecipient; + +import android.annotation.NonNull; +import android.os.RemoteException; +import android.util.Slog; +import android.view.accessibility.IWindowMagnificationConnection; +import android.view.accessibility.IWindowMagnificationConnectionCallback; + +/** + * A wrapper of {@link IWindowMagnificationConnection}. + */ +class WindowMagnificationConnectionWrapper { + + private static final boolean DBG = false; + private static final String TAG = "WindowMagnificationConnectionWrapper"; + + private final @NonNull IWindowMagnificationConnection mConnection; + + WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection) { + mConnection = connection; + } + + //Should not use this instance anymore after calling it. + void unlinkToDeath(@NonNull DeathRecipient deathRecipient) { + mConnection.asBinder().unlinkToDeath(deathRecipient, 0); + } + + void linkToDeath(@NonNull DeathRecipient deathRecipient) throws RemoteException { + mConnection.asBinder().linkToDeath(deathRecipient, 0); + } + + boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY) { + try { + mConnection.enableWindowMagnification(displayId, scale, centerX, centerY); + } catch (RemoteException e) { + if (DBG) { + Slog.e(TAG, "Error calling enableWindowMagnification()"); + } + return false; + } + return true; + } + + boolean setScale(int displayId, float scale) { + try { + mConnection.setScale(displayId, scale); + } catch (RemoteException e) { + if (DBG) { + Slog.e(TAG, "Error calling setScale()"); + } + return false; + } + return true; + } + + boolean disableWindowMagnification(int displayId) { + try { + mConnection.disableWindowMagnification(displayId); + } catch (RemoteException e) { + if (DBG) { + Slog.e(TAG, "Error calling disableWindowMagnification()"); + } + return false; + } + return true; + } + + boolean moveWindowMagnifier(int displayId, float offsetX, float offsetY) { + try { + mConnection.moveWindowMagnifier(displayId, offsetX, offsetY); + } catch (RemoteException e) { + if (DBG) { + Slog.e(TAG, "Error calling moveWindowMagnifier()"); + } + return false; + } + return true; + } + + boolean setConnectionCallback(IWindowMagnificationConnectionCallback connectionCallback) { + try { + mConnection.setConnectionCallback(connectionCallback); + } catch (RemoteException e) { + if (DBG) { + Slog.e(TAG, "Error calling setConnectionCallback()"); + } + return false; + } + return true; + } + +} diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java new file mode 100644 index 000000000000..00db3294c9e6 --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.magnification; + +import android.annotation.Nullable; +import android.graphics.Rect; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; +import android.view.accessibility.IWindowMagnificationConnection; +import android.view.accessibility.IWindowMagnificationConnectionCallback; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * A class to manipulate window magnification through {@link WindowMagnificationConnectionWrapper}. + */ +public final class WindowMagnificationManager { + + private static final String TAG = "WindowMagnificationMgr"; + private final Object mLock = new Object(); + @VisibleForTesting + @Nullable WindowMagnificationConnectionWrapper mConnectionWrapper; + private ConnectionCallback mConnectionCallback; + + /** + * Sets {@link IWindowMagnificationConnection}. + * @param connection {@link IWindowMagnificationConnection} + */ + public void setConnection(@Nullable IWindowMagnificationConnection connection) { + synchronized (mLock) { + //Reset connectionWrapper. + if (mConnectionWrapper != null) { + mConnectionWrapper.setConnectionCallback(null); + if (mConnectionCallback != null) { + mConnectionCallback.mExpiredDeathRecipient = true; + } + mConnectionWrapper.unlinkToDeath(mConnectionCallback); + mConnectionWrapper = null; + } + if (connection != null) { + mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection); + } + + if (mConnectionWrapper != null) { + try { + mConnectionCallback = new ConnectionCallback(); + mConnectionWrapper.linkToDeath(mConnectionCallback); + mConnectionWrapper.setConnectionCallback(mConnectionCallback); + } catch (RemoteException e) { + Slog.e(TAG, "setConnection failed", e); + mConnectionWrapper = null; + } + } + } + } + + private class ConnectionCallback extends IWindowMagnificationConnectionCallback.Stub implements + IBinder.DeathRecipient { + private boolean mExpiredDeathRecipient = false; + + @Override + public void onWindowMagnifierBoundsChanged(int display, Rect frame) throws RemoteException { + } + + @Override + public void onChangeMagnificationMode(int display, int magnificationMode) + throws RemoteException { + } + + @Override + public void binderDied() { + synchronized (mLock) { + if (mExpiredDeathRecipient) { + Slog.w(TAG, "binderDied DeathRecipient is expired"); + return; + } + mConnectionWrapper.unlinkToDeath(this); + mConnectionWrapper = null; + mConnectionCallback = null; + } + } + } +} diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java index 3af15c7a0e5c..5e6f97e4f282 100644 --- a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java @@ -20,23 +20,24 @@ import static com.android.server.autofill.Helper.sDebug; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.slice.Slice; import android.content.Context; -import android.os.Handler; import android.os.RemoteException; import android.service.autofill.Dataset; +import android.service.autofill.InlinePresentation; import android.util.Slog; import android.view.SurfaceControl; import android.view.View; import android.view.autofill.AutofillId; import android.view.autofill.IAutoFillManagerClient; -import android.view.inline.InlinePresentationSpec; import android.view.inputmethod.InlineSuggestion; import android.view.inputmethod.InlineSuggestionInfo; -import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; import com.android.internal.view.inline.IInlineContentCallback; import com.android.internal.view.inline.IInlineContentProvider; +import com.android.server.UiThread; +import com.android.server.autofill.ui.AutoFillUI; import com.android.server.autofill.ui.InlineSuggestionUi; import java.util.ArrayList; @@ -56,23 +57,59 @@ public final class InlineSuggestionFactory { int sessionId, @NonNull Dataset[] datasets, @NonNull AutofillId autofillId, - @NonNull InlineSuggestionsRequest request, - @NonNull Handler uiHandler, @NonNull Context context, @NonNull IAutoFillManagerClient client) { - if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called"); + if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called"); final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>(); final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context); for (Dataset dataset : datasets) { - // TODO(b/146453195): use the spec in the dataset. - InlinePresentationSpec spec = request.getPresentationSpecs().get(0); - if (spec == null) { - Slog.w(TAG, "InlinePresentationSpec is not provided in the response data set"); - continue; + final int fieldIndex = dataset.getFieldIds().indexOf(autofillId); + if (fieldIndex < 0) { + Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset"); + return null; + } + final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation( + fieldIndex); + if (inlinePresentation == null) { + Slog.w(TAG, "InlinePresentation not found in dataset"); + return null; } InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(sessionId, dataset, - autofillId, spec, uiHandler, inlineSuggestionUi, client); + inlinePresentation, inlineSuggestionUi, client); + inlineSuggestions.add(inlineSuggestion); + } + return new InlineSuggestionsResponse(inlineSuggestions); + } + + /** + * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the + * autofill service. + */ + public static InlineSuggestionsResponse createInlineSuggestionsResponse(int requestId, + @NonNull Dataset[] datasets, + @NonNull AutofillId autofillId, + @NonNull Context context, + @NonNull AutoFillUI.AutoFillUiCallback client) { + if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called"); + + final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>(); + final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context); + for (Dataset dataset : datasets) { + final int fieldIndex = dataset.getFieldIds().indexOf(autofillId); + if (fieldIndex < 0) { + Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset"); + return null; + } + final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation( + fieldIndex); + if (inlinePresentation == null) { + Slog.w(TAG, "InlinePresentation not found in dataset"); + return null; + } + InlineSuggestion inlineSuggestion = createInlineSuggestion(requestId, dataset, + fieldIndex, + inlinePresentation, inlineSuggestionUi, client); inlineSuggestions.add(inlineSuggestion); } return new InlineSuggestionsResponse(inlineSuggestions); @@ -80,33 +117,55 @@ public final class InlineSuggestionFactory { private static InlineSuggestion createAugmentedInlineSuggestion(int sessionId, @NonNull Dataset dataset, - @NonNull AutofillId autofillId, - @NonNull InlinePresentationSpec spec, - @NonNull Handler uiHandler, + @NonNull InlinePresentation inlinePresentation, @NonNull InlineSuggestionUi inlineSuggestionUi, @NonNull IAutoFillManagerClient client) { // TODO(b/146453195): fill in the autofill hint properly. final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( - spec, InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""}); - final View.OnClickListener onClickListener = createOnClickListener(sessionId, dataset, - client); + inlinePresentation.getInlinePresentationSpec(), + InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""}); + final View.OnClickListener onClickListener = v -> { + try { + client.autofill(sessionId, dataset.getFieldIds(), dataset.getFieldValues()); + } catch (RemoteException e) { + Slog.w(TAG, "Encounter exception autofilling the values"); + } + }; final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo, - createInlineContentProvider(autofillId, dataset, uiHandler, inlineSuggestionUi, + createInlineContentProvider(inlinePresentation.getSlice(), inlineSuggestionUi, onClickListener)); return inlineSuggestion; } - private static IInlineContentProvider.Stub createInlineContentProvider( - @NonNull AutofillId autofillId, @NonNull Dataset dataset, @NonNull Handler uiHandler, + private static InlineSuggestion createInlineSuggestion(int requestId, + @NonNull Dataset dataset, + int fieldIndex, + @NonNull InlinePresentation inlinePresentation, @NonNull InlineSuggestionUi inlineSuggestionUi, + @NonNull AutoFillUI.AutoFillUiCallback client) { + // TODO(b/146453195): fill in the autofill hint properly. + final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( + inlinePresentation.getInlinePresentationSpec(), + InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""}); + final View.OnClickListener onClickListener = v -> { + client.fill(requestId, fieldIndex, dataset); + }; + final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo, + createInlineContentProvider(inlinePresentation.getSlice(), inlineSuggestionUi, + onClickListener)); + return inlineSuggestion; + } + + private static IInlineContentProvider.Stub createInlineContentProvider( + @NonNull Slice slice, @NonNull InlineSuggestionUi inlineSuggestionUi, @Nullable View.OnClickListener onClickListener) { return new IInlineContentProvider.Stub() { @Override public void provideContent(int width, int height, IInlineContentCallback callback) { - uiHandler.post(() -> { - SurfaceControl sc = inlineSuggestionUi.inflate(dataset, autofillId, - width, height, onClickListener); + UiThread.getHandler().post(() -> { + SurfaceControl sc = inlineSuggestionUi.inflate(slice, width, height, + onClickListener); try { callback.onContent(sc); } catch (RemoteException e) { @@ -117,19 +176,6 @@ public final class InlineSuggestionFactory { }; } - private static View.OnClickListener createOnClickListener(int sessionId, - @NonNull Dataset dataset, - @NonNull IAutoFillManagerClient client) { - return v -> { - if (sDebug) Slog.d(TAG, "Inline suggestion clicked"); - try { - client.autofill(sessionId, dataset.getFieldIds(), dataset.getFieldValues()); - } catch (RemoteException e) { - Slog.w(TAG, "Encounter exception autofilling the values"); - } - }; - } - private InlineSuggestionFactory() { } } diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index 2a7357b8fca6..5fbdd25ea6c6 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -161,8 +161,7 @@ final class RemoteAugmentedAutofillService @Override public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) { maybeHandleInlineSuggestions(sessionId, inlineSuggestionsData, - focusedId, inlineSuggestionsRequest, - inlineSuggestionsCallback, client); + focusedId, inlineSuggestionsCallback, client); requestAutofill.complete(null); } @@ -226,19 +225,15 @@ final class RemoteAugmentedAutofillService private void maybeHandleInlineSuggestions(int sessionId, @Nullable Dataset[] inlineSuggestionsData, @NonNull AutofillId focusedId, - @Nullable InlineSuggestionsRequest inlineSuggestionsRequest, @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback, @NonNull IAutoFillManagerClient client) { - if (inlineSuggestionsRequest == null - || ArrayUtils.isEmpty(inlineSuggestionsData) - || inlineSuggestionsCallback == null) { + if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null) { return; } try { inlineSuggestionsCallback.onInlineSuggestionsResponse( - InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse( - sessionId, inlineSuggestionsData, focusedId, inlineSuggestionsRequest, - getJobHandler(), mContext, client)); + InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(sessionId, + inlineSuggestionsData, focusedId, mContext, client)); } catch (RemoteException e) { Slog.w(TAG, "Exception sending inline suggestions response back to IME."); } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index cdd75107db24..ee37de56ca15 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -71,7 +71,6 @@ import android.service.autofill.FieldClassificationUserData; import android.service.autofill.FillContext; import android.service.autofill.FillRequest; import android.service.autofill.FillResponse; -import android.service.autofill.InlinePresentation; import android.service.autofill.InternalSanitizer; import android.service.autofill.InternalValidator; import android.service.autofill.SaveInfo; @@ -93,9 +92,6 @@ import android.view.autofill.AutofillManager.SmartSuggestionMode; import android.view.autofill.AutofillValue; import android.view.autofill.IAutoFillManagerClient; import android.view.autofill.IAutofillWindowPresenter; -import android.view.inline.InlinePresentationSpec; -import android.view.inputmethod.InlineSuggestion; -import android.view.inputmethod.InlineSuggestionInfo; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; @@ -106,8 +102,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; import com.android.internal.view.IInlineSuggestionsRequestCallback; import com.android.internal.view.IInlineSuggestionsResponseCallback; -import com.android.internal.view.inline.IInlineContentCallback; -import com.android.internal.view.inline.IInlineContentProvider; import com.android.server.autofill.ui.AutoFillUI; import com.android.server.autofill.ui.PendingUi; import com.android.server.inputmethod.InputMethodManagerInternal; @@ -148,6 +142,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private final Handler mHandler; private final Object mLock; private final AutoFillUI mUi; + private final Context mContext; private final MetricsLogger mMetricsLogger = new MetricsLogger(); @@ -773,6 +768,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mLock = lock; mUi = ui; mHandler = handler; + mContext = context; mRemoteFillService = serviceComponentName == null ? null : new RemoteFillService(context, serviceComponentName, userId, this, bindInstantServiceAllowed); @@ -2733,35 +2729,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } - final int size = datasets.size(); - final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>(); - - for (int index = 0; index < size; index++) { - final Dataset dataset = datasets.get(index); - //TODO(b/146453536): Use the proper presentation/spec for currently focused view. - final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(0); - if (inlinePresentation == null) { - if (sDebug) Log.d(TAG, "Missing InlinePresentation on dataset=" + dataset); - continue; - } - final InlinePresentationSpec spec = inlinePresentation.getInlinePresentationSpec(); - final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( - spec, InlineSuggestionInfo.SOURCE_AUTOFILL, new String[] { "" }); - - inlineSuggestions.add(new InlineSuggestion(inlineSuggestionInfo, - new IInlineContentProvider.Stub() { - @Override - public void provideContent(int width, int height, - IInlineContentCallback callback) throws RemoteException { - getUiForShowing().getSuggestionSurfaceForShowing(dataset, response, - mCurrentViewId, width, height, callback); - } - })); - } - + InlineSuggestionsResponse inlineSuggestionsResponse = + InlineSuggestionFactory.createInlineSuggestionsResponse(response.getRequestId(), + datasets.toArray(new Dataset[]{}), mCurrentViewId, mContext, this); try { - inlineContentCallback.onInlineSuggestionsResponse( - new InlineSuggestionsResponse(inlineSuggestions)); + inlineContentCallback.onInlineSuggestionsResponse(inlineSuggestionsResponse); } catch (RemoteException e) { Log.w(TAG, "onFillReady() remote error calling onInlineSuggestionsResponse()"); return false; diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index 0511bf2f3b74..26bb7c35b9dc 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -37,7 +37,6 @@ import android.service.autofill.ValueFinder; import android.text.TextUtils; import android.util.Slog; import android.view.KeyEvent; -import android.view.SurfaceControl; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.IAutofillWindowPresenter; @@ -45,7 +44,6 @@ import android.widget.Toast; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.view.inline.IInlineContentCallback; import com.android.server.LocalServices; import com.android.server.UiModeManagerInternal; import com.android.server.UiThread; @@ -173,36 +171,6 @@ public final class AutoFillUI { } /** - * TODO(b/137800469): Fill in javadoc. - * TODO(b/137800469): peoperly manage lifecycle of suggestions surfaces. - */ - public void getSuggestionSurfaceForShowing(@NonNull Dataset dataset, - @NonNull FillResponse response, AutofillId autofillId, int width, int height, - IInlineContentCallback cb) { - if (dataset == null) { - Slog.w(TAG, "getSuggestionSurfaceForShowing() called with null dataset"); - } - mHandler.post(() -> { - final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(mContext); - final SurfaceControl suggestionSurface = inlineSuggestionUi.inflate(dataset, - autofillId, width, height, v -> { - Slog.d(TAG, "Inline suggestion clicked"); - hideFillUiUiThread(mCallback, true); - if (mCallback != null) { - final int datasetIndex = response.getDatasets().indexOf(dataset); - mCallback.fill(response.getRequestId(), datasetIndex, dataset); - } - }); - - try { - cb.onContent(suggestionSurface); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException replying onContent(" + suggestionSurface + "): " + e); - } - }); - } - - /** * Shows the fill UI, removing the previous fill UI if the has changed. * * @param focusedId the currently focused field diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java index 1e3ee888a272..2460732d17dc 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java @@ -28,17 +28,13 @@ import android.content.Context; import android.graphics.PixelFormat; import android.graphics.drawable.Icon; import android.os.IBinder; -import android.service.autofill.Dataset; import android.util.Log; -import android.util.Slog; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; -import android.view.autofill.AutofillId; -import android.view.autofill.AutofillValue; import android.widget.ImageView; import android.widget.TextView; @@ -69,23 +65,18 @@ public class InlineSuggestionUi { */ @MainThread @Nullable - public SurfaceControl inflate(@NonNull Dataset dataset, @NonNull AutofillId autofillId, - int width, int height, @Nullable View.OnClickListener onClickListener) { + public SurfaceControl inflate(@NonNull Slice slice, int width, int height, + @Nullable View.OnClickListener onClickListener) { Log.d(TAG, "Inflating the inline suggestion UI"); - final int index = dataset.getFieldIds().indexOf(autofillId); - if (index < 0) { - Slog.w(TAG, "inflateInlineSuggestion(): AutofillId=" + autofillId - + " not found in dataset"); - return null; - } - final AutofillValue datasetValue = dataset.getFieldValues().get(index); + //TODO(b/137800469): Pass in inputToken from IME. final SurfaceControlViewHost wvr = new SurfaceControlViewHost(mContext, mContext.getDisplay(), (IBinder) null); final SurfaceControl sc = wvr.getSurfacePackage().getSurfaceControl(); - - final ViewGroup suggestionView = - (ViewGroup) renderSlice(dataset.getFieldInlinePresentation(index).getSlice()); + final ViewGroup suggestionView = (ViewGroup) renderSlice(slice); + if (onClickListener != null) { + suggestionView.setOnClickListener(onClickListener); + } WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 2c229b443f91..f0fa99a4eec7 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -31,6 +31,7 @@ import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSI import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE; +import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR; import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE; import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE; @@ -111,7 +112,6 @@ import com.android.server.backup.internal.ClearDataObserver; import com.android.server.backup.internal.OnTaskFinishedListener; import com.android.server.backup.internal.Operation; import com.android.server.backup.internal.PerformInitializeTask; -import com.android.server.backup.internal.RunBackupReceiver; import com.android.server.backup.internal.RunInitializeReceiver; import com.android.server.backup.internal.SetupObserver; import com.android.server.backup.keyvalue.BackupRequest; @@ -257,7 +257,6 @@ public class UserBackupManagerService { // Retry interval for clear/init when the transport is unavailable private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR; - public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN"; public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT"; private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED"; private static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName"; @@ -319,7 +318,6 @@ public class UserBackupManagerService { private boolean mSetupComplete; private boolean mAutoRestore; - private final PendingIntent mRunBackupIntent; private final PendingIntent mRunInitIntent; private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names @@ -417,7 +415,6 @@ public class UserBackupManagerService { @Nullable private File mAncestralSerialNumberFile; private final ContentObserver mSetupObserver; - private final BroadcastReceiver mRunBackupReceiver; private final BroadcastReceiver mRunInitReceiver; /** @@ -566,19 +563,9 @@ public class UserBackupManagerService { mDataDir = Objects.requireNonNull(dataDir, "dataDir cannot be null"); mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng); - // Receivers for scheduled backups and transport initialization operations. - mRunBackupReceiver = new RunBackupReceiver(this); - IntentFilter filter = new IntentFilter(); - filter.addAction(RUN_BACKUP_ACTION); - context.registerReceiverAsUser( - mRunBackupReceiver, - UserHandle.of(userId), - filter, - android.Manifest.permission.BACKUP, - /* scheduler */ null); - + // Receiver for transport initialization. mRunInitReceiver = new RunInitializeReceiver(this); - filter = new IntentFilter(); + IntentFilter filter = new IntentFilter(); filter.addAction(RUN_INITIALIZE_ACTION); context.registerReceiverAsUser( mRunInitReceiver, @@ -587,16 +574,6 @@ public class UserBackupManagerService { android.Manifest.permission.BACKUP, /* scheduler */ null); - Intent backupIntent = new Intent(RUN_BACKUP_ACTION); - backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mRunBackupIntent = - PendingIntent.getBroadcastAsUser( - context, - /* requestCode */ 0, - backupIntent, - /* flags */ 0, - UserHandle.of(userId)); - Intent initIntent = new Intent(RUN_INITIALIZE_ACTION); initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mRunInitIntent = @@ -659,7 +636,6 @@ public class UserBackupManagerService { mAgentTimeoutParameters.stop(); mConstants.stop(); mContext.getContentResolver().unregisterContentObserver(mSetupObserver); - mContext.unregisterReceiver(mRunBackupReceiver); mContext.unregisterReceiver(mRunInitReceiver); mContext.unregisterReceiver(mPackageTrackingReceiver); mBackupHandler.stop(); @@ -2538,15 +2514,38 @@ public class UserBackupManagerService { KeyValueBackupJob.schedule(mUserId, mContext, mConstants); } else { if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass"); - // Fire the intent that kicks off the whole shebang... - try { - mRunBackupIntent.send(); - } catch (PendingIntent.CanceledException e) { - // should never happen - Slog.e(TAG, "run-backup intent cancelled!"); + + synchronized (getQueueLock()) { + if (getPendingInits().size() > 0) { + // If there are pending init operations, we process those and then settle + // into the usual periodic backup schedule. + if (MORE_DEBUG) { + Slog.v(TAG, "Init pending at scheduled backup"); + } + try { + getAlarmManager().cancel(mRunInitIntent); + mRunInitIntent.send(); + } catch (PendingIntent.CanceledException ce) { + Slog.w(TAG, "Run init intent cancelled"); + } + return; } - // ...and cancel any pending scheduled job, because we've just superseded it - KeyValueBackupJob.cancel(mUserId, mContext); + } + + // Don't run backups if we're disabled or not yet set up. + if (!isEnabled() || !isSetupComplete()) { + Slog.w( + TAG, + "Backup pass but enabled=" + isEnabled() + + " setupComplete=" + isSetupComplete()); + return; + } + + // Fire the msg that kicks off the whole shebang... + Message message = mBackupHandler.obtainMessage(MSG_RUN_BACKUP); + mBackupHandler.sendMessage(message); + // ...and cancel any pending scheduled job, because we've just superseded it + KeyValueBackupJob.cancel(mUserId, mContext); } } finally { Binder.restoreCallingIdentity(oldId); diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java index b06fc52a24c2..05396f36b364 100644 --- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java +++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java @@ -158,10 +158,6 @@ public class BackupHandler extends Handler { .disposeOfTransportClient(transportClient, callerLogString); } Slog.v(TAG, "Backup requested but no transport available"); - synchronized (backupManagerService.getQueueLock()) { - backupManagerService.setBackupRunning(false); - } - backupManagerService.getWakelock().release(); break; } @@ -169,6 +165,21 @@ public class BackupHandler extends Handler { List<String> queue = new ArrayList<>(); DataChangedJournal oldJournal = backupManagerService.getJournal(); synchronized (backupManagerService.getQueueLock()) { + // Don't run backups if one is already running. + if (backupManagerService.isBackupRunning()) { + Slog.i(TAG, "Backup time but one already running"); + return; + } + + if (DEBUG) { + Slog.v(TAG, "Running a backup pass"); + } + + // Acquire the wakelock and pass it to the backup thread. It will be released + // once backup concludes. + backupManagerService.setBackupRunning(true); + backupManagerService.getWakelock().acquire(); + // Do we have any work to do? Construct the work queue // then release the synchronization lock to actually run // the backup. diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java deleted file mode 100644 index d37b106c2b26..000000000000 --- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java +++ /dev/null @@ -1,110 +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.backup.internal; - -import static com.android.server.backup.BackupManagerService.DEBUG; -import static com.android.server.backup.BackupManagerService.MORE_DEBUG; -import static com.android.server.backup.BackupManagerService.TAG; -import static com.android.server.backup.UserBackupManagerService.RUN_BACKUP_ACTION; -import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP; - -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.Handler; -import android.os.Message; -import android.util.Slog; - -import com.android.server.backup.UserBackupManagerService; - -/** - * A {@link BroadcastReceiver} for the action {@link UserBackupManagerService#RUN_BACKUP_ACTION} - * that runs an immediate backup operation if eligible. - */ -public class RunBackupReceiver extends BroadcastReceiver { - private final UserBackupManagerService mUserBackupManagerService; - - public RunBackupReceiver(UserBackupManagerService userBackupManagerService) { - mUserBackupManagerService = userBackupManagerService; - } - - /** - * Run a backup pass if we're eligible. We're eligible if the following conditions are met: - * - * <ul> - * <li>No transports are pending initialization (otherwise we kick off an initialization - * operation instead). - * <li>Backup is enabled for the user. - * <li>The user has completed setup. - * <li>No backup operation is currently running for the user. - * </ul> - */ - public void onReceive(Context context, Intent intent) { - if (!RUN_BACKUP_ACTION.equals(intent.getAction())) { - return; - } - - synchronized (mUserBackupManagerService.getQueueLock()) { - if (mUserBackupManagerService.getPendingInits().size() > 0) { - // If there are pending init operations, we process those and then settle into the - // usual periodic backup schedule. - if (MORE_DEBUG) { - Slog.v(TAG, "Init pending at scheduled backup"); - } - try { - PendingIntent runInitIntent = mUserBackupManagerService.getRunInitIntent(); - mUserBackupManagerService.getAlarmManager().cancel(runInitIntent); - runInitIntent.send(); - } catch (PendingIntent.CanceledException ce) { - Slog.w(TAG, "Run init intent cancelled"); - } - } else { - // Don't run backups if we're disabled or not yet set up. - if (!mUserBackupManagerService.isEnabled() - || !mUserBackupManagerService.isSetupComplete()) { - Slog.w( - TAG, - "Backup pass but enabled=" - + mUserBackupManagerService.isEnabled() - + " setupComplete=" - + mUserBackupManagerService.isSetupComplete()); - return; - } - - // Don't run backups if one is already running. - if (mUserBackupManagerService.isBackupRunning()) { - Slog.i(TAG, "Backup time but one already running"); - return; - } - - if (DEBUG) { - Slog.v(TAG, "Running a backup pass"); - } - - // Acquire the wakelock and pass it to the backup thread. It will be released once - // backup concludes. - mUserBackupManagerService.setBackupRunning(true); - mUserBackupManagerService.getWakelock().acquire(); - - Handler backupHandler = mUserBackupManagerService.getBackupHandler(); - Message message = backupHandler.obtainMessage(MSG_RUN_BACKUP); - backupHandler.sendMessage(message); - } - } - } -} diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index c1e23e42c14f..98b572801716 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -39,6 +39,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.uidRulesToString; @@ -5840,11 +5841,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { newNc.addCapability(NET_CAPABILITY_FOREGROUND); } - if (nai.isSuspended()) { - newNc.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); - } else { - newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED); - } if (nai.partialConnectivity) { newNc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY); } else { @@ -5852,6 +5848,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken()); + // TODO : remove this once all factories are updated to send NOT_SUSPENDED and NOT_ROAMING + if (!newNc.hasTransport(TRANSPORT_CELLULAR)) { + newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + newNc.addCapability(NET_CAPABILITY_NOT_ROAMING); + } + return newNc; } @@ -5896,6 +5898,19 @@ public class ConnectivityService extends IConnectivityManager.Stub // on this network. We might have been called by rematchNetworkAndRequests when a // network changed foreground state. processListenRequests(nai); + final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); + final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); + final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); + final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); + if (prevSuspended != suspended || prevRoaming != roaming) { + // TODO (b/73132094) : remove this call once the few users of onSuspended and + // onResumed have been removed. + notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED + : ConnectivityManager.CALLBACK_RESUMED); + // updateNetworkInfo will mix in the suspended info from the capabilities and + // take appropriate action for the network having possibly changed state. + updateNetworkInfo(nai, nai.networkInfo); + } } else { // If the requestable capabilities have changed or the score changed, we can't have been // called by rematchNetworkAndRequests, so it's safe to start a rematch. @@ -5903,6 +5918,9 @@ public class ConnectivityService extends IConnectivityManager.Stub notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED); } + // TODO : static analysis indicates that prevNc can't be null here (getAndSetNetworkCaps + // never returns null), so mark the relevant members and functions in nai as @NonNull and + // remove this test if (prevNc != null) { final boolean oldMetered = prevNc.isMetered(); final boolean newMetered = newNc.isMetered(); @@ -6597,10 +6615,31 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) { + @NonNull + private NetworkInfo mixInInfo(@NonNull final NetworkAgentInfo nai, @NonNull NetworkInfo info) { + final NetworkInfo newInfo = new NetworkInfo(info); + // The suspended and roaming bits are managed in NetworkCapabilities. + final boolean suspended = + !nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); + if (suspended && info.getDetailedState() == NetworkInfo.DetailedState.CONNECTED) { + // Only override the state with SUSPENDED if the network is currently in CONNECTED + // state. This is because the network could have been suspended before connecting, + // or it could be disconnecting while being suspended, and in both these cases + // the state should not be overridden. Note that the only detailed state that + // maps to State.CONNECTED is DetailedState.CONNECTED, so there is also no need to + // worry about multiple different substates of CONNECTED. + newInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, info.getReason(), + info.getExtraInfo()); + } + newInfo.setRoaming(!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + return newInfo; + } + + private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo info) { + final NetworkInfo newInfo = mixInInfo(networkAgent, info); + final NetworkInfo.State state = newInfo.getState(); NetworkInfo oldInfo = null; - final int oldScore = networkAgent.getCurrentScore(); synchronized (networkAgent) { oldInfo = networkAgent.networkInfo; networkAgent.networkInfo = newInfo; @@ -6682,17 +6721,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } else if (networkAgent.created && (oldInfo.getState() == NetworkInfo.State.SUSPENDED || state == NetworkInfo.State.SUSPENDED)) { - // going into or coming out of SUSPEND: re-score and notify - if (networkAgent.getCurrentScore() != oldScore) { - rematchAllNetworksAndRequests(); - } - updateCapabilities(networkAgent.getCurrentScore(), networkAgent, - networkAgent.networkCapabilities); - // TODO (b/73132094) : remove this call once the few users of onSuspended and - // onResumed have been removed. - notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ? - ConnectivityManager.CALLBACK_SUSPENDED : - ConnectivityManager.CALLBACK_RESUMED)); mLegacyTypeTracker.update(networkAgent); } } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 2f60817b4cd4..e9db9c819ab7 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -16,6 +16,8 @@ package com.android.server; +import static android.Manifest.permission.ACCESS_COARSE_LOCATION; +import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER; @@ -83,6 +85,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -1400,20 +1403,19 @@ public class LocationManagerService extends ILocationManager.Stub { private String getResolutionPermission(int resolutionLevel) { switch (resolutionLevel) { case RESOLUTION_LEVEL_FINE: - return android.Manifest.permission.ACCESS_FINE_LOCATION; + return ACCESS_FINE_LOCATION; case RESOLUTION_LEVEL_COARSE: - return android.Manifest.permission.ACCESS_COARSE_LOCATION; + return ACCESS_COARSE_LOCATION; default: return null; } } private int getAllowedResolutionLevel(int pid, int uid) { - if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, - pid, uid) == PERMISSION_GRANTED) { + if (mContext.checkPermission(ACCESS_FINE_LOCATION, pid, uid) == PERMISSION_GRANTED) { return RESOLUTION_LEVEL_FINE; - } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, - pid, uid) == PERMISSION_GRANTED) { + } else if (mContext.checkPermission(ACCESS_COARSE_LOCATION, pid, uid) + == PERMISSION_GRANTED) { return RESOLUTION_LEVEL_COARSE; } else { return RESOLUTION_LEVEL_NONE; @@ -1424,59 +1426,28 @@ public class LocationManagerService extends ILocationManager.Stub { return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid()); } - private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) { - if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { - throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission"); - } + private boolean checkCallingOrSelfLocationPermission() { + return mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED + || mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) + == PERMISSION_GRANTED; } - @GuardedBy("mLock") - private int getMinimumResolutionLevelForProviderUseLocked(String provider) { - if (GPS_PROVIDER.equals(provider) || PASSIVE_PROVIDER.equals(provider)) { - // gps and passive providers require FINE permission - return RESOLUTION_LEVEL_FINE; - } else if (NETWORK_PROVIDER.equals(provider) || FUSED_PROVIDER.equals(provider)) { - // network and fused providers are ok with COARSE or FINE - return RESOLUTION_LEVEL_COARSE; - } else { - for (LocationProviderManager lp : mProviderManagers) { - if (!lp.getName().equals(provider)) { - continue; - } - - ProviderProperties properties = lp.getProperties(); - if (properties != null) { - if (properties.mRequiresSatellite) { - // provider requiring satellites require FINE permission - return RESOLUTION_LEVEL_FINE; - } else if (properties.mRequiresNetwork || properties.mRequiresCell) { - // provider requiring network and or cell require COARSE or FINE - return RESOLUTION_LEVEL_COARSE; - } - } - } + private void enforceCallingOrSelfLocationPermission() { + if (checkCallingOrSelfLocationPermission()) { + return; } - return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE + throw new SecurityException("uid " + Binder.getCallingUid() + " does not have " + + ACCESS_COARSE_LOCATION + " or " + ACCESS_FINE_LOCATION + "."); } - @GuardedBy("mLock") - private void checkResolutionLevelIsSufficientForProviderUseLocked(int allowedResolutionLevel, - String providerName) { - int requiredResolutionLevel = getMinimumResolutionLevelForProviderUseLocked(providerName); - if (allowedResolutionLevel < requiredResolutionLevel) { - switch (requiredResolutionLevel) { - case RESOLUTION_LEVEL_FINE: - throw new SecurityException("\"" + providerName + "\" location provider " + - "requires ACCESS_FINE_LOCATION permission."); - case RESOLUTION_LEVEL_COARSE: - throw new SecurityException("\"" + providerName + "\" location provider " + - "requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission."); - default: - throw new SecurityException("Insufficient permission for \"" + providerName + - "\" location provider."); - } + private void enforceCallingOrSelfPackageName(String packageName) { + int uid = Binder.getCallingUid(); + if (ArrayUtils.contains(mPackageManager.getPackagesForUid(uid), packageName)) { + return; } + + throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid); } public static int resolutionLevelToOp(int allowedResolutionLevel) { @@ -1552,7 +1523,10 @@ public class LocationManagerService extends ILocationManager.Stub { */ @Override public List<String> getProviders(Criteria criteria, boolean enabledOnly) { - int allowedResolutionLevel = getCallerAllowedResolutionLevel(); + if (!checkCallingOrSelfLocationPermission()) { + return Collections.emptyList(); + } + synchronized (mLock) { ArrayList<String> providers = new ArrayList<>(mProviderManagers.size()); for (LocationProviderManager manager : mProviderManagers) { @@ -1560,9 +1534,6 @@ public class LocationManagerService extends ILocationManager.Stub { if (FUSED_PROVIDER.equals(name)) { continue; } - if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUseLocked(name)) { - continue; - } if (enabledOnly && !manager.isUseable()) { continue; } @@ -2006,33 +1977,18 @@ public class LocationManagerService extends ILocationManager.Stub { return sanitizedRequest; } - private void checkPackageName(String packageName) { - if (packageName == null) { - throw new SecurityException("invalid package name: " + null); - } - int uid = Binder.getCallingUid(); - String[] packages = mPackageManager.getPackagesForUid(uid); - if (packages == null) { - throw new SecurityException("invalid UID " + uid); - } - for (String pkg : packages) { - if (packageName.equals(pkg)) return; - } - throw new SecurityException("invalid package name: " + packageName); - } - @Override public void requestLocationUpdates(LocationRequest request, ILocationListener listener, PendingIntent intent, String packageName, String featureId, String listenerIdentifier) { Objects.requireNonNull(listenerIdentifier); + enforceCallingOrSelfLocationPermission(); + enforceCallingOrSelfPackageName(packageName); + synchronized (mLock) { if (request == null) request = DEFAULT_LOCATION_REQUEST; - checkPackageName(packageName); int allowedResolutionLevel = getCallerAllowedResolutionLevel(); - checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel, - request.getProvider()); WorkSource workSource = request.getWorkSource(); if (workSource != null && !workSource.isEmpty()) { mContext.enforceCallingOrSelfPermission( @@ -2139,7 +2095,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void removeUpdates(ILocationListener listener, PendingIntent intent, String packageName) { - checkPackageName(packageName); + enforceCallingOrSelfPackageName(packageName); int pid = Binder.getCallingPid(); int uid = Binder.getCallingUid(); @@ -2201,12 +2157,12 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public Location getLastLocation(LocationRequest r, String packageName, String featureId) { + enforceCallingOrSelfLocationPermission(); + enforceCallingOrSelfPackageName(packageName); + synchronized (mLock) { LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST; int allowedResolutionLevel = getCallerAllowedResolutionLevel(); - checkPackageName(packageName); - checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel, - request.getProvider()); // no need to sanitize this request, as only the provider name is used final int pid = Binder.getCallingPid(); @@ -2352,7 +2308,7 @@ public class LocationManagerService extends ILocationManager.Stub { public boolean injectLocation(Location location) { mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to inject location"); - mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, + mContext.enforceCallingPermission(ACCESS_FINE_LOCATION, "Access Fine Location permission not granted to inject Location"); synchronized (mLock) { @@ -2378,17 +2334,14 @@ public class LocationManagerService extends ILocationManager.Stub { String packageName, String featureId, String listenerIdentifier) { Objects.requireNonNull(listenerIdentifier); + mContext.enforceCallingOrSelfPermission(ACCESS_FINE_LOCATION, null); + enforceCallingOrSelfPackageName(packageName); + if (request == null) request = DEFAULT_LOCATION_REQUEST; int allowedResolutionLevel = getCallerAllowedResolutionLevel(); - checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel); if (intent == null) { throw new IllegalArgumentException("invalid pending intent: " + null); } - checkPackageName(packageName); - synchronized (mLock) { - checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel, - request.getProvider()); - } // Require that caller can manage given document boolean callerHasLocationHardwarePermission = mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE) @@ -2434,7 +2387,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (intent == null) { throw new IllegalArgumentException("invalid pending intent: " + null); } - checkPackageName(packageName); + enforceCallingOrSelfPackageName(packageName); if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent); @@ -2521,36 +2474,30 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean sendExtraCommand(String providerName, String command, Bundle extras) { - if (providerName == null) { - // throw NullPointerException to remain compatible with previous implementation - throw new NullPointerException(); - } + Objects.requireNonNull(providerName); + Objects.requireNonNull(command); mContext.enforceCallingOrSelfPermission( Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, null); + enforceCallingOrSelfLocationPermission(); - synchronized (mLock) { - checkResolutionLevelIsSufficientForProviderUseLocked(getCallerAllowedResolutionLevel(), - providerName); - - mLocationUsageLogger.logLocationApiUsage( - LocationStatsEnums.USAGE_STARTED, - LocationStatsEnums.API_SEND_EXTRA_COMMAND, - providerName); + mLocationUsageLogger.logLocationApiUsage( + LocationStatsEnums.USAGE_STARTED, + LocationStatsEnums.API_SEND_EXTRA_COMMAND, + providerName); - LocationProviderManager manager = getLocationProviderManager(providerName); - if (manager != null) { - manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(), command, - extras); - } + LocationProviderManager manager = getLocationProviderManager(providerName); + if (manager != null) { + manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(), command, + extras); + } - mLocationUsageLogger.logLocationApiUsage( - LocationStatsEnums.USAGE_ENDED, - LocationStatsEnums.API_SEND_EXTRA_COMMAND, - providerName); + mLocationUsageLogger.logLocationApiUsage( + LocationStatsEnums.USAGE_ENDED, + LocationStatsEnums.API_SEND_EXTRA_COMMAND, + providerName); - return true; - } + return true; } @Override diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 4f03a8e878e1..7324d9793cc9 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -2413,43 +2413,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { Intent intent = new Intent(ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); intent.putExtra(PHONE_CONSTANTS_STATE_KEY, dataStateToString(state)); intent.putExtra(PHONE_CONSTANTS_DATA_APN_KEY, apn); - intent.putExtra(PHONE_CONSTANTS_DATA_APN_TYPE_KEY, getApnTypesStringFromBitmask(apnType)); + intent.putExtra(PHONE_CONSTANTS_DATA_APN_TYPE_KEY, + ApnSetting.getApnTypesStringFromBitmask(apnType)); intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } - private static final Map<Integer, String> APN_TYPE_INT_MAP; - static { - APN_TYPE_INT_MAP = new android.util.ArrayMap<Integer, String>(); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_DEFAULT, "default"); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_MMS, "mms"); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_SUPL, "supl"); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_DUN, "dun"); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_HIPRI, "hipri"); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_FOTA, "fota"); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_IMS, "ims"); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_CBS, "cbs"); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_IA, "ia"); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_EMERGENCY, "emergency"); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_MCX, "mcx"); - APN_TYPE_INT_MAP.put(ApnSetting.TYPE_XCAP, "xcap"); - } - - /** - * Copy of ApnSetting#getApnTypesStringFromBitmask for legacy broadcast. - * @param apnTypeBitmask bitmask of APN types. - * @return comma delimited list of APN types. - */ - private static String getApnTypesStringFromBitmask(int apnTypeBitmask) { - List<String> types = new ArrayList<>(); - for (Integer type : APN_TYPE_INT_MAP.keySet()) { - if ((apnTypeBitmask & type) == type) { - types.add(APN_TYPE_INT_MAP.get(type)); - } - } - return android.text.TextUtils.join(",", types); - } - private void enforceNotifyPermissionOrCarrierPrivilege(String method) { if (checkNotifyPermission()) { return; diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java index dfc00806992b..4bf606e801f9 100644 --- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java +++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java @@ -53,6 +53,7 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) { boolean debuggableBuild = false; boolean finalBuild = false; + int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId); debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild(); finalBuild = mAndroidBuildClassifier.isFinalBuild(); @@ -76,15 +77,14 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) { return new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1); } - int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId); - // Do not allow overriding non-target sdk gated changes on user builds - if (minTargetSdk == -1) { - return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk); - } // Allow overriding any change for debuggable apps on non-final builds. if (!finalBuild) { return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk); } + // Do not allow overriding non-target sdk gated changes on user builds + if (minTargetSdk == -1) { + return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk); + } // Only allow to opt-in for a targetSdk gated change. if (applicationInfo.targetSdkVersion < minTargetSdk) { return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index c1ab55106ab1..d66aec576137 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -451,15 +451,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { && !isLingering(); } - /** - * Returns whether this network is currently suspended. A network is suspended if it is still - * connected but data temporarily fails to transfer. See {@link NetworkInfo.State#SUSPENDED} - * and {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED}. - */ - public boolean isSuspended() { - return networkInfo.getState() == NetworkInfo.State.SUSPENDED; - } - // Does this network satisfy request? public boolean satisfies(NetworkRequest request) { return created && diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index bc7307b3ee6c..74c1e63172fa 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -16,6 +16,8 @@ package com.android.server.content; +import static android.content.PermissionChecker.PERMISSION_GRANTED; + import android.Manifest; import android.accounts.Account; import android.annotation.Nullable; @@ -1212,7 +1214,7 @@ public final class ContentService extends IContentService.Stub { @RequiresPermission(android.Manifest.permission.CACHE_CONTENT) public void putCache(String packageName, Uri key, Bundle value, int userId) { Bundle.setDefusable(value, true); - enforceCrossUserPermission(userId, TAG); + enforceNonFullCrossUserPermission(userId, TAG); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG); mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(), packageName); @@ -1234,7 +1236,7 @@ public final class ContentService extends IContentService.Stub { @Override @RequiresPermission(android.Manifest.permission.CACHE_CONTENT) public Bundle getCache(String packageName, Uri key, int userId) { - enforceCrossUserPermission(userId, TAG); + enforceNonFullCrossUserPermission(userId, TAG); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG); mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(), packageName); @@ -1302,6 +1304,30 @@ public final class ContentService extends IContentService.Stub { } } + /** + * Checks if the request is from the system or an app that has {@code INTERACT_ACROSS_USERS} or + * {@code INTERACT_ACROSS_USERS_FULL} permission, if the {@code userHandle} is not for the + * caller. + * + * @param userHandle the user handle of the user we want to act on behalf of. + * @param message the message to log on security exception. + */ + private void enforceNonFullCrossUserPermission(int userHandle, String message) { + final int callingUser = UserHandle.getCallingUserId(); + if (callingUser == userHandle) { + return; + } + + int interactAcrossUsersState = mContext.checkCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS); + if (interactAcrossUsersState == PERMISSION_GRANTED) { + return; + } + + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); + } + private static int normalizeSyncable(int syncable) { if (syncable > 0) { return SyncStorageEngine.AuthorityInfo.SYNCABLE; diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 1794df3b602e..7c2ec78c1cbc 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -491,7 +491,7 @@ public class HdmiControlService extends SystemService { mIoThread.start(); mIoLooper = mIoThread.getLooper(); } - mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; + mPowerStatus = getInitialPowerStatus(); mProhibitMode = false; mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true); mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true); @@ -538,6 +538,28 @@ public class HdmiControlService extends SystemService { mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED); } + private void bootCompleted() { + // on boot, if device is interactive, set HDMI CEC state as powered on as well + if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) { + onWakeUp(); + } + } + + /** + * Returns the initial power status used when the HdmiControlService starts. + */ + @VisibleForTesting + int getInitialPowerStatus() { + // The initial power status is POWER_STATUS_TRANSIENT_TO_STANDBY. + // Once boot completes the service transitions to POWER_STATUS_ON if the device is + // interactive. + // Quiescent boot is a special boot mode, in which the screen stays off during boot + // and the device goes to sleep after boot has finished. + // We don't transition to POWER_STATUS_ON initially, as we might be booting in quiescent + // mode, during which we don't want to appear powered on to avoid being made active source. + return HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; + } + @VisibleForTesting void setCecController(HdmiCecController cecController) { mCecController = cecController; @@ -553,7 +575,9 @@ public class HdmiControlService extends SystemService { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { mTvInputManager = (TvInputManager) getContext().getSystemService( Context.TV_INPUT_SERVICE); - mPowerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); + mPowerManager = getContext().getSystemService(PowerManager.class); + } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { + runOnServiceThread(this::bootCompleted); } } @@ -579,9 +603,7 @@ public class HdmiControlService extends SystemService { * Called when the initialization of local devices is complete. */ private void onInitializeCecComplete(int initiatedBy) { - if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) { - mPowerStatus = HdmiControlManager.POWER_STATUS_ON; - } + updatePowerStatusOnInitializeCecComplete(); mWakeUpMessageReceived = false; if (isTvDeviceEnabled()) { @@ -606,6 +628,17 @@ public class HdmiControlService extends SystemService { } } + /** + * Updates the power status once the initialization of local devices is complete. + */ + private void updatePowerStatusOnInitializeCecComplete() { + if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) { + mPowerStatus = HdmiControlManager.POWER_STATUS_ON; + } else if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) { + mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; + } + } + private void registerContentObserver() { ContentResolver resolver = getContext().getContentResolver(); String[] settings = new String[] { @@ -2667,6 +2700,13 @@ public class HdmiControlService extends SystemService { } @ServiceThreadOnly + @VisibleForTesting + void setPowerStatus(int powerStatus) { + assertRunOnServiceThread(); + mPowerStatus = powerStatus; + } + + @ServiceThreadOnly boolean isPowerOnOrTransient() { assertRunOnServiceThread(); return mPowerStatus == HdmiControlManager.POWER_STATUS_ON diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java index 6afadbad2054..f5ed975bf772 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java @@ -56,6 +56,9 @@ import java.util.stream.Collectors; /** A helper class to serialize rules from the {@link Rule} model to Binary representation. */ public class RuleBinarySerializer implements RuleSerializer { + static final int TOTAL_RULE_SIZE_LIMIT = 200000; + static final int INDEXED_RULE_SIZE_LIMIT = 100000; + static final int NONINDEXED_RULE_SIZE_LIMIT = 1000; // Get the byte representation for a list of rules. @Override @@ -79,10 +82,23 @@ public class RuleBinarySerializer implements RuleSerializer { OutputStream indexingFileOutputStream) throws RuleSerializeException { try { + if (rules == null) { + throw new IllegalArgumentException("Null rules cannot be serialized."); + } + + if (rules.size() > TOTAL_RULE_SIZE_LIMIT) { + throw new IllegalArgumentException("Too many rules provided."); + } + // Determine the indexing groups and the order of the rules within each indexed group. Map<Integer, Map<String, List<Rule>>> indexedRules = RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules); + // Validate the rule blocks are not larger than expected limits. + verifySize(indexedRules.get(PACKAGE_NAME_INDEXED), INDEXED_RULE_SIZE_LIMIT); + verifySize(indexedRules.get(APP_CERTIFICATE_INDEXED), INDEXED_RULE_SIZE_LIMIT); + verifySize(indexedRules.get(NOT_INDEXED), NONINDEXED_RULE_SIZE_LIMIT); + // Serialize the rules. ByteTrackedOutputStream ruleFileByteTrackedOutputStream = new ByteTrackedOutputStream(rulesFileOutputStream); @@ -112,6 +128,16 @@ public class RuleBinarySerializer implements RuleSerializer { } } + private void verifySize(Map<String, List<Rule>> ruleListMap, int ruleSizeLimit) { + int totalRuleCount = + ruleListMap.values().stream() + .map(list -> list.size()) + .collect(Collectors.summingInt(Integer::intValue)); + if (totalRuleCount > ruleSizeLimit) { + throw new IllegalArgumentException("Too many rules provided in the indexing group."); + } + } + private void serializeRuleFileMetadata( Optional<Integer> formatVersion, ByteTrackedOutputStream outputStream) throws IOException { diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index 408c1c9b7d18..1cd8aad3f0c5 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.content.Intent; import android.media.MediaRoute2ProviderInfo; import android.media.RoutingSessionInfo; +import android.os.Bundle; import com.android.internal.annotations.GuardedBy; @@ -49,8 +50,8 @@ abstract class MediaRoute2Provider { mCallback = callback; } - public abstract void requestCreateSession(String packageName, String routeId, - String routeType, long requestId); + public abstract void requestCreateSession(String packageName, String routeId, long requestId, + @Nullable Bundle sessionHints); public abstract void releaseSession(String sessionId); public abstract void selectRoute(String sessionId, String routeId); diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java index 3840d0206016..97614619653e 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java @@ -26,6 +26,7 @@ import android.media.IMediaRoute2ProviderClient; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; import android.media.RoutingSessionInfo; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; @@ -73,11 +74,11 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } @Override - public void requestCreateSession(String packageName, String routeId, String routeType, - long requestId) { + public void requestCreateSession(String packageName, String routeId, long requestId, + Bundle sessionHints) { if (mConnectionReady) { - mActiveConnection.requestCreateSession(packageName, routeId, routeType, - requestId); + mActiveConnection.requestCreateSession( + packageName, routeId, requestId, sessionHints); updateBinding(); } } @@ -429,11 +430,10 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv mClient.dispose(); } - public void requestCreateSession(String packageName, String routeId, String routeType, - long requestId) { + public void requestCreateSession(String packageName, String routeId, long requestId, + Bundle sessionHints) { try { - mProvider.requestCreateSession(packageName, routeId, - routeType, requestId); + mProvider.requestCreateSession(packageName, routeId, requestId, sessionHints); } catch (RemoteException ex) { Slog.e(TAG, "Failed to deliver request to create a session.", ex); } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 161afb51cbfb..216753017010 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -22,6 +22,7 @@ import static android.media.MediaRouter2Utils.getProviderId; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; @@ -63,12 +64,12 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; /** - * TODO: Merge this to MediaRouterService once it's finished. + * Implements features related to {@link android.media.MediaRouter2} and + * {@link android.media.MediaRouter2Manager}. */ class MediaRouter2ServiceImpl { private static final String TAG = "MR2ServiceImpl"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final long ROUTE_SELECTION_REQUEST_TIMEOUT_MS = 5000L; private final Context mContext; private final Object mLock = new Object(); @@ -178,18 +179,14 @@ class MediaRouter2ServiceImpl { } public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route, - String routeFeature, int requestId) { + int requestId, Bundle sessionHints) { Objects.requireNonNull(client, "client must not be null"); Objects.requireNonNull(route, "route must not be null"); - if (TextUtils.isEmpty(routeFeature)) { - throw new IllegalArgumentException("routeFeature must not be empty"); - } final long token = Binder.clearCallingIdentity(); - try { synchronized (mLock) { - requestCreateSessionLocked(client, route, routeFeature, requestId); + requestCreateSessionLocked(client, route, requestId, sessionHints); } } finally { Binder.restoreCallingIdentity(token); @@ -381,6 +378,53 @@ class MediaRouter2ServiceImpl { } } + public void selectClientRoute(IMediaRouter2Manager manager, String sessionId, + MediaRoute2Info route) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + selectClientRouteLocked(manager, sessionId, route); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public void deselectClientRoute(IMediaRouter2Manager manager, String sessionId, + MediaRoute2Info route) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + deselectClientRouteLocked(manager, sessionId, route); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public void transferToClientRoute(IMediaRouter2Manager manager, String sessionId, + MediaRoute2Info route) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + transferClientRouteLocked(manager, sessionId, route); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public void releaseClientSession(IMediaRouter2Manager manager, String sessionId) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + releaseClientSessionLocked(manager, sessionId); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + //TODO: Review this is handling multi-user properly. void switchUser() { synchronized (mLock) { @@ -421,6 +465,7 @@ class MediaRouter2ServiceImpl { int uid, int pid, String packageName, int userId, boolean trusted) { final IBinder binder = client.asBinder(); if (mAllClientRecords.get(binder) == null) { + UserRecord userRecord = getOrCreateUserRecordLocked(userId); Client2Record clientRecord = new Client2Record(userRecord, client, uid, pid, packageName, trusted); @@ -450,7 +495,7 @@ class MediaRouter2ServiceImpl { } private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client, - @NonNull MediaRoute2Info route, @NonNull String routeFeature, long requestId) { + @NonNull MediaRoute2Info route, long requestId, @Nullable Bundle sessionHints) { final IBinder binder = client.asBinder(); final Client2Record clientRecord = mAllClientRecords.get(binder); @@ -463,7 +508,7 @@ class MediaRouter2ServiceImpl { clientRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::requestCreateSessionOnHandler, clientRecord.mUserRecord.mHandler, - clientRecord, route, routeFeature, requestId)); + clientRecord, route, requestId, sessionHints)); } } @@ -622,9 +667,9 @@ class MediaRouter2ServiceImpl { } long uniqueRequestId = toUniqueRequestId(managerRecord.mClientId, requestId); if (clientRecord != null && managerRecord.mTrusted) { - //TODO: select route feature properly + //TODO: Use client's OnCreateSessionListener to send proper session hints. requestCreateSessionLocked(clientRecord.mClient, route, - route.getFeatures().get(0), uniqueRequestId); + uniqueRequestId, null /* sessionHints */); } } } @@ -658,6 +703,7 @@ class MediaRouter2ServiceImpl { ManagerRecord managerRecord = mAllManagerRecords.get(binder); if (managerRecord == null) { + Slog.w(TAG, "getActiveSessionLocked: Ignoring unknown manager"); return Collections.emptyList(); } @@ -681,6 +727,93 @@ class MediaRouter2ServiceImpl { return userRecord; } + private void selectClientRouteLocked(IMediaRouter2Manager manager, String sessionId, + MediaRoute2Info route) { + final IBinder binder = manager.asBinder(); + ManagerRecord managerRecord = mAllManagerRecords.get(binder); + + if (managerRecord == null) { + Slog.w(TAG, "selectClientRouteLocked: Ignoring unknown manager."); + return; + } + //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?) + Client2Record clientRecord = managerRecord.mUserRecord.mHandler + .findClientforSessionLocked(sessionId); + if (clientRecord == null) { + Slog.w(TAG, "selectClientRouteLocked: Ignoring unknown session."); + return; + } + + clientRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::selectRouteOnHandler, + clientRecord.mUserRecord.mHandler, + clientRecord, sessionId, route)); + } + + private void deselectClientRouteLocked(IMediaRouter2Manager manager, String sessionId, + MediaRoute2Info route) { + final IBinder binder = manager.asBinder(); + ManagerRecord managerRecord = mAllManagerRecords.get(binder); + + if (managerRecord == null) { + Slog.w(TAG, "deselectClientRouteLocked: Ignoring unknown manager."); + return; + } + //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?) + Client2Record clientRecord = managerRecord.mUserRecord.mHandler + .findClientforSessionLocked(sessionId); + if (clientRecord == null) { + Slog.w(TAG, "deslectClientRouteLocked: Ignoring unknown session."); + return; + } + + clientRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::deselectRouteOnHandler, + clientRecord.mUserRecord.mHandler, + clientRecord, sessionId, route)); + } + + private void transferClientRouteLocked(IMediaRouter2Manager manager, String sessionId, + MediaRoute2Info route) { + final IBinder binder = manager.asBinder(); + ManagerRecord managerRecord = mAllManagerRecords.get(binder); + + if (managerRecord == null) { + Slog.w(TAG, "transferClientRouteLocked: Ignoring unknown manager."); + return; + } + //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?) + Client2Record clientRecord = managerRecord.mUserRecord.mHandler + .findClientforSessionLocked(sessionId); + if (clientRecord == null) { + Slog.w(TAG, "transferClientRouteLocked: Ignoring unknown session."); + return; + } + + clientRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::transferToRouteOnHandler, + clientRecord.mUserRecord.mHandler, + clientRecord, sessionId, route)); + } + + private void releaseClientSessionLocked(IMediaRouter2Manager manager, String sessionId) { + final IBinder binder = manager.asBinder(); + ManagerRecord managerRecord = mAllManagerRecords.get(binder); + + if (managerRecord == null) { + Slog.w(TAG, "releaseClientSessionLocked: Ignoring unknown manager."); + return; + } + + Client2Record clientRecord = managerRecord.mUserRecord.mHandler + .findClientforSessionLocked(sessionId); + + managerRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::releaseSessionOnHandler, + managerRecord.mUserRecord.mHandler, + clientRecord, sessionId)); + } + private void disposeUserIfNeededLocked(UserRecord userRecord) { // If there are no records left and the user is no longer current then go ahead // and purge the user record and all of its associated state. If the user is current @@ -901,6 +1034,11 @@ class MediaRouter2ServiceImpl { this, provider, sessionInfo)); } + @Nullable + public Client2Record findClientforSessionLocked(@NonNull String sessionId) { + return mSessionToClientMap.get(sessionId); + } + //TODO: notify session info updates private void onProviderStateChangedOnHandler(MediaRoute2Provider provider) { int providerIndex = getProviderInfoIndex(provider.getUniqueId()); @@ -985,7 +1123,7 @@ class MediaRouter2ServiceImpl { } private void requestCreateSessionOnHandler(Client2Record clientRecord, - MediaRoute2Info route, String routeFeature, long requestId) { + MediaRoute2Info route, long requestId, @Nullable Bundle sessionHints) { final MediaRoute2Provider provider = findProvider(route.getProviderId()); if (provider == null) { @@ -995,20 +1133,13 @@ class MediaRouter2ServiceImpl { return; } - if (!route.getFeatures().contains(routeFeature)) { - Slog.w(TAG, "Ignoring session creation request since the given route=" + route - + " doesn't support the given feature=" + routeFeature); - notifySessionCreationFailed(clientRecord, toClientRequestId(requestId)); - return; - } - // TODO: Apply timeout for each request (How many seconds should we wait?) - SessionCreationRequest request = new SessionCreationRequest( - clientRecord, route, routeFeature, requestId); + SessionCreationRequest request = + new SessionCreationRequest(clientRecord, route, requestId); mSessionCreationRequests.add(request); provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(), - routeFeature, requestId); + requestId, sessionHints); } private void selectRouteOnHandler(@NonNull Client2Record clientRecord, @@ -1140,9 +1271,10 @@ class MediaRouter2ServiceImpl { private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo, long requestId) { + notifySessionCreatedToManagers(getManagers(), sessionInfo); + if (requestId == MediaRoute2ProviderService.REQUEST_ID_UNKNOWN) { // The session is created without any matching request. - // TODO: Tell managers for the session creation return; } @@ -1173,15 +1305,11 @@ class MediaRouter2ServiceImpl { } String originalRouteId = matchingRequest.mRoute.getId(); - String originalRouteFeature = matchingRequest.mRouteFeature; Client2Record client2Record = matchingRequest.mClientRecord; - if (!sessionInfo.getSelectedRoutes().contains(originalRouteId) - || !TextUtils.equals(originalRouteFeature, - sessionInfo.getRouteFeature())) { + if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)) { Slog.w(TAG, "Created session doesn't match the original request." + " originalRouteId=" + originalRouteId - + ", originalRouteFeature=" + originalRouteFeature + ", requestId=" + requestId + ", sessionInfo=" + sessionInfo); notifySessionCreationFailed(matchingRequest.mClientRecord, toClientRequestId(requestId)); @@ -1192,7 +1320,6 @@ class MediaRouter2ServiceImpl { notifySessionCreated(matchingRequest.mClientRecord, sessionInfo, toClientRequestId(requestId)); mSessionToClientMap.put(sessionInfo.getId(), client2Record); - // TODO: Tell managers for the session creation } private void onSessionCreationFailedOnHandler(@NonNull MediaRoute2Provider provider, @@ -1221,29 +1348,29 @@ class MediaRouter2ServiceImpl { private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo) { + List<IMediaRouter2Manager> managers = getManagers(); + notifySessionInfosChangedToManagers(managers); Client2Record client2Record = mSessionToClientMap.get( sessionInfo.getId()); if (client2Record == null) { Slog.w(TAG, "No matching client found for session=" + sessionInfo); - // TODO: Tell managers for the session update return; } notifySessionInfoChanged(client2Record, sessionInfo); - // TODO: Tell managers for the session update } private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo) { + List<IMediaRouter2Manager> managers = getManagers(); + notifySessionInfosChangedToManagers(managers); Client2Record client2Record = mSessionToClientMap.get(sessionInfo.getId()); if (client2Record == null) { Slog.w(TAG, "No matching client found for session=" + sessionInfo); - // TODO: Tell managers for the session release return; } notifySessionReleased(client2Record, sessionInfo); - // TODO: Tell managers for the session release } private void notifySessionCreated(Client2Record clientRecord, @@ -1349,11 +1476,6 @@ class MediaRouter2ServiceImpl { } } - // TODO: Remove notifyRouteSelected* methods - private void notifyRouteSelectedToClient(IMediaRouter2Client client, - MediaRoute2Info route, int reason, Bundle controlHints) { - } - private void notifyRoutesAddedToClients(List<IMediaRouter2Client> clients, List<MediaRoute2Info> routes) { for (IMediaRouter2Client client : clients) { @@ -1435,6 +1557,29 @@ class MediaRouter2ServiceImpl { } } + private void notifySessionCreatedToManagers(List<IMediaRouter2Manager> managers, + RoutingSessionInfo sessionInfo) { + for (IMediaRouter2Manager manager : managers) { + try { + manager.notifySessionCreated(sessionInfo); + } catch (RemoteException ex) { + Slog.w(TAG, "notifySessionCreatedToManagers: " + + "failed to notify. Manager probably died.", ex); + } + } + } + + private void notifySessionInfosChangedToManagers(List<IMediaRouter2Manager> managers) { + for (IMediaRouter2Manager manager : managers) { + try { + manager.notifySessionsUpdated(); + } catch (RemoteException ex) { + Slog.w(TAG, "notifySessionInfosChangedToManagers: " + + "failed to notify. Manager probably died.", ex); + } + } + } + private void updateClientUsage(Client2Record clientRecord) { MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { @@ -1448,8 +1593,6 @@ class MediaRouter2ServiceImpl { } for (IMediaRouter2Manager manager : managers) { try { - manager.notifyRouteSelected(clientRecord.mPackageName, - clientRecord.mSelectedRoute); manager.notifyPreferredFeaturesChanged(clientRecord.mPackageName, clientRecord.mDiscoveryPreference.getPreferredFeatures()); } catch (RemoteException ex) { @@ -1470,15 +1613,12 @@ class MediaRouter2ServiceImpl { final class SessionCreationRequest { public final Client2Record mClientRecord; public final MediaRoute2Info mRoute; - public final String mRouteFeature; public final long mRequestId; SessionCreationRequest(@NonNull Client2Record clientRecord, - @NonNull MediaRoute2Info route, - @NonNull String routeFeature, long requestId) { + @NonNull MediaRoute2Info route, long requestId) { mClientRecord = clientRecord; mRoute = route; - mRouteFeature = routeFeature; mRequestId = requestId; } } diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index c80a898184bc..5437fadf2d74 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -42,6 +42,7 @@ import android.media.RemoteDisplayState.RemoteDisplayInfo; import android.media.RouteDiscoveryPreference; import android.media.RoutingSessionInfo; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -460,8 +461,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route, - String routeType, int requestId) { - mService2.requestCreateSession(client, route, routeType, requestId); + int requestId, Bundle sessionHints) { + mService2.requestCreateSession(client, route, requestId, sessionHints); } // Binder call @@ -556,6 +557,33 @@ public final class MediaRouterService extends IMediaRouterService.Stub return mService2.getActiveSessions(manager); } + // Binder call + @Override + public void selectClientRoute(IMediaRouter2Manager manager, String sessionId, + MediaRoute2Info route) { + mService2.selectClientRoute(manager, sessionId, route); + } + + // Binder call + @Override + public void deselectClientRoute(IMediaRouter2Manager manager, String sessionId, + MediaRoute2Info route) { + mService2.deselectClientRoute(manager, sessionId, route); + } + + // Binder call + @Override + public void transferToClientRoute(IMediaRouter2Manager manager, String sessionId, + MediaRoute2Info route) { + mService2.transferToClientRoute(manager, sessionId, route); + } + + // Binder call + @Override + public void releaseClientSession(IMediaRouter2Manager manager, String sessionId) { + mService2.releaseClientSession(manager, sessionId); + } + void restoreBluetoothA2dp() { try { boolean a2dpOn; diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 6695227fd236..56c33fe339ea 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -26,6 +26,7 @@ import android.media.IAudioRoutesObserver; import android.media.IAudioService; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -96,8 +97,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } @Override - public void requestCreateSession(String packageName, String routeId, String routeType, - long requestId) { + public void requestCreateSession(String packageName, String routeId, long requestId, + Bundle sessionHints) { // Do nothing } diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java index eaf120ee2755..0e14364b4280 100644 --- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java +++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java @@ -15,7 +15,10 @@ */ package com.android.server.notification; +import android.app.Notification; +import android.app.NotificationChannel; import android.content.Context; +import android.util.FeatureFlagUtils; import android.util.Slog; /** @@ -26,8 +29,10 @@ public class NotificationChannelExtractor implements NotificationSignalExtractor private static final boolean DBG = false; private RankingConfig mConfig; + private Context mContext; public void initialize(Context ctx, NotificationUsageStats usageStats) { + mContext = ctx; if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); } @@ -41,11 +46,11 @@ public class NotificationChannelExtractor implements NotificationSignalExtractor if (DBG) Slog.d(TAG, "missing config"); return null; } - - record.updateNotificationChannel(mConfig.getConversationNotificationChannel( + NotificationChannel updatedChannel = mConfig.getConversationNotificationChannel( record.sbn.getPackageName(), record.sbn.getUid(), record.getChannel().getId(), - record.getNotification().getShortcutId(), true, false)); + record.sbn.getShortcutId(mContext), true, false); + record.updateNotificationChannel(updatedChannel); return null; } diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java index 1b56c7bb5b8f..41bc29f7a82e 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java @@ -68,6 +68,8 @@ public class NotificationHistoryManager { private final SparseArray<List<String>> mUserPendingPackageRemovals = new SparseArray<>(); @GuardedBy("mLock") private final SparseBooleanArray mHistoryEnabled = new SparseBooleanArray(); + @GuardedBy("mLock") + private final SparseBooleanArray mUserPendingHistoryDisables = new SparseBooleanArray(); public NotificationHistoryManager(Context context, Handler handler) { mContext = context; @@ -75,6 +77,11 @@ public class NotificationHistoryManager { mSettingsObserver = new SettingsObserver(handler); } + @VisibleForTesting + void onDestroy() { + mSettingsObserver.stopObserving(); + } + void onBootPhaseAppsCanStart() { mSettingsObserver.observe(); } @@ -99,8 +106,8 @@ public class NotificationHistoryManager { } // delete history if it was disabled when the user was locked - if (!mHistoryEnabled.get(userId)) { - userHistory.disableHistory(); + if (mUserPendingHistoryDisables.get(userId)) { + disableHistory(userHistory, userId); } } } @@ -118,6 +125,7 @@ public class NotificationHistoryManager { // removed) - we just need clean up our internal state for GC mUserPendingPackageRemovals.put(userId, null); mHistoryEnabled.put(userId, false); + mUserPendingHistoryDisables.put(userId, false); onUserStopped(userId); } } @@ -213,20 +221,29 @@ public class NotificationHistoryManager { void onHistoryEnabledChanged(@UserIdInt int userId, boolean historyEnabled) { synchronized (mLock) { - mHistoryEnabled.put(userId, historyEnabled); - - // These requests might fail if the user is locked; onUserUnlocked will pick up those - // cases + if (historyEnabled) { + mHistoryEnabled.put(userId, historyEnabled); + } final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(userId); if (userHistory != null) { if (!historyEnabled) { - userHistory.disableHistory(); + disableHistory(userHistory, userId); } + } else { + mUserPendingHistoryDisables.put(userId, !historyEnabled); } } } + private void disableHistory(NotificationHistoryDatabase userHistory, @UserIdInt int userId) { + userHistory.disableHistory(); + + mUserPendingHistoryDisables.put(userId, false); + mHistoryEnabled.put(userId, false); + mUserState.put(userId, null); + } + @GuardedBy("mLock") private @Nullable NotificationHistoryDatabase getUserHistoryAndInitializeIfNeededLocked( int userId) { @@ -316,6 +333,11 @@ public class NotificationHistoryManager { } } + void stopObserving() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.unregisterContentObserver(this); + } + @Override public void onChange(boolean selfChange, Uri uri, int userId) { update(uri, userId); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 1d493647304f..385d84a93fbd 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -208,6 +208,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.FeatureFlagUtils; import android.util.IntArray; import android.util.Log; import android.util.Pair; @@ -3038,7 +3039,7 @@ public class NotificationManagerService extends SystemService { @Override public void createConversationNotificationChannelForPackage(String pkg, int uid, - NotificationChannel parentChannel, String conversationId) { + String triggeringKey, NotificationChannel parentChannel, String conversationId) { enforceSystemOrSystemUI("only system can call this"); Preconditions.checkNotNull(parentChannel); Preconditions.checkNotNull(conversationId); @@ -3049,6 +3050,8 @@ public class NotificationManagerService extends SystemService { conversationChannel.setConversationId(parentId, conversationId); createNotificationChannelsImpl( pkg, uid, new ParceledListSlice(Arrays.asList(conversationChannel))); + mRankingHandler.requestSort(); + handleSavePolicyFile(); } @Override @@ -5257,9 +5260,10 @@ public class NotificationManagerService extends SystemService { if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) { channelId = (new Notification.TvExtender(notification)).getChannelId(); } - // TODO: flag this behavior String shortcutId = notification.getShortcutId(); - if (shortcutId == null + if (FeatureFlagUtils.isEnabled(getContext(), + FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ) + && shortcutId == null && notification.getNotificationStyle() == Notification.MessagingStyle.class) { shortcutId = id + tag + NotificationChannel.PLACEHOLDER_CONVERSATION_ID; } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 185d75c754a2..b0c1863ea31d 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; @@ -40,6 +41,7 @@ import android.service.notification.RankingHelperProto; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.FeatureFlagUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; @@ -139,6 +141,8 @@ public class PreferencesHelper implements RankingConfig { private boolean mAreChannelsBypassingDnd; private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS; + private boolean mAllowInvalidShortcuts = false; + private static final String BADGING_FORCED_TRUE = "force_badging_true_for_bug"; // STOPSHIP (b/142218092) this should be removed before ship @@ -164,6 +168,8 @@ public class PreferencesHelper implements RankingConfig { updateBadgingEnabled(); updateBubblesEnabled(); syncChannelsBypassingDnd(mContext.getUserId()); + mAllowInvalidShortcuts = FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ); } public void readXml(XmlPullParser parser, boolean forRestore, int userId) @@ -266,7 +272,14 @@ public class PreferencesHelper implements RankingConfig { } channel.setImportanceLockedByCriticalDeviceFunction( r.defaultAppLockedImportance); - r.channels.put(id, channel); + boolean isInvalidShortcutChannel = + channel.getConversationId() != null && + channel.getConversationId().contains( + PLACEHOLDER_CONVERSATION_ID); + if (mAllowInvalidShortcuts || (!mAllowInvalidShortcuts + && !isInvalidShortcutChannel)) { + r.channels.put(id, channel); + } } } // Delegate diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index ed7139991937..bdc1b074920a 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -16,6 +16,8 @@ package com.android.server.pm; import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES; +import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY; +import static android.content.pm.CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; @@ -26,6 +28,7 @@ import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.AppOpsManager.Mode; import android.app.IApplicationThread; import android.app.admin.DevicePolicyEventLogger; import android.app.admin.DevicePolicyManagerInternal; @@ -66,8 +69,6 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private Context mContext; private Injector mInjector; private AppOpsService mAppOpsService; - private final DevicePolicyManagerInternal mDpmi; - private final IPackageManager mIpm; public CrossProfileAppsServiceImpl(Context context) { this(context, new InjectorImpl(context)); @@ -77,8 +78,6 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { CrossProfileAppsServiceImpl(Context context, Injector injector) { mContext = context; mInjector = injector; - mIpm = AppGlobals.getPackageManager(); - mDpmi = LocalServices.getService(DevicePolicyManagerInternal.class); } @Override @@ -144,7 +143,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { // must have the required permission and the users must be in the same profile group // in order to launch any of its own activities. if (callerUserId != userId) { - final int permissionFlag = ActivityManager.checkComponentPermission( + final int permissionFlag = mInjector.checkComponentPermission( android.Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid, -1, true); if (permissionFlag != PackageManager.PERMISSION_GRANTED @@ -172,23 +171,27 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { public boolean canRequestInteractAcrossProfiles(String callingPackage) { Objects.requireNonNull(callingPackage); verifyCallingPackage(callingPackage); - - final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked( + return canRequestInteractAcrossProfilesUnchecked( callingPackage, mInjector.getCallingUserId()); + } + + private boolean canRequestInteractAcrossProfilesUnchecked( + String packageName, @UserIdInt int userId) { + List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(packageName, userId); if (targetUserProfiles.isEmpty()) { return false; } - if (!hasRequestedAppOpPermission( - AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), callingPackage)) { + AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) { return false; } - return isCrossProfilePackageWhitelisted(callingPackage); + return isCrossProfilePackageWhitelisted(packageName); } private boolean hasRequestedAppOpPermission(String permission, String packageName) { try { - String[] packages = mIpm.getAppOpPermissionPackages(permission); + String[] packages = + mInjector.getIPackageManager().getAppOpPermissionPackages(permission); return ArrayUtils.contains(packages, packageName); } catch (RemoteException exc) { Slog.e(TAG, "PackageManager dead. Cannot get permission info"); @@ -206,7 +209,6 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { if (targetUserProfiles.isEmpty()) { return false; } - final int callingUid = mInjector.getCallingUid(); return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid) || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, callingUid) @@ -219,7 +221,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private boolean isCrossProfilePackageWhitelisted(String packageName) { final long ident = mInjector.clearCallingIdentity(); try { - return mDpmi.getAllCrossProfilePackages().contains(packageName); + return mInjector.getDevicePolicyManagerInternal() + .getAllCrossProfilePackages().contains(packageName); } finally { mInjector.restoreCallingIdentity(ident); } @@ -295,6 +298,104 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } + @Override + public void setInteractAcrossProfilesAppOp(String packageName, @Mode int newMode) { + final int callingUid = mInjector.getCallingUid(); + if (!isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid) + && !isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, callingUid)) { + throw new SecurityException( + "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL is required to set the" + + " app-op for interacting across profiles."); + } + if (!isPermissionGranted(Manifest.permission.MANAGE_APP_OPS_MODES, callingUid)) { + throw new SecurityException( + "MANAGE_APP_OPS_MODES is required to set the app-op for interacting across" + + " profiles."); + } + final int callingUserId = mInjector.getCallingUserId(); + if (newMode == AppOpsManager.MODE_ALLOWED + && !canRequestInteractAcrossProfilesUnchecked(packageName, callingUserId)) { + // The user should not be prompted for apps that cannot request to interact across + // profiles. However, we return early here if required to avoid race conditions. + Slog.e(TAG, "Tried to turn on the appop for interacting across profiles for invalid" + + " app " + packageName); + return; + } + final int[] profileIds = + mInjector.getUserManager().getProfileIds(callingUserId, /* enabledOnly= */ false); + for (int profileId : profileIds) { + if (!isPackageInstalled(packageName, profileId)) { + continue; + } + setInteractAcrossProfilesAppOpForUser(packageName, newMode, profileId); + } + } + + private boolean isPackageInstalled(String packageName, @UserIdInt int userId) { + final int callingUid = mInjector.getCallingUid(); + final long identity = mInjector.clearCallingIdentity(); + try { + final PackageInfo info = + mInjector.getPackageManagerInternal() + .getPackageInfo( + packageName, + MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + callingUid, + userId); + return info != null; + } finally { + mInjector.restoreCallingIdentity(identity); + } + } + + private void setInteractAcrossProfilesAppOpForUser( + String packageName, @Mode int newMode, @UserIdInt int userId) { + try { + setInteractAcrossProfilesAppOpForUserOrThrow(packageName, newMode, userId); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Missing package " + packageName + " on user ID " + userId, e); + } + } + + private void setInteractAcrossProfilesAppOpForUserOrThrow( + String packageName, @Mode int newMode, @UserIdInt int userId) + throws PackageManager.NameNotFoundException { + final int uid = mInjector.getPackageManager() + .getPackageUidAsUser(packageName, /* flags= */ 0, userId); + if (currentModeEquals(newMode, packageName, uid)) { + Slog.w(TAG,"Attempt to set mode to existing value of " + newMode + " for " + + packageName + " on user ID " + userId); + return; + } + mInjector.getAppOpsManager() + .setMode(OP_INTERACT_ACROSS_PROFILES, + uid, + packageName, + newMode); + sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId)); + } + + private boolean currentModeEquals(@Mode int otherMode, String packageName, int uid) { + final String op = + AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES); + return otherMode == + mInjector.getAppOpsManager().unsafeCheckOpNoThrow(op, uid, packageName); + } + + private void sendCanInteractAcrossProfilesChangedBroadcast( + String packageName, int uid, UserHandle userHandle) { + final Intent intent = new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED) + .setPackage(packageName); + if (!appDeclaresCrossProfileAttribute(uid)) { + intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY); + } + mInjector.sendBroadcastAsUser(intent, userHandle); + } + + private boolean appDeclaresCrossProfileAttribute(int uid) { + return mInjector.getPackageManagerInternal().getPackage(uid).isCrossProfile(); + } + private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); try { @@ -311,8 +412,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { mInjector.getAppOpsManager().checkPackage(mInjector.getCallingUid(), callingPackage); } - private static boolean isPermissionGranted(String permission, int uid) { - return PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission( + private boolean isPermissionGranted(String permission, int uid) { + return PackageManager.PERMISSION_GRANTED == mInjector.checkComponentPermission( permission, uid, /* owningUid= */-1, /* exported= */ true); } @@ -376,6 +477,27 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { public ActivityTaskManagerInternal getActivityTaskManagerInternal() { return LocalServices.getService(ActivityTaskManagerInternal.class); } + + @Override + public IPackageManager getIPackageManager() { + return AppGlobals.getPackageManager(); + } + + @Override + public DevicePolicyManagerInternal getDevicePolicyManagerInternal() { + return LocalServices.getService(DevicePolicyManagerInternal.class); + } + + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user) { + mContext.sendBroadcastAsUser(intent, user); + } + + @Override + public int checkComponentPermission( + String permission, int uid, int owningUid, boolean exported) { + return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported); + } } @VisibleForTesting @@ -401,5 +523,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { ActivityManagerInternal getActivityManagerInternal(); ActivityTaskManagerInternal getActivityTaskManagerInternal(); + + IPackageManager getIPackageManager(); + + DevicePolicyManagerInternal getDevicePolicyManagerInternal(); + + void sendBroadcastAsUser(Intent intent, UserHandle user); + + int checkComponentPermission(String permission, int uid, int owningUid, boolean exported); } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 6331dd46c035..b1c38d1ebed4 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -400,10 +400,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } finally { IoUtils.closeQuietly(fis); } - // After all of the sessions were loaded, they are ready to be sealed and validated + // Re-sealing the sealed sessions. for (int i = 0; i < mSessions.size(); ++i) { PackageInstallerSession session = mSessions.valueAt(i); - session.sealAndValidateIfNecessary(); + session.sealIfNecessary(); } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 165bdebe070f..71555c98f9d2 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1374,15 +1374,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } /** - * If session should be sealed, then it's sealed to prevent further modification - * and then it's validated. - * - * If the session was sealed but something went wrong then it's destroyed. + * If session should be sealed, then it's sealed to prevent further modification. + * If the session can't be sealed then it's destroyed. * * <p> This is meant to be called after all of the sessions are loaded and added to * PackageInstallerService */ - void sealAndValidateIfNecessary() { + void sealIfNecessary() { synchronized (mLock) { if (!mShouldBeSealed || isStagedAndInTerminalState()) { return; @@ -1391,9 +1389,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { List<PackageInstallerSession> childSessions = getChildSessions(); synchronized (mLock) { try { - sealAndValidateLocked(childSessions); - } catch (StreamingException e) { - Slog.e(TAG, "Streaming failed", e); + sealLocked(childSessions); } catch (PackageManagerException e) { Slog.e(TAG, "Package not valid", e); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 66a2b013e299..2a249d2c92ec 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -74,6 +74,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManager.EnforcingUser; +import android.os.UserManager.QuietModeFlag; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; import android.os.storage.StorageManager; @@ -914,7 +915,7 @@ public class UserManagerService extends IUserManager.Stub { @Override public boolean requestQuietModeEnabled(@NonNull String callingPackage, boolean enableQuietMode, - @UserIdInt int userId, @Nullable IntentSender target) { + @UserIdInt int userId, @Nullable IntentSender target, @QuietModeFlag int flags) { Objects.requireNonNull(callingPackage); if (enableQuietMode && target != null) { @@ -925,24 +926,24 @@ public class UserManagerService extends IUserManager.Stub { ensureCanModifyQuietMode(callingPackage, Binder.getCallingUid(), userId, target != null); final long identity = Binder.clearCallingIdentity(); try { - boolean result = false; if (enableQuietMode) { setQuietModeEnabled( userId, true /* enableQuietMode */, target, callingPackage); - result = true; - } else { - boolean needToShowConfirmCredential = - mLockPatternUtils.isSecure(userId) - && !StorageManager.isUserKeyUnlocked(userId); - if (needToShowConfirmCredential) { - showConfirmCredentialToDisableQuietMode(userId, target); - } else { - setQuietModeEnabled( - userId, false /* enableQuietMode */, target, callingPackage); - result = true; + return true; + } + boolean needToShowConfirmCredential = + mLockPatternUtils.isSecure(userId) + && !StorageManager.isUserKeyUnlocked(userId); + if (needToShowConfirmCredential) { + if ((flags & UserManager.QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED) != 0) { + return false; } + showConfirmCredentialToDisableQuietMode(userId, target); + return false; } - return result; + setQuietModeEnabled( + userId, false /* enableQuietMode */, target, callingPackage); + return true; } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 7fc9fdc0180d..8460ede91fd0 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -678,9 +678,20 @@ public class Notifier { final int powerState; synchronized (mLock) { if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) { - // Broadcasted power state is unknown. Send wake up. - mPendingWakeUpBroadcast = false; - mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE; + // Broadcasted power state is unknown. + // Send wake up or go to sleep. + switch (mPendingInteractiveState) { + case INTERACTIVE_STATE_ASLEEP: + mPendingGoToSleepBroadcast = false; + mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP; + break; + + case INTERACTIVE_STATE_AWAKE: + default: + mPendingWakeUpBroadcast = false; + mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE; + break; + } } else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) { // Broadcasted power state is awake. Send asleep if needed. if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 93d50b846bc1..c1b71aab38fd 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -346,7 +346,8 @@ public final class PowerManagerService extends SystemService // True if systemReady() has been called. private boolean mSystemReady; - // True if boot completed occurred. We keep the screen on until this happens. + // True if boot completed occurred. We keep the screen on until this happens. + // The screen will be off if we are in quiescent mode. private boolean mBootCompleted; // True if auto-suspend mode is enabled. @@ -864,6 +865,12 @@ public final class PowerManagerService extends SystemService mBatterySaverStateMachine.onBootCompleted(); userActivityNoUpdateLocked( now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); + + if (sQuiescent) { + goToSleepNoUpdateLocked(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_QUIESCENT, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); + } updatePowerStateLocked(); } } @@ -1430,8 +1437,7 @@ public final class PowerManagerService extends SystemService + ", uid=" + uid); } - if (eventTime < mLastSleepTime || eventTime < mLastWakeTime - || !mBootCompleted || !mSystemReady) { + if (eventTime < mLastSleepTime || eventTime < mLastWakeTime || !mSystemReady) { return false; } @@ -1508,7 +1514,7 @@ public final class PowerManagerService extends SystemService } if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE - || !mBootCompleted || !mSystemReady || mForceSuspendActive) { + || mForceSuspendActive || !mSystemReady) { return false; } @@ -1530,6 +1536,10 @@ public final class PowerManagerService extends SystemService mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid); userActivityNoUpdateLocked( eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid); + + if (sQuiescent) { + mDirty |= DIRTY_QUIESCENT; + } } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } @@ -1561,7 +1571,8 @@ public final class PowerManagerService extends SystemService if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP || mWakefulness == WAKEFULNESS_DOZING - || !mBootCompleted || !mSystemReady) { + || !mSystemReady + || !mBootCompleted) { return false; } @@ -2645,6 +2656,10 @@ public final class PowerManagerService extends SystemService | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED | DIRTY_QUIESCENT)) != 0) { + if ((dirty & DIRTY_QUIESCENT) != 0) { + sQuiescent = false; + } + mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked(); // Determine appropriate screen brightness and auto-brightness adjustments. @@ -2694,9 +2709,6 @@ public final class PowerManagerService extends SystemService mRequestWaitForNegativeProximity); mRequestWaitForNegativeProximity = false; - if ((dirty & DIRTY_QUIESCENT) != 0) { - sQuiescent = false; - } if (DEBUG_SPEW) { Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady + ", policy=" + mDisplayPowerRequest.policy diff --git a/services/core/java/com/android/server/stats/StatsPullAtomService.java b/services/core/java/com/android/server/stats/StatsPullAtomService.java deleted file mode 100644 index f78330e9e7d2..000000000000 --- a/services/core/java/com/android/server/stats/StatsPullAtomService.java +++ /dev/null @@ -1,1446 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.stats; - -import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED; -import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; -import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; -import static android.os.Debug.getIonHeapsSizeKb; -import static android.os.Process.THREAD_PRIORITY_BACKGROUND; -import static android.os.Process.getUidForPid; -import static android.os.storage.VolumeInfo.TYPE_PRIVATE; -import static android.os.storage.VolumeInfo.TYPE_PUBLIC; - -import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; -import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs; -import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs; -import static com.android.server.stats.ProcfsMemoryUtil.forEachPid; -import static com.android.server.stats.ProcfsMemoryUtil.readCmdlineFromProcfs; -import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.ActivityManagerInternal; -import android.app.AlarmManager; -import android.app.AlarmManager.OnAlarmListener; -import android.app.AppOpsManager; -import android.app.AppOpsManager.HistoricalOps; -import android.app.AppOpsManager.HistoricalOpsRequest; -import android.app.AppOpsManager.HistoricalPackageOps; -import android.app.AppOpsManager.HistoricalUidOps; -import android.app.INotificationManager; -import android.app.ProcessMemoryState; -import android.app.StatsManager; -import android.app.StatsManager.PullAtomMetadata; -import android.bluetooth.BluetoothActivityEnergyInfo; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.UidTraffic; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PermissionInfo; -import android.content.pm.UserInfo; -import android.hardware.biometrics.BiometricsProtoEnums; -import android.hardware.face.FaceManager; -import android.hardware.fingerprint.FingerprintManager; -import android.net.ConnectivityManager; -import android.net.INetworkStatsService; -import android.net.Network; -import android.net.NetworkRequest; -import android.net.NetworkStats; -import android.net.wifi.WifiManager; -import android.os.BatteryStats; -import android.os.BatteryStatsInternal; -import android.os.Binder; -import android.os.Build; -import android.os.Bundle; -import android.os.CoolingDevice; -import android.os.Environment; -import android.os.FileUtils; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.IPullAtomCallback; -import android.os.IStatsCompanionService; -import android.os.IStatsd; -import android.os.IStoraged; -import android.os.IThermalEventListener; -import android.os.IThermalService; -import android.os.Looper; -import android.os.ParcelFileDescriptor; -import android.os.Parcelable; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.StatFs; -import android.os.StatsLogEventWrapper; -import android.os.SynchronousResultReceiver; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.Temperature; -import android.os.UserHandle; -import android.os.UserManager; -import android.os.connectivity.WifiActivityEnergyInfo; -import android.os.storage.DiskInfo; -import android.os.storage.StorageManager; -import android.os.storage.VolumeInfo; -import android.provider.Settings; -import android.stats.storage.StorageEnums; -import android.telephony.ModemActivityInfo; -import android.telephony.TelephonyManager; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; -import android.util.Slog; -import android.util.StatsEvent; -import android.util.StatsLog; -import android.util.proto.ProtoOutputStream; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.app.procstats.IProcessStats; -import com.android.internal.app.procstats.ProcessStats; -import com.android.internal.os.BackgroundThread; -import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; -import com.android.internal.os.BinderCallsStats.ExportedCallStat; -import com.android.internal.os.KernelCpuSpeedReader; -import com.android.internal.os.KernelCpuThreadReader; -import com.android.internal.os.KernelCpuThreadReaderDiff; -import com.android.internal.os.KernelCpuThreadReaderSettingsObserver; -import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader; -import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader; -import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; -import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; -import com.android.internal.os.KernelWakelockReader; -import com.android.internal.os.KernelWakelockStats; -import com.android.internal.os.LooperStats; -import com.android.internal.os.PowerProfile; -import com.android.internal.os.ProcessCpuTracker; -import com.android.internal.os.StoragedUidIoStatsReader; -import com.android.internal.util.DumpUtils; -import com.android.server.BinderCallsStatsService; -import com.android.server.LocalServices; -import com.android.server.SystemService; -import com.android.server.SystemServiceManager; -import com.android.server.am.MemoryStatUtil.MemoryStat; -import com.android.server.notification.NotificationManagerService; -import com.android.server.role.RoleManagerInternal; -import com.android.server.stats.IonMemoryUtil.IonAllocations; -import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot; -import com.android.server.storage.DiskStatsFileLogger; -import com.android.server.storage.DiskStatsLoggingService; - -import com.google.android.collect.Sets; - -import libcore.io.IoUtils; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * SystemService containing PullAtomCallbacks that are registered with statsd. - * - * @hide - */ -public class StatsPullAtomService extends SystemService { - private static final String TAG = "StatsPullAtomService"; - private static final boolean DEBUG = true; - - private static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity"; - /** - * How long to wait on an individual subsystem to return its stats. - */ - private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000; - - private final Object mNetworkStatsLock = new Object(); - @GuardedBy("mNetworkStatsLock") - private INetworkStatsService mNetworkStatsService; - private final Object mThermalLock = new Object(); - @GuardedBy("mThermalLock") - private IThermalService mThermalService; - - private final Context mContext; - private StatsManager mStatsManager; - - public StatsPullAtomService(Context context) { - super(context); - mContext = context; - } - - @Override - public void onStart() { - mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER); - mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); - mTelephony = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); - - // Used to initialize the CPU Frequency atom. - PowerProfile powerProfile = new PowerProfile(mContext); - final int numClusters = powerProfile.getNumCpuClusters(); - mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters]; - int firstCpuOfCluster = 0; - for (int i = 0; i < numClusters; i++) { - final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i); - mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster, - numSpeedSteps); - firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i); - } - } - - @Override - public void onBootPhase(int phase) { - super.onBootPhase(phase); - if (phase == PHASE_SYSTEM_SERVICES_READY) { - BackgroundThread.getHandler().post(() -> { - registerAllPullers(); - }); - } - } - - void registerAllPullers() { - if (DEBUG) { - Slog.d(TAG, "Registering all pullers with statsd"); - } - registerWifiBytesTransfer(); - registerWifiBytesTransferBackground(); - registerMobileBytesTransfer(); - registerMobileBytesTransferBackground(); - registerBluetoothBytesTransfer(); - registerKernelWakelock(); - registerCpuTimePerFreq(); - registerCpuTimePerUid(); - registerCpuTimePerUidFreq(); - registerCpuActiveTime(); - registerCpuClusterTime(); - registerWifiActivityInfo(); - registerModemActivityInfo(); - registerBluetoothActivityInfo(); - registerSystemElapsedRealtime(); - registerSystemUptime(); - registerRemainingBatteryCapacity(); - registerFullBatteryCapacity(); - registerBatteryVoltage(); - registerBatteryLevel(); - registerBatteryCycleCount(); - registerProcessMemoryState(); - registerProcessMemoryHighWaterMark(); - registerProcessMemorySnapshot(); - registerSystemIonHeapSize(); - registerIonHeapSize(); - registerProcessSystemIonHeapSize(); - registerTemperature(); - registerCoolingDevice(); - registerBinderCalls(); - registerBinderCallsExceptions(); - registerLooperStats(); - registerDiskStats(); - registerDirectoryUsage(); - registerAppSize(); - registerCategorySize(); - registerNumFingerprintsEnrolled(); - registerNumFacesEnrolled(); - registerProcStats(); - registerProcStatsPkgProc(); - registerDiskIO(); - registerPowerProfile(); - registerProcessCpuTime(); - registerCpuTimePerThreadFreq(); - registerDeviceCalculatedPowerUse(); - registerDeviceCalculatedPowerBlameUid(); - registerDeviceCalculatedPowerBlameOther(); - registerDebugElapsedClock(); - registerDebugFailingElapsedClock(); - registerBuildInformation(); - registerRoleHolder(); - registerDangerousPermissionState(); - registerTimeZoneDataInfo(); - registerExternalStorageInfo(); - registerAppsOnExternalStorageInfo(); - registerFaceSettings(); - registerAppOps(); - registerNotificationRemoteViews(); - registerDangerousPermissionState(); - registerDangerousPermissionStateSampled(); - } - - private INetworkStatsService getINetworkStatsService() { - synchronized (mNetworkStatsLock) { - if (mNetworkStatsService == null) { - mNetworkStatsService = INetworkStatsService.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); - if (mNetworkStatsService != null) { - try { - mNetworkStatsService.asBinder().linkToDeath(() -> { - synchronized (mNetworkStatsLock) { - mNetworkStatsService = null; - } - }, /* flags */ 0); - } catch (RemoteException e) { - Slog.e(TAG, "linkToDeath with NetworkStatsService failed", e); - mNetworkStatsService = null; - } - } - - } - return mNetworkStatsService; - } - } - - private IThermalService getIThermalService() { - synchronized (mThermalLock) { - if (mThermalService == null) { - mThermalService = IThermalService.Stub.asInterface( - ServiceManager.getService(Context.THERMAL_SERVICE)); - if (mThermalService != null) { - try { - mThermalService.asBinder().linkToDeath(() -> { - synchronized (mThermalLock) { - mThermalService = null; - } - }, /* flags */ 0); - } catch (RemoteException e) { - Slog.e(TAG, "linkToDeath with thermalService failed", e); - mThermalService = null; - } - } - } - return mThermalService; - } - } - private void registerWifiBytesTransfer() { - int tagId = StatsLog.WIFI_BYTES_TRANSFER; - PullAtomMetadata metadata = PullAtomMetadata.newBuilder() - .setAdditiveFields(new int[] {2, 3, 4, 5}) - .build(); - mStatsManager.registerPullAtomCallback( - tagId, - metadata, - (atomTag, data) -> pullWifiBytesTransfer(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullWifiBytesTransfer(int atomTag, List<StatsEvent> pulledData) { - INetworkStatsService networkStatsService = getINetworkStatsService(); - if (networkStatsService == null) { - Slog.e(TAG, "NetworkStats Service is not available!"); - return StatsManager.PULL_SKIP; - } - long token = Binder.clearCallingIdentity(); - try { - // TODO: Consider caching the following call to get BatteryStatsInternal. - BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); - String[] ifaces = bs.getWifiIfaces(); - if (ifaces.length == 0) { - return StatsManager.PULL_SKIP; - } - // Combine all the metrics per Uid into one record. - NetworkStats stats = networkStatsService.getDetailedUidStats(ifaces).groupedByUid(); - addNetworkStats(atomTag, pulledData, stats, false); - } catch (RemoteException e) { - Slog.e(TAG, "Pulling netstats for wifi bytes has error", e); - return StatsManager.PULL_SKIP; - } finally { - Binder.restoreCallingIdentity(token); - } - return StatsManager.PULL_SUCCESS; - } - - private void addNetworkStats( - int tag, List<StatsEvent> ret, NetworkStats stats, boolean withFGBG) { - int size = stats.size(); - NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling - for (int j = 0; j < size; j++) { - stats.getValues(j, entry); - StatsEvent.Builder e = StatsEvent.newBuilder(); - e.setAtomId(tag); - e.writeInt(entry.uid); - if (withFGBG) { - e.writeInt(entry.set); - } - e.writeLong(entry.rxBytes); - e.writeLong(entry.rxPackets); - e.writeLong(entry.txBytes); - e.writeLong(entry.txPackets); - ret.add(e.build()); - } - } - - /** - * Allows rollups per UID but keeping the set (foreground/background) slicing. - * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java - */ - private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) { - final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1); - - final NetworkStats.Entry entry = new NetworkStats.Entry(); - entry.iface = NetworkStats.IFACE_ALL; - entry.tag = NetworkStats.TAG_NONE; - entry.metered = NetworkStats.METERED_ALL; - entry.roaming = NetworkStats.ROAMING_ALL; - - int size = stats.size(); - NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values - for (int i = 0; i < size; i++) { - stats.getValues(i, recycle); - - // Skip specific tags, since already counted in TAG_NONE - if (recycle.tag != NetworkStats.TAG_NONE) continue; - - entry.set = recycle.set; // Allows slicing by background/foreground - entry.uid = recycle.uid; - entry.rxBytes = recycle.rxBytes; - entry.rxPackets = recycle.rxPackets; - entry.txBytes = recycle.txBytes; - entry.txPackets = recycle.txPackets; - // Operations purposefully omitted since we don't use them for statsd. - ret.combineValues(entry); - } - return ret; - } - - private void registerWifiBytesTransferBackground() { - int tagId = StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG; - PullAtomMetadata metadata = PullAtomMetadata.newBuilder() - .setAdditiveFields(new int[] {3, 4, 5, 6}) - .build(); - mStatsManager.registerPullAtomCallback( - tagId, - metadata, - (atomTag, data) -> pullWifiBytesTransferBackground(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullWifiBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) { - INetworkStatsService networkStatsService = getINetworkStatsService(); - if (networkStatsService == null) { - Slog.e(TAG, "NetworkStats Service is not available!"); - return StatsManager.PULL_SKIP; - } - long token = Binder.clearCallingIdentity(); - try { - BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); - String[] ifaces = bs.getWifiIfaces(); - if (ifaces.length == 0) { - return StatsManager.PULL_SKIP; - } - NetworkStats stats = rollupNetworkStatsByFGBG( - networkStatsService.getDetailedUidStats(ifaces)); - addNetworkStats(atomTag, pulledData, stats, true); - } catch (RemoteException e) { - Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e); - return StatsManager.PULL_SKIP; - } finally { - Binder.restoreCallingIdentity(token); - } - return StatsManager.PULL_SUCCESS; - } - - private void registerMobileBytesTransfer() { - int tagId = StatsLog.MOBILE_BYTES_TRANSFER; - PullAtomMetadata metadata = PullAtomMetadata.newBuilder() - .setAdditiveFields(new int[] {2, 3, 4, 5}) - .build(); - mStatsManager.registerPullAtomCallback( - tagId, - metadata, - (atomTag, data) -> pullMobileBytesTransfer(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullMobileBytesTransfer(int atomTag, List<StatsEvent> pulledData) { - INetworkStatsService networkStatsService = getINetworkStatsService(); - if (networkStatsService == null) { - Slog.e(TAG, "NetworkStats Service is not available!"); - return StatsManager.PULL_SKIP; - } - long token = Binder.clearCallingIdentity(); - try { - BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); - String[] ifaces = bs.getMobileIfaces(); - if (ifaces.length == 0) { - return StatsManager.PULL_SKIP; - } - // Combine all the metrics per Uid into one record. - NetworkStats stats = networkStatsService.getDetailedUidStats(ifaces).groupedByUid(); - addNetworkStats(atomTag, pulledData, stats, false); - } catch (RemoteException e) { - Slog.e(TAG, "Pulling netstats for mobile bytes has error", e); - return StatsManager.PULL_SKIP; - } finally { - Binder.restoreCallingIdentity(token); - } - return StatsManager.PULL_SUCCESS; - } - - private void registerMobileBytesTransferBackground() { - int tagId = StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG; - PullAtomMetadata metadata = PullAtomMetadata.newBuilder() - .setAdditiveFields(new int[] {3, 4, 5, 6}) - .build(); - mStatsManager.registerPullAtomCallback( - tagId, - metadata, - (atomTag, data) -> pullMobileBytesTransferBackground(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullMobileBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) { - INetworkStatsService networkStatsService = getINetworkStatsService(); - if (networkStatsService == null) { - Slog.e(TAG, "NetworkStats Service is not available!"); - return StatsManager.PULL_SKIP; - } - long token = Binder.clearCallingIdentity(); - try { - BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); - String[] ifaces = bs.getMobileIfaces(); - if (ifaces.length == 0) { - return StatsManager.PULL_SKIP; - } - NetworkStats stats = rollupNetworkStatsByFGBG( - networkStatsService.getDetailedUidStats(ifaces)); - addNetworkStats(atomTag, pulledData, stats, true); - } catch (RemoteException e) { - Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e); - return StatsManager.PULL_SKIP; - } finally { - Binder.restoreCallingIdentity(token); - } - return StatsManager.PULL_SUCCESS; - } - - private void registerBluetoothBytesTransfer() { - int tagId = StatsLog.BLUETOOTH_BYTES_TRANSFER; - PullAtomMetadata metadata = PullAtomMetadata.newBuilder() - .setAdditiveFields(new int[] {2, 3}) - .build(); - mStatsManager.registerPullAtomCallback( - tagId, - metadata, - (atomTag, data) -> pullBluetoothBytesTransfer(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - /** - * Helper method to extract the Parcelable controller info from a - * SynchronousResultReceiver. - */ - private static <T extends Parcelable> T awaitControllerInfo( - @Nullable SynchronousResultReceiver receiver) { - if (receiver == null) { - return null; - } - - try { - final SynchronousResultReceiver.Result result = - receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS); - if (result.bundle != null) { - // This is the final destination for the Bundle. - result.bundle.setDefusable(true); - - final T data = result.bundle.getParcelable(RESULT_RECEIVER_CONTROLLER_KEY); - if (data != null) { - return data; - } - } - Slog.e(TAG, "no controller energy info supplied for " + receiver.getName()); - } catch (TimeoutException e) { - Slog.w(TAG, "timeout reading " + receiver.getName() + " stats"); - } - return null; - } - - private synchronized BluetoothActivityEnergyInfo fetchBluetoothData() { - // TODO: Investigate whether the synchronized keyword is needed. - final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null) { - SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver( - "bluetooth"); - adapter.requestControllerActivityEnergyInfo(bluetoothReceiver); - return awaitControllerInfo(bluetoothReceiver); - } else { - Slog.e(TAG, "Failed to get bluetooth adapter!"); - return null; - } - } - - private int pullBluetoothBytesTransfer(int atomTag, List<StatsEvent> pulledData) { - BluetoothActivityEnergyInfo info = fetchBluetoothData(); - if (info == null || info.getUidTraffic() == null) { - return StatsManager.PULL_SKIP; - } - for (UidTraffic traffic : info.getUidTraffic()) { - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeInt(traffic.getUid()) - .writeLong(traffic.getRxBytes()) - .writeLong(traffic.getTxBytes()) - .build(); - pulledData.add(e); - } - return StatsManager.PULL_SUCCESS; - } - - private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); - private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); - - private void registerKernelWakelock() { - int tagId = StatsLog.KERNEL_WAKELOCK; - mStatsManager.registerPullAtomCallback( - tagId, - /* PullAtomMetadata */ null, - (atomTag, data) -> pullKernelWakelock(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullKernelWakelock(int atomTag, List<StatsEvent> pulledData) { - final KernelWakelockStats wakelockStats = - mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats); - for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) { - String name = ent.getKey(); - KernelWakelockStats.Entry kws = ent.getValue(); - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeString(name) - .writeInt(kws.mCount) - .writeInt(kws.mVersion) - .writeLong(kws.mTotalTime) - .build(); - pulledData.add(e); - } - return StatsManager.PULL_SUCCESS; - } - - private KernelCpuSpeedReader[] mKernelCpuSpeedReaders; - // Disables throttler on CPU time readers. - private KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader = - new KernelCpuUidUserSysTimeReader(false); - private KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader = - new KernelCpuUidFreqTimeReader(false); - private KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader = - new KernelCpuUidActiveTimeReader(false); - private KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader = - new KernelCpuUidClusterTimeReader(false); - - private void registerCpuTimePerFreq() { - int tagId = StatsLog.CPU_TIME_PER_FREQ; - PullAtomMetadata metadata = PullAtomMetadata.newBuilder() - .setAdditiveFields(new int[] {3}) - .build(); - mStatsManager.registerPullAtomCallback( - tagId, - metadata, - (atomTag, data) -> pullCpuTimePerFreq(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullCpuTimePerFreq(int atomTag, List<StatsEvent> pulledData) { - for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) { - long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute(); - if (clusterTimeMs != null) { - for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) { - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeInt(cluster) - .writeInt(speed) - .writeLong(clusterTimeMs[speed]) - .build(); - pulledData.add(e); - } - } - } - return StatsManager.PULL_SUCCESS; - } - - private void registerCpuTimePerUid() { - int tagId = StatsLog.CPU_TIME_PER_UID; - PullAtomMetadata metadata = PullAtomMetadata.newBuilder() - .setAdditiveFields(new int[] {2, 3}) - .build(); - mStatsManager.registerPullAtomCallback( - tagId, - metadata, - (atomTag, data) -> pullCpuTimePerUid(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullCpuTimePerUid(int atomTag, List<StatsEvent> pulledData) { - mCpuUidUserSysTimeReader.readAbsolute((uid, timesUs) -> { - long userTimeUs = timesUs[0], systemTimeUs = timesUs[1]; - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeInt(uid) - .writeLong(userTimeUs) - .writeLong(systemTimeUs) - .build(); - pulledData.add(e); - }); - return StatsManager.PULL_SUCCESS; - } - - private void registerCpuTimePerUidFreq() { - // the throttling is 3sec, handled in - // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader - int tagId = StatsLog.CPU_TIME_PER_UID_FREQ; - PullAtomMetadata metadata = PullAtomMetadata.newBuilder() - .setAdditiveFields(new int[] {4}) - .build(); - mStatsManager.registerPullAtomCallback( - tagId, - metadata, - (atomTag, data) -> pullCpuTimeperUidFreq(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullCpuTimeperUidFreq(int atomTag, List<StatsEvent> pulledData) { - mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> { - for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { - if (cpuFreqTimeMs[freqIndex] != 0) { - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeInt(uid) - .writeInt(freqIndex) - .writeLong(cpuFreqTimeMs[freqIndex]) - .build(); - pulledData.add(e); - } - } - }); - return StatsManager.PULL_SUCCESS; - } - - private void registerCpuActiveTime() { - // the throttling is 3sec, handled in - // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader - int tagId = StatsLog.CPU_ACTIVE_TIME; - PullAtomMetadata metadata = PullAtomMetadata.newBuilder() - .setAdditiveFields(new int[] {2}) - .build(); - mStatsManager.registerPullAtomCallback( - tagId, - metadata, - (atomTag, data) -> pullCpuActiveTime(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullCpuActiveTime(int atomTag, List<StatsEvent> pulledData) { - mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> { - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeInt(uid) - .writeLong(cpuActiveTimesMs) - .build(); - pulledData.add(e); - }); - return StatsManager.PULL_SUCCESS; - } - - private void registerCpuClusterTime() { - // the throttling is 3sec, handled in - // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader - int tagId = StatsLog.CPU_CLUSTER_TIME; - PullAtomMetadata metadata = PullAtomMetadata.newBuilder() - .setAdditiveFields(new int[] {3}) - .build(); - mStatsManager.registerPullAtomCallback( - tagId, - metadata, - (atomTag, data) -> pullCpuClusterTime(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullCpuClusterTime(int atomTag, List<StatsEvent> pulledData) { - mCpuUidClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> { - for (int i = 0; i < cpuClusterTimesMs.length; i++) { - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeInt(uid) - .writeInt(i) - .writeLong(cpuClusterTimesMs[i]) - .build(); - pulledData.add(e); - } - }); - return StatsManager.PULL_SUCCESS; - } - - private void registerWifiActivityInfo() { - int tagId = StatsLog.WIFI_ACTIVITY_INFO; - mStatsManager.registerPullAtomCallback( - tagId, - null, // use default PullAtomMetadata values - (atomTag, data) -> pullWifiActivityInfo(atomTag, data), - BackgroundThread.getExecutor() - ); - } - - private WifiManager mWifiManager; - private TelephonyManager mTelephony; - - private int pullWifiActivityInfo(int atomTag, List<StatsEvent> pulledData) { - long token = Binder.clearCallingIdentity(); - try { - SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi"); - mWifiManager.getWifiActivityEnergyInfoAsync( - new Executor() { - @Override - public void execute(Runnable runnable) { - // run the listener on the binder thread, if it was run on the main - // thread it would deadlock since we would be waiting on ourselves - runnable.run(); - } - }, - info -> { - Bundle bundle = new Bundle(); - bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info); - wifiReceiver.send(0, bundle); - } - ); - final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver); - if (wifiInfo == null) { - return StatsManager.PULL_SKIP; - } - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeLong(wifiInfo.getTimeSinceBootMillis()) - .writeInt(wifiInfo.getStackState()) - .writeLong(wifiInfo.getControllerTxDurationMillis()) - .writeLong(wifiInfo.getControllerRxDurationMillis()) - .writeLong(wifiInfo.getControllerIdleDurationMillis()) - .writeLong(wifiInfo.getControllerEnergyUsedMicroJoules()) - .build(); - pulledData.add(e); - } catch (RuntimeException e) { - Slog.e(TAG, "failed to getWifiActivityEnergyInfoAsync", e); - return StatsManager.PULL_SKIP; - } finally { - Binder.restoreCallingIdentity(token); - } - return StatsManager.PULL_SUCCESS; - } - - private void registerModemActivityInfo() { - int tagId = StatsLog.MODEM_ACTIVITY_INFO; - mStatsManager.registerPullAtomCallback( - tagId, - null, // use default PullAtomMetadata values - (atomTag, data) -> pullModemActivityInfo(atomTag, data), - BackgroundThread.getExecutor() - ); - } - - private int pullModemActivityInfo(int atomTag, List<StatsEvent> pulledData) { - long token = Binder.clearCallingIdentity(); - try { - SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony"); - mTelephony.requestModemActivityInfo(modemReceiver); - final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver); - if (modemInfo == null) { - return StatsManager.PULL_SKIP; - } - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeLong(modemInfo.getTimestamp()) - .writeLong(modemInfo.getSleepTimeMillis()) - .writeLong(modemInfo.getIdleTimeMillis()) - .writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis()) - .writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis()) - .writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis()) - .writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis()) - .writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis()) - .writeLong(modemInfo.getReceiveTimeMillis()) - .build(); - pulledData.add(e); - } finally { - Binder.restoreCallingIdentity(token); - } - return StatsManager.PULL_SUCCESS; - } - - private void registerBluetoothActivityInfo() { - int tagId = StatsLog.BLUETOOTH_ACTIVITY_INFO; - mStatsManager.registerPullAtomCallback( - tagId, - /* metadata */ null, - (atomTag, data) -> pullBluetoothActivityInfo(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullBluetoothActivityInfo(int atomTag, List<StatsEvent> pulledData) { - BluetoothActivityEnergyInfo info = fetchBluetoothData(); - if (info == null) { - return StatsManager.PULL_SKIP; - } - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeLong(info.getTimeStamp()) - .writeInt(info.getBluetoothStackState()) - .writeLong(info.getControllerTxTimeMillis()) - .writeLong(info.getControllerRxTimeMillis()) - .writeLong(info.getControllerIdleTimeMillis()) - .writeLong(info.getControllerEnergyUsed()) - .build(); - pulledData.add(e); - return StatsManager.PULL_SUCCESS; - } - - private void registerSystemElapsedRealtime() { - // No op. - } - - private void pullSystemElapsedRealtime() { - // No op. - } - - private void registerSystemUptime() { - int tagId = StatsLog.SYSTEM_UPTIME; - mStatsManager.registerPullAtomCallback( - tagId, - null, // use default PullAtomMetadata values - (atomTag, data) -> pullSystemUptime(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullSystemUptime(int atomTag, List<StatsEvent> pulledData) { - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeLong(SystemClock.elapsedRealtime()) - .build(); - pulledData.add(e); - return StatsManager.PULL_SUCCESS; - } - - private void registerRemainingBatteryCapacity() { - // No op. - } - - private void pullRemainingBatteryCapacity() { - // No op. - } - - private void registerFullBatteryCapacity() { - // No op. - } - - private void pullFullBatteryCapacity() { - // No op. - } - - private void registerBatteryVoltage() { - // No op. - } - - private void pullBatteryVoltage() { - // No op. - } - - private void registerBatteryLevel() { - // No op. - } - - private void pullBatteryLevel() { - // No op. - } - - private void registerBatteryCycleCount() { - // No op. - } - - private void pullBatteryCycleCount() { - // No op. - } - - private void registerProcessMemoryState() { - // No op. - } - - private void pullProcessMemoryState() { - // No op. - } - - private void registerProcessMemoryHighWaterMark() { - // No op. - } - - private void pullProcessMemoryHighWaterMark() { - // No op. - } - - private void registerProcessMemorySnapshot() { - // No op. - } - - private void pullProcessMemorySnapshot() { - // No op. - } - - private void registerSystemIonHeapSize() { - // No op. - } - - private void pullSystemIonHeapSize() { - // No op. - } - - private void registerIonHeapSize() { - int tagId = StatsLog.ION_HEAP_SIZE; - mStatsManager.registerPullAtomCallback( - tagId, - /* PullAtomMetadata */ null, - (atomTag, data) -> pullIonHeapSize(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullIonHeapSize(int atomTag, List<StatsEvent> pulledData) { - int ionHeapSizeInKilobytes = (int) getIonHeapsSizeKb(); - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeInt(ionHeapSizeInKilobytes) - .build(); - pulledData.add(e); - return StatsManager.PULL_SUCCESS; - } - - private void registerProcessSystemIonHeapSize() { - // No op. - } - - private void pullProcessSystemIonHeapSize() { - // No op. - } - - private void registerTemperature() { - // No op. - } - - private void pullTemperature() { - // No op. - } - - private void registerCoolingDevice() { - // No op. - } - - private void pullCooldownDevice() { - // No op. - } - - private void registerBinderCalls() { - // No op. - } - - private void pullBinderCalls() { - // No op. - } - - private void registerBinderCallsExceptions() { - // No op. - } - - private void pullBinderCallsExceptions() { - // No op. - } - - private void registerLooperStats() { - // No op. - } - - private void pullLooperStats() { - // No op. - } - - private void registerDiskStats() { - // No op. - } - - private void pullDiskStats() { - // No op. - } - - private void registerDirectoryUsage() { - // No op. - } - - private void pullDirectoryUsage() { - // No op. - } - - private void registerAppSize() { - // No op. - } - - private void pullAppSize() { - // No op. - } - - private void registerCategorySize() { - // No op. - } - - private void pullCategorySize() { - // No op. - } - - private void registerNumFingerprintsEnrolled() { - // No op. - } - - private void pullNumFingerprintsEnrolled() { - // No op. - } - - private void registerNumFacesEnrolled() { - // No op. - } - - private void pullNumFacesEnrolled() { - // No op. - } - - private void registerProcStats() { - // No op. - } - - private void pullProcStats() { - // No op. - } - - private void registerProcStatsPkgProc() { - // No op. - } - - private void pullProcStatsPkgProc() { - // No op. - } - - private void registerDiskIO() { - // No op. - } - - private void pullDiskIO() { - // No op. - } - - private void registerPowerProfile() { - int tagId = StatsLog.POWER_PROFILE; - mStatsManager.registerPullAtomCallback( - tagId, - /* PullAtomMetadata */ null, - (atomTag, data) -> pullPowerProfile(atomTag, data), - Executors.newSingleThreadExecutor() - ); - } - - private int pullPowerProfile(int atomTag, List<StatsEvent> pulledData) { - PowerProfile powerProfile = new PowerProfile(mContext); - ProtoOutputStream proto = new ProtoOutputStream(); - powerProfile.dumpDebug(proto); - proto.flush(); - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeByteArray(proto.getBytes()) - .build(); - pulledData.add(e); - return StatsManager.PULL_SUCCESS; - } - - private void registerProcessCpuTime() { - // No op. - } - - private void pullProcessCpuTime() { - // No op. - } - - private void registerCpuTimePerThreadFreq() { - // No op. - } - - private void pullCpuTimePerThreadFreq() { - // No op. - } - - // TODO: move to top of file when all migrations are complete - private BatteryStatsHelper mBatteryStatsHelper = null; - private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000; - private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS; - private static final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L; - - private BatteryStatsHelper getBatteryStatsHelper() { - if (mBatteryStatsHelper == null) { - final long callingToken = Binder.clearCallingIdentity(); - try { - // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly(). - mBatteryStatsHelper = new BatteryStatsHelper(mContext, false); - } finally { - Binder.restoreCallingIdentity(callingToken); - } - mBatteryStatsHelper.create((Bundle) null); - } - long currentTime = SystemClock.elapsedRealtime(); - if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) { - // Load BatteryStats and do all the calculations. - mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL); - // Calculations are done so we don't need to save the raw BatteryStats data in RAM. - mBatteryStatsHelper.clearStats(); - mBatteryStatsHelperTimestampMs = currentTime; - } - return mBatteryStatsHelper; - } - - private long milliAmpHrsToNanoAmpSecs(double mAh) { - return (long) (mAh * MILLI_AMP_HR_TO_NANO_AMP_SECS + 0.5); - } - - private void registerDeviceCalculatedPowerUse() { - int tagId = StatsLog.DEVICE_CALCULATED_POWER_USE; - mStatsManager.registerPullAtomCallback( - tagId, - null, // use default PullAtomMetadata values - (atomTag, data) -> pullDeviceCalculatedPowerUse(atomTag, data), - BackgroundThread.getExecutor() - ); - } - - private int pullDeviceCalculatedPowerUse(int atomTag, List<StatsEvent> pulledData) { - BatteryStatsHelper bsHelper = getBatteryStatsHelper(); - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower())) - .build(); - pulledData.add(e); - return StatsManager.PULL_SUCCESS; - } - - private void registerDeviceCalculatedPowerBlameUid() { - int tagId = StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID; - mStatsManager.registerPullAtomCallback( - tagId, - null, // use default PullAtomMetadata values - (atomTag, data) -> pullDeviceCalculatedPowerBlameUid(atomTag, data), - BackgroundThread.getExecutor() - ); - } - - private int pullDeviceCalculatedPowerBlameUid(int atomTag, List<StatsEvent> pulledData) { - final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList(); - if (sippers == null) { - return StatsManager.PULL_SKIP; - } - - for (BatterySipper bs : sippers) { - if (bs.drainType != bs.drainType.APP) { - continue; - } - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeInt(bs.uidObj.getUid()) - .writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah)) - .build(); - pulledData.add(e); - } - return StatsManager.PULL_SUCCESS; - } - - private void registerDeviceCalculatedPowerBlameOther() { - int tagId = StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER; - mStatsManager.registerPullAtomCallback( - tagId, - null, // use default PullAtomMetadata values - (atomTag, data) -> pullDeviceCalculatedPowerBlameOther(atomTag, data), - BackgroundThread.getExecutor() - ); - } - - private int pullDeviceCalculatedPowerBlameOther(int atomTag, List<StatsEvent> pulledData) { - final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList(); - if (sippers == null) { - return StatsManager.PULL_SKIP; - } - - for (BatterySipper bs : sippers) { - if (bs.drainType == bs.drainType.APP) { - continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid(). - } - if (bs.drainType == bs.drainType.USER) { - continue; // This is not supported. We purposefully calculate over USER_ALL. - } - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeInt(bs.drainType.ordinal()) - .writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah)) - .build(); - pulledData.add(e); - } - return StatsManager.PULL_SUCCESS; - } - - private void registerDebugElapsedClock() { - // No op. - } - - private void pullDebugElapsedClock() { - // No op. - } - - private void registerDebugFailingElapsedClock() { - // No op. - } - - private void pullDebugFailingElapsedClock() { - // No op. - } - - private void registerBuildInformation() { - int tagId = StatsLog.BUILD_INFORMATION; - mStatsManager.registerPullAtomCallback( - tagId, - null, // use default PullAtomMetadata values - (atomTag, data) -> pullBuildInformation(atomTag, data), - BackgroundThread.getExecutor() - ); - } - - private int pullBuildInformation(int atomTag, List<StatsEvent> pulledData) { - StatsEvent e = StatsEvent.newBuilder() - .setAtomId(atomTag) - .writeString(Build.FINGERPRINT) - .writeString(Build.BRAND) - .writeString(Build.PRODUCT) - .writeString(Build.DEVICE) - .writeString(Build.VERSION.RELEASE) - .writeString(Build.ID) - .writeString(Build.VERSION.INCREMENTAL) - .writeString(Build.TYPE) - .writeString(Build.TAGS) - .build(); - pulledData.add(e); - return StatsManager.PULL_SUCCESS; - } - - private void registerRoleHolder() { - // No op. - } - - private void pullRoleHolder() { - // No op. - } - - private void registerDangerousPermissionState() { - // No op. - } - - private void pullDangerousPermissionState() { - // No op. - } - - private void registerTimeZoneDataInfo() { - // No op. - } - - private void pullTimeZoneDataInfo() { - // No op. - } - - private void registerExternalStorageInfo() { - // No op. - } - - private void pullExternalStorageInfo() { - // No op. - } - - private void registerAppsOnExternalStorageInfo() { - // No op. - } - - private void pullAppsOnExternalStorageInfo() { - // No op. - } - - private void registerFaceSettings() { - // No op. - } - - private void pullRegisterFaceSettings() { - // No op. - } - - private void registerAppOps() { - // No op. - } - - private void pullAppOps() { - // No op. - } - - private void registerNotificationRemoteViews() { - // No op. - } - - private void pullNotificationRemoteViews() { - // No op. - } - - private void registerDangerousPermissionStateSampled() { - // No op. - } - - private void pullDangerousPermissionStateSampled() { - // No op. - } -} diff --git a/services/core/java/com/android/server/stats/IonMemoryUtil.java b/services/core/java/com/android/server/stats/pull/IonMemoryUtil.java index c9be96f08876..fde0a590df34 100644 --- a/services/core/java/com/android/server/stats/IonMemoryUtil.java +++ b/services/core/java/com/android/server/stats/pull/IonMemoryUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.stats; +package com.android.server.stats.pull; import android.os.FileUtils; import android.util.Slog; @@ -30,8 +30,11 @@ import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; -/** Utility methods for reading ion memory stats. */ -final class IonMemoryUtil { +/** + * Utility methods for reading ion memory stats. + * TODO: Consider making package private after puller migration + */ +public final class IonMemoryUtil { private static final String TAG = "IonMemoryUtil"; /** Path to debugfs file for the system ion heap. */ @@ -50,7 +53,7 @@ final class IonMemoryUtil { * Returns value of the total size in bytes of the system ion heap from * /sys/kernel/debug/ion/heaps/system. */ - static long readSystemIonHeapSizeFromDebugfs() { + public static long readSystemIonHeapSizeFromDebugfs() { return parseIonHeapSizeFromDebugfs(readFile(DEBUG_SYSTEM_ION_HEAP_FILE)); } @@ -78,7 +81,7 @@ final class IonMemoryUtil { * Returns values of allocation sizes in bytes on the system ion heap from * /sys/kernel/debug/ion/heaps/system. */ - static List<IonAllocations> readProcessSystemIonHeapSizesFromDebugfs() { + public static List<IonAllocations> readProcessSystemIonHeapSizesFromDebugfs() { return parseProcessIonHeapSizesFromDebugfs(readFile(DEBUG_SYSTEM_ION_HEAP_FILE)); } @@ -130,7 +133,7 @@ final class IonMemoryUtil { } /** Summary information about process ion allocations. */ - static final class IonAllocations { + public static final class IonAllocations { /** PID these allocations belong to. */ public int pid; /** Size of all individual allocations added together. */ diff --git a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java index c1eacce1f304..638dfd23c27f 100644 --- a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java +++ b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.stats; +package com.android.server.stats.pull; import static android.os.Process.PROC_OUT_STRING; @@ -22,7 +22,7 @@ import android.os.Process; import java.util.function.BiConsumer; -final class ProcfsMemoryUtil { +public final class ProcfsMemoryUtil { private static final int[] CMDLINE_OUT = new int[] { PROC_OUT_STRING }; private static final String[] STATUS_KEYS = new String[] { "Uid:", @@ -39,7 +39,7 @@ final class ProcfsMemoryUtil { * VmSwap fields in /proc/pid/status in kilobytes or null if not available. */ @Nullable - static MemorySnapshot readMemorySnapshotFromProcfs(int pid) { + public static MemorySnapshot readMemorySnapshotFromProcfs(int pid) { long[] output = new long[STATUS_KEYS.length]; output[0] = -1; Process.readProcLines("/proc/" + pid + "/status", STATUS_KEYS, output); @@ -63,7 +63,7 @@ final class ProcfsMemoryUtil { * Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string * if the file is not available. */ - static String readCmdlineFromProcfs(int pid) { + public static String readCmdlineFromProcfs(int pid) { String[] cmdline = new String[1]; if (!Process.readProcFile("/proc/" + pid + "/cmdline", CMDLINE_OUT, cmdline, null, null)) { return ""; @@ -71,7 +71,7 @@ final class ProcfsMemoryUtil { return cmdline[0]; } - static void forEachPid(BiConsumer<Integer, String> func) { + public static void forEachPid(BiConsumer<Integer, String> func) { int[] pids = new int[1024]; pids = Process.getPids("/proc", pids); for (int pid : pids) { @@ -86,7 +86,7 @@ final class ProcfsMemoryUtil { } } - static final class MemorySnapshot { + public static final class MemorySnapshot { public int uid; public int rssHighWaterMarkInKilobytes; public int rssInKilobytes; diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java new file mode 100644 index 000000000000..b11ec6fd97fb --- /dev/null +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -0,0 +1,2671 @@ +/* + * 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.stats.pull; + +import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED; +import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; +import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; +import static android.os.Debug.getIonHeapsSizeKb; +import static android.os.Process.THREAD_PRIORITY_BACKGROUND; +import static android.os.Process.getUidForPid; +import static android.os.storage.VolumeInfo.TYPE_PRIVATE; +import static android.os.storage.VolumeInfo.TYPE_PUBLIC; + +import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; +import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs; +import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs; +import static com.android.server.stats.pull.ProcfsMemoryUtil.forEachPid; +import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs; +import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManagerInternal; +import android.app.AlarmManager; +import android.app.AlarmManager.OnAlarmListener; +import android.app.AppOpsManager; +import android.app.AppOpsManager.HistoricalOps; +import android.app.AppOpsManager.HistoricalOpsRequest; +import android.app.AppOpsManager.HistoricalPackageOps; +import android.app.AppOpsManager.HistoricalUidOps; +import android.app.INotificationManager; +import android.app.ProcessMemoryState; +import android.app.StatsManager; +import android.app.StatsManager.PullAtomMetadata; +import android.bluetooth.BluetoothActivityEnergyInfo; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.UidTraffic; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PermissionInfo; +import android.content.pm.UserInfo; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; +import android.net.ConnectivityManager; +import android.net.INetworkStatsService; +import android.net.Network; +import android.net.NetworkRequest; +import android.net.NetworkStats; +import android.net.wifi.WifiManager; +import android.os.BatteryStats; +import android.os.BatteryStatsInternal; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.CoolingDevice; +import android.os.Environment; +import android.os.FileUtils; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.IPullAtomCallback; +import android.os.IStatsCompanionService; +import android.os.IStatsd; +import android.os.IStoraged; +import android.os.IThermalEventListener; +import android.os.IThermalService; +import android.os.Looper; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.StatFs; +import android.os.StatsLogEventWrapper; +import android.os.SynchronousResultReceiver; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.Temperature; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.connectivity.WifiActivityEnergyInfo; +import android.os.storage.DiskInfo; +import android.os.storage.StorageManager; +import android.os.storage.VolumeInfo; +import android.provider.Settings; +import android.stats.storage.StorageEnums; +import android.telephony.ModemActivityInfo; +import android.telephony.TelephonyManager; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; +import android.util.Slog; +import android.util.StatsEvent; +import android.util.StatsLog; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.app.procstats.IProcessStats; +import com.android.internal.app.procstats.ProcessStats; +import com.android.internal.os.BackgroundThread; +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; +import com.android.internal.os.BinderCallsStats.ExportedCallStat; +import com.android.internal.os.KernelCpuSpeedReader; +import com.android.internal.os.KernelCpuThreadReader; +import com.android.internal.os.KernelCpuThreadReaderDiff; +import com.android.internal.os.KernelCpuThreadReaderSettingsObserver; +import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader; +import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader; +import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; +import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; +import com.android.internal.os.KernelWakelockReader; +import com.android.internal.os.KernelWakelockStats; +import com.android.internal.os.LooperStats; +import com.android.internal.os.PowerProfile; +import com.android.internal.os.ProcessCpuTracker; +import com.android.internal.os.StoragedUidIoStatsReader; +import com.android.internal.util.DumpUtils; +import com.android.server.BinderCallsStatsService; +import com.android.server.LocalServices; +import com.android.server.SystemService; +import com.android.server.SystemServiceManager; +import com.android.server.am.MemoryStatUtil.MemoryStat; +import com.android.server.notification.NotificationManagerService; +import com.android.server.role.RoleManagerInternal; +import com.android.server.stats.pull.IonMemoryUtil.IonAllocations; +import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot; +import com.android.server.storage.DiskStatsFileLogger; +import com.android.server.storage.DiskStatsLoggingService; + +import com.google.android.collect.Sets; + +import libcore.io.IoUtils; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.MissingResourceException; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * SystemService containing PullAtomCallbacks that are registered with statsd. + * + * @hide + */ +public class StatsPullAtomService extends SystemService { + private static final String TAG = "StatsPullAtomService"; + private static final boolean DEBUG = true; + + private static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity"; + /** + * How long to wait on an individual subsystem to return its stats. + */ + private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000; + + private final Object mNetworkStatsLock = new Object(); + @GuardedBy("mNetworkStatsLock") + private INetworkStatsService mNetworkStatsService; + private final Object mThermalLock = new Object(); + @GuardedBy("mThermalLock") + private IThermalService mThermalService; + private final Object mStoragedLock = new Object(); + @GuardedBy("mStoragedLock") + private IStoraged mStorageService; + private final Object mNotificationStatsLock = new Object(); + @GuardedBy("mNotificationStatsLock") + private INotificationManager mNotificationManagerService; + + private final Context mContext; + private StatsManager mStatsManager; + private StorageManager mStorageManager; + + public StatsPullAtomService(Context context) { + super(context); + mContext = context; + } + + @Override + public void onStart() { + mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER); + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + mTelephony = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class); + + // Used to initialize the CPU Frequency atom. + PowerProfile powerProfile = new PowerProfile(mContext); + final int numClusters = powerProfile.getNumCpuClusters(); + mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters]; + int firstCpuOfCluster = 0; + for (int i = 0; i < numClusters; i++) { + final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i); + mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster, + numSpeedSteps); + firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i); + } + + // Used for CPU_TIME_PER_THREAD_FREQ + mKernelCpuThreadReader = + KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext); + } + + @Override + public void onBootPhase(int phase) { + super.onBootPhase(phase); + if (phase == PHASE_SYSTEM_SERVICES_READY) { + BackgroundThread.getHandler().post(() -> { + registerAllPullers(); + }); + } + } + + void registerAllPullers() { + if (DEBUG) { + Slog.d(TAG, "Registering all pullers with statsd"); + } + registerWifiBytesTransfer(); + registerWifiBytesTransferBackground(); + registerMobileBytesTransfer(); + registerMobileBytesTransferBackground(); + registerBluetoothBytesTransfer(); + registerKernelWakelock(); + registerCpuTimePerFreq(); + registerCpuTimePerUid(); + registerCpuTimePerUidFreq(); + registerCpuActiveTime(); + registerCpuClusterTime(); + registerWifiActivityInfo(); + registerModemActivityInfo(); + registerBluetoothActivityInfo(); + registerSystemElapsedRealtime(); + registerSystemUptime(); + registerRemainingBatteryCapacity(); + registerFullBatteryCapacity(); + registerBatteryVoltage(); + registerBatteryLevel(); + registerBatteryCycleCount(); + registerProcessMemoryState(); + registerProcessMemoryHighWaterMark(); + registerProcessMemorySnapshot(); + registerSystemIonHeapSize(); + registerIonHeapSize(); + registerProcessSystemIonHeapSize(); + registerTemperature(); + registerCoolingDevice(); + registerBinderCallsStats(); + registerBinderCallsStatsExceptions(); + registerLooperStats(); + registerDiskStats(); + registerDirectoryUsage(); + registerAppSize(); + registerCategorySize(); + registerNumFingerprintsEnrolled(); + registerNumFacesEnrolled(); + registerProcStats(); + registerProcStatsPkgProc(); + registerDiskIO(); + registerPowerProfile(); + registerProcessCpuTime(); + registerCpuTimePerThreadFreq(); + registerDeviceCalculatedPowerUse(); + registerDeviceCalculatedPowerBlameUid(); + registerDeviceCalculatedPowerBlameOther(); + registerDebugElapsedClock(); + registerDebugFailingElapsedClock(); + registerBuildInformation(); + registerRoleHolder(); + registerTimeZoneDataInfo(); + registerExternalStorageInfo(); + registerAppsOnExternalStorageInfo(); + registerFaceSettings(); + registerAppOps(); + registerNotificationRemoteViews(); + registerDangerousPermissionState(); + registerDangerousPermissionStateSampled(); + } + + private INetworkStatsService getINetworkStatsService() { + synchronized (mNetworkStatsLock) { + if (mNetworkStatsService == null) { + mNetworkStatsService = INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + if (mNetworkStatsService != null) { + try { + mNetworkStatsService.asBinder().linkToDeath(() -> { + synchronized (mNetworkStatsLock) { + mNetworkStatsService = null; + } + }, /* flags */ 0); + } catch (RemoteException e) { + Slog.e(TAG, "linkToDeath with NetworkStatsService failed", e); + mNetworkStatsService = null; + } + } + + } + return mNetworkStatsService; + } + } + + private IThermalService getIThermalService() { + synchronized (mThermalLock) { + if (mThermalService == null) { + mThermalService = IThermalService.Stub.asInterface( + ServiceManager.getService(Context.THERMAL_SERVICE)); + if (mThermalService != null) { + try { + mThermalService.asBinder().linkToDeath(() -> { + synchronized (mThermalLock) { + mThermalService = null; + } + }, /* flags */ 0); + } catch (RemoteException e) { + Slog.e(TAG, "linkToDeath with thermalService failed", e); + mThermalService = null; + } + } + } + return mThermalService; + } + } + + private IStoraged getIStoragedService() { + synchronized (mStoragedLock) { + if (mStorageService == null) { + mStorageService = IStoraged.Stub.asInterface( + ServiceManager.getService("storaged")); + } + if (mStorageService != null) { + try { + mStorageService.asBinder().linkToDeath(() -> { + synchronized (mStoragedLock) { + mStorageService = null; + } + }, /* flags */ 0); + } catch (RemoteException e) { + Slog.e(TAG, "linkToDeath with storagedService failed", e); + mStorageService = null; + } + } + } + return mStorageService; + } + + private INotificationManager getINotificationManagerService() { + synchronized (mNotificationStatsLock) { + if (mNotificationManagerService == null) { + mNotificationManagerService = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + } + if (mNotificationManagerService != null) { + try { + mNotificationManagerService.asBinder().linkToDeath(() -> { + synchronized (mNotificationStatsLock) { + mNotificationManagerService = null; + } + }, /* flags */ 0); + } catch (RemoteException e) { + Slog.e(TAG, "linkToDeath with notificationManager failed", e); + mNotificationManagerService = null; + } + } + } + return mNotificationManagerService; + } + + private void registerWifiBytesTransfer() { + int tagId = StatsLog.WIFI_BYTES_TRANSFER; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {2, 3, 4, 5}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullWifiBytesTransfer(atomTag, data) + ); + } + + private int pullWifiBytesTransfer(int atomTag, List<StatsEvent> pulledData) { + INetworkStatsService networkStatsService = getINetworkStatsService(); + if (networkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return StatsManager.PULL_SKIP; + } + long token = Binder.clearCallingIdentity(); + try { + // TODO: Consider caching the following call to get BatteryStatsInternal. + BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); + String[] ifaces = bs.getWifiIfaces(); + if (ifaces.length == 0) { + return StatsManager.PULL_SKIP; + } + // Combine all the metrics per Uid into one record. + NetworkStats stats = networkStatsService.getDetailedUidStats(ifaces).groupedByUid(); + addNetworkStats(atomTag, pulledData, stats, false); + } catch (RemoteException e) { + Slog.e(TAG, "Pulling netstats for wifi bytes has error", e); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + + private void addNetworkStats( + int tag, List<StatsEvent> ret, NetworkStats stats, boolean withFGBG) { + int size = stats.size(); + NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling + for (int j = 0; j < size; j++) { + stats.getValues(j, entry); + StatsEvent.Builder e = StatsEvent.newBuilder(); + e.setAtomId(tag); + e.writeInt(entry.uid); + if (withFGBG) { + e.writeInt(entry.set); + } + e.writeLong(entry.rxBytes); + e.writeLong(entry.rxPackets); + e.writeLong(entry.txBytes); + e.writeLong(entry.txPackets); + ret.add(e.build()); + } + } + + /** + * Allows rollups per UID but keeping the set (foreground/background) slicing. + * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java + */ + private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) { + final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1); + + final NetworkStats.Entry entry = new NetworkStats.Entry(); + entry.iface = NetworkStats.IFACE_ALL; + entry.tag = NetworkStats.TAG_NONE; + entry.metered = NetworkStats.METERED_ALL; + entry.roaming = NetworkStats.ROAMING_ALL; + + int size = stats.size(); + NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values + for (int i = 0; i < size; i++) { + stats.getValues(i, recycle); + + // Skip specific tags, since already counted in TAG_NONE + if (recycle.tag != NetworkStats.TAG_NONE) continue; + + entry.set = recycle.set; // Allows slicing by background/foreground + entry.uid = recycle.uid; + entry.rxBytes = recycle.rxBytes; + entry.rxPackets = recycle.rxPackets; + entry.txBytes = recycle.txBytes; + entry.txPackets = recycle.txPackets; + // Operations purposefully omitted since we don't use them for statsd. + ret.combineValues(entry); + } + return ret; + } + + private void registerWifiBytesTransferBackground() { + int tagId = StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {3, 4, 5, 6}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullWifiBytesTransferBackground(atomTag, data) + ); + } + + private int pullWifiBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) { + INetworkStatsService networkStatsService = getINetworkStatsService(); + if (networkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return StatsManager.PULL_SKIP; + } + long token = Binder.clearCallingIdentity(); + try { + BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); + String[] ifaces = bs.getWifiIfaces(); + if (ifaces.length == 0) { + return StatsManager.PULL_SKIP; + } + NetworkStats stats = rollupNetworkStatsByFGBG( + networkStatsService.getDetailedUidStats(ifaces)); + addNetworkStats(atomTag, pulledData, stats, true); + } catch (RemoteException e) { + Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerMobileBytesTransfer() { + int tagId = StatsLog.MOBILE_BYTES_TRANSFER; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {2, 3, 4, 5}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullMobileBytesTransfer(atomTag, data) + ); + } + + private int pullMobileBytesTransfer(int atomTag, List<StatsEvent> pulledData) { + INetworkStatsService networkStatsService = getINetworkStatsService(); + if (networkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return StatsManager.PULL_SKIP; + } + long token = Binder.clearCallingIdentity(); + try { + BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); + String[] ifaces = bs.getMobileIfaces(); + if (ifaces.length == 0) { + return StatsManager.PULL_SKIP; + } + // Combine all the metrics per Uid into one record. + NetworkStats stats = networkStatsService.getDetailedUidStats(ifaces).groupedByUid(); + addNetworkStats(atomTag, pulledData, stats, false); + } catch (RemoteException e) { + Slog.e(TAG, "Pulling netstats for mobile bytes has error", e); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerMobileBytesTransferBackground() { + int tagId = StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {3, 4, 5, 6}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullMobileBytesTransferBackground(atomTag, data) + ); + } + + private int pullMobileBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) { + INetworkStatsService networkStatsService = getINetworkStatsService(); + if (networkStatsService == null) { + Slog.e(TAG, "NetworkStats Service is not available!"); + return StatsManager.PULL_SKIP; + } + long token = Binder.clearCallingIdentity(); + try { + BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class); + String[] ifaces = bs.getMobileIfaces(); + if (ifaces.length == 0) { + return StatsManager.PULL_SKIP; + } + NetworkStats stats = rollupNetworkStatsByFGBG( + networkStatsService.getDetailedUidStats(ifaces)); + addNetworkStats(atomTag, pulledData, stats, true); + } catch (RemoteException e) { + Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerBluetoothBytesTransfer() { + int tagId = StatsLog.BLUETOOTH_BYTES_TRANSFER; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {2, 3}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullBluetoothBytesTransfer(atomTag, data) + ); + } + + /** + * Helper method to extract the Parcelable controller info from a + * SynchronousResultReceiver. + */ + private static <T extends Parcelable> T awaitControllerInfo( + @Nullable SynchronousResultReceiver receiver) { + if (receiver == null) { + return null; + } + + try { + final SynchronousResultReceiver.Result result = + receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS); + if (result.bundle != null) { + // This is the final destination for the Bundle. + result.bundle.setDefusable(true); + + final T data = result.bundle.getParcelable(RESULT_RECEIVER_CONTROLLER_KEY); + if (data != null) { + return data; + } + } + Slog.e(TAG, "no controller energy info supplied for " + receiver.getName()); + } catch (TimeoutException e) { + Slog.w(TAG, "timeout reading " + receiver.getName() + " stats"); + } + return null; + } + + private synchronized BluetoothActivityEnergyInfo fetchBluetoothData() { + // TODO: Investigate whether the synchronized keyword is needed. + final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null) { + SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver( + "bluetooth"); + adapter.requestControllerActivityEnergyInfo(bluetoothReceiver); + return awaitControllerInfo(bluetoothReceiver); + } else { + Slog.e(TAG, "Failed to get bluetooth adapter!"); + return null; + } + } + + private int pullBluetoothBytesTransfer(int atomTag, List<StatsEvent> pulledData) { + BluetoothActivityEnergyInfo info = fetchBluetoothData(); + if (info == null || info.getUidTraffic() == null) { + return StatsManager.PULL_SKIP; + } + for (UidTraffic traffic : info.getUidTraffic()) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(traffic.getUid()) + .writeLong(traffic.getRxBytes()) + .writeLong(traffic.getTxBytes()) + .build(); + pulledData.add(e); + } + return StatsManager.PULL_SUCCESS; + } + + private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); + private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); + + private void registerKernelWakelock() { + int tagId = StatsLog.KERNEL_WAKELOCK; + mStatsManager.registerPullAtomCallback( + tagId, + /* PullAtomMetadata */ null, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullKernelWakelock(atomTag, data) + ); + } + + private int pullKernelWakelock(int atomTag, List<StatsEvent> pulledData) { + final KernelWakelockStats wakelockStats = + mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats); + for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) { + String name = ent.getKey(); + KernelWakelockStats.Entry kws = ent.getValue(); + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeString(name) + .writeInt(kws.mCount) + .writeInt(kws.mVersion) + .writeLong(kws.mTotalTime) + .build(); + pulledData.add(e); + } + return StatsManager.PULL_SUCCESS; + } + + private KernelCpuSpeedReader[] mKernelCpuSpeedReaders; + // Disables throttler on CPU time readers. + private KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader = + new KernelCpuUidUserSysTimeReader(false); + private KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader = + new KernelCpuUidFreqTimeReader(false); + private KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader = + new KernelCpuUidActiveTimeReader(false); + private KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader = + new KernelCpuUidClusterTimeReader(false); + + private void registerCpuTimePerFreq() { + int tagId = StatsLog.CPU_TIME_PER_FREQ; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {3}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullCpuTimePerFreq(atomTag, data) + ); + } + + private int pullCpuTimePerFreq(int atomTag, List<StatsEvent> pulledData) { + for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) { + long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute(); + if (clusterTimeMs != null) { + for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(cluster) + .writeInt(speed) + .writeLong(clusterTimeMs[speed]) + .build(); + pulledData.add(e); + } + } + } + return StatsManager.PULL_SUCCESS; + } + + private void registerCpuTimePerUid() { + int tagId = StatsLog.CPU_TIME_PER_UID; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {2, 3}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullCpuTimePerUid(atomTag, data) + ); + } + + private int pullCpuTimePerUid(int atomTag, List<StatsEvent> pulledData) { + mCpuUidUserSysTimeReader.readAbsolute((uid, timesUs) -> { + long userTimeUs = timesUs[0], systemTimeUs = timesUs[1]; + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(uid) + .writeLong(userTimeUs) + .writeLong(systemTimeUs) + .build(); + pulledData.add(e); + }); + return StatsManager.PULL_SUCCESS; + } + + private void registerCpuTimePerUidFreq() { + // the throttling is 3sec, handled in + // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader + int tagId = StatsLog.CPU_TIME_PER_UID_FREQ; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {4}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullCpuTimeperUidFreq(atomTag, data) + ); + } + + private int pullCpuTimeperUidFreq(int atomTag, List<StatsEvent> pulledData) { + mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> { + for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { + if (cpuFreqTimeMs[freqIndex] != 0) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(uid) + .writeInt(freqIndex) + .writeLong(cpuFreqTimeMs[freqIndex]) + .build(); + pulledData.add(e); + } + } + }); + return StatsManager.PULL_SUCCESS; + } + + private void registerCpuActiveTime() { + // the throttling is 3sec, handled in + // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader + int tagId = StatsLog.CPU_ACTIVE_TIME; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {2}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullCpuActiveTime(atomTag, data) + ); + } + + private int pullCpuActiveTime(int atomTag, List<StatsEvent> pulledData) { + mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(uid) + .writeLong(cpuActiveTimesMs) + .build(); + pulledData.add(e); + }); + return StatsManager.PULL_SUCCESS; + } + + private void registerCpuClusterTime() { + // the throttling is 3sec, handled in + // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader + int tagId = StatsLog.CPU_CLUSTER_TIME; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {3}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullCpuClusterTime(atomTag, data) + ); + } + + private int pullCpuClusterTime(int atomTag, List<StatsEvent> pulledData) { + mCpuUidClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> { + for (int i = 0; i < cpuClusterTimesMs.length; i++) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(uid) + .writeInt(i) + .writeLong(cpuClusterTimesMs[i]) + .build(); + pulledData.add(e); + } + }); + return StatsManager.PULL_SUCCESS; + } + + private void registerWifiActivityInfo() { + int tagId = StatsLog.WIFI_ACTIVITY_INFO; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullWifiActivityInfo(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private WifiManager mWifiManager; + private TelephonyManager mTelephony; + + private int pullWifiActivityInfo(int atomTag, List<StatsEvent> pulledData) { + long token = Binder.clearCallingIdentity(); + try { + SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi"); + mWifiManager.getWifiActivityEnergyInfoAsync( + new Executor() { + @Override + public void execute(Runnable runnable) { + // run the listener on the binder thread, if it was run on the main + // thread it would deadlock since we would be waiting on ourselves + runnable.run(); + } + }, + info -> { + Bundle bundle = new Bundle(); + bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info); + wifiReceiver.send(0, bundle); + } + ); + final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver); + if (wifiInfo == null) { + return StatsManager.PULL_SKIP; + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(wifiInfo.getTimeSinceBootMillis()) + .writeInt(wifiInfo.getStackState()) + .writeLong(wifiInfo.getControllerTxDurationMillis()) + .writeLong(wifiInfo.getControllerRxDurationMillis()) + .writeLong(wifiInfo.getControllerIdleDurationMillis()) + .writeLong(wifiInfo.getControllerEnergyUsedMicroJoules()) + .build(); + pulledData.add(e); + } catch (RuntimeException e) { + Slog.e(TAG, "failed to getWifiActivityEnergyInfoAsync", e); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerModemActivityInfo() { + int tagId = StatsLog.MODEM_ACTIVITY_INFO; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullModemActivityInfo(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullModemActivityInfo(int atomTag, List<StatsEvent> pulledData) { + long token = Binder.clearCallingIdentity(); + try { + SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony"); + mTelephony.requestModemActivityInfo(modemReceiver); + final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver); + if (modemInfo == null) { + return StatsManager.PULL_SKIP; + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(modemInfo.getTimestamp()) + .writeLong(modemInfo.getSleepTimeMillis()) + .writeLong(modemInfo.getIdleTimeMillis()) + .writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis()) + .writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis()) + .writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis()) + .writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis()) + .writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis()) + .writeLong(modemInfo.getReceiveTimeMillis()) + .build(); + pulledData.add(e); + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerBluetoothActivityInfo() { + int tagId = StatsLog.BLUETOOTH_ACTIVITY_INFO; + mStatsManager.registerPullAtomCallback( + tagId, + /* metadata */ null, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullBluetoothActivityInfo(atomTag, data) + ); + } + + private int pullBluetoothActivityInfo(int atomTag, List<StatsEvent> pulledData) { + BluetoothActivityEnergyInfo info = fetchBluetoothData(); + if (info == null) { + return StatsManager.PULL_SKIP; + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(info.getTimeStamp()) + .writeInt(info.getBluetoothStackState()) + .writeLong(info.getControllerTxTimeMillis()) + .writeLong(info.getControllerRxTimeMillis()) + .writeLong(info.getControllerIdleTimeMillis()) + .writeLong(info.getControllerEnergyUsed()) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; + } + + private static final long NS_PER_SEC = 1000000000; + + private void registerSystemElapsedRealtime() { + int tagId = StatsLog.SYSTEM_ELAPSED_REALTIME; + PullAtomMetadata metadata = PullAtomMetadata.newBuilder() + .setCoolDownNs(NS_PER_SEC) + .setTimeoutNs(NS_PER_SEC / 2) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + (atomTag, data) -> pullSystemElapsedRealtime(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullSystemElapsedRealtime(int atomTag, List<StatsEvent> pulledData) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(SystemClock.elapsedRealtime()) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; + } + + private void registerSystemUptime() { + int tagId = StatsLog.SYSTEM_UPTIME; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + BackgroundThread.getExecutor(), + (atomTag, data) -> pullSystemUptime(atomTag, data) + ); + } + + private int pullSystemUptime(int atomTag, List<StatsEvent> pulledData) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(SystemClock.elapsedRealtime()) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; + } + + private void registerRemainingBatteryCapacity() { + // No op. + } + + private void pullRemainingBatteryCapacity() { + // No op. + } + + private void registerFullBatteryCapacity() { + // No op. + } + + private void pullFullBatteryCapacity() { + // No op. + } + + private void registerBatteryVoltage() { + // No op. + } + + private void pullBatteryVoltage() { + // No op. + } + + private void registerBatteryLevel() { + // No op. + } + + private void pullBatteryLevel() { + // No op. + } + + private void registerBatteryCycleCount() { + // No op. + } + + private void pullBatteryCycleCount() { + // No op. + } + + private void registerProcessMemoryState() { + int tagId = StatsLog.PROCESS_MEMORY_STATE; + PullAtomMetadata metadata = PullAtomMetadata.newBuilder() + .setAdditiveFields(new int[] {4, 5, 6, 7, 8}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + (atomTag, data) -> pullProcessMemoryState(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullProcessMemoryState(int atomTag, List<StatsEvent> pulledData) { + List<ProcessMemoryState> processMemoryStates = + LocalServices.getService(ActivityManagerInternal.class) + .getMemoryStateForProcesses(); + for (ProcessMemoryState processMemoryState : processMemoryStates) { + final MemoryStat memoryStat = readMemoryStatFromFilesystem(processMemoryState.uid, + processMemoryState.pid); + if (memoryStat == null) { + continue; + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(processMemoryState.uid) + .writeString(processMemoryState.processName) + .writeInt(processMemoryState.oomScore) + .writeLong(memoryStat.pgfault) + .writeLong(memoryStat.pgmajfault) + .writeLong(memoryStat.rssInBytes) + .writeLong(memoryStat.cacheInBytes) + .writeLong(memoryStat.swapInBytes) + .writeLong(-1) // unused + .writeLong(-1) // unused + .writeInt(-1) // unused + .build(); + pulledData.add(e); + } + return StatsManager.PULL_SUCCESS; + } + + /** + * Which native processes to snapshot memory for. + * + * <p>Processes are matched by their cmdline in procfs. Example: cat /proc/pid/cmdline returns + * /system/bin/statsd for the stats daemon. + */ + private static final Set<String> MEMORY_INTERESTING_NATIVE_PROCESSES = Sets.newHashSet( + "/system/bin/statsd", // Stats daemon. + "/system/bin/surfaceflinger", + "/system/bin/apexd", // APEX daemon. + "/system/bin/audioserver", + "/system/bin/cameraserver", + "/system/bin/drmserver", + "/system/bin/healthd", + "/system/bin/incidentd", + "/system/bin/installd", + "/system/bin/lmkd", // Low memory killer daemon. + "/system/bin/logd", + "media.codec", + "media.extractor", + "media.metrics", + "/system/bin/mediadrmserver", + "/system/bin/mediaserver", + "/system/bin/performanced", + "/system/bin/tombstoned", + "/system/bin/traced", // Perfetto. + "/system/bin/traced_probes", // Perfetto. + "webview_zygote", + "zygote", + "zygote64"); + + /** + * Lowest available uid for apps. + * + * <p>Used to quickly discard memory snapshots of the zygote forks from native process + * measurements. + */ + private static final int MIN_APP_UID = 10_000; + + private static boolean isAppUid(int uid) { + return uid >= MIN_APP_UID; + } + + private void registerProcessMemoryHighWaterMark() { + int tagId = StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullProcessMemoryHighWaterMark(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullProcessMemoryHighWaterMark(int atomTag, List<StatsEvent> pulledData) { + List<ProcessMemoryState> managedProcessList = + LocalServices.getService(ActivityManagerInternal.class) + .getMemoryStateForProcesses(); + for (ProcessMemoryState managedProcess : managedProcessList) { + final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid); + if (snapshot == null) { + continue; + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(managedProcess.uid) + .writeString(managedProcess.processName) + // RSS high-water mark in bytes. + .writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L) + .writeInt(snapshot.rssHighWaterMarkInKilobytes) + .build(); + pulledData.add(e); + } + forEachPid((pid, cmdLine) -> { + if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) { + return; + } + final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid); + if (snapshot == null) { + return; + } + // Sometimes we get here a process that is not included in the whitelist. It comes + // from forking the zygote for an app. We can ignore that sample because this process + // is collected by ProcessMemoryState. + if (isAppUid(snapshot.uid)) { + return; + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(snapshot.uid) + .writeString(cmdLine) + // RSS high-water mark in bytes. + .writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L) + .writeInt(snapshot.rssHighWaterMarkInKilobytes) + .build(); + pulledData.add(e); + }); + // Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes. + SystemProperties.set("sys.rss_hwm_reset.on", "1"); + return StatsManager.PULL_SUCCESS; + } + + private void registerProcessMemorySnapshot() { + int tagId = StatsLog.PROCESS_MEMORY_SNAPSHOT; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullProcessMemorySnapshot(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullProcessMemorySnapshot(int atomTag, List<StatsEvent> pulledData) { + List<ProcessMemoryState> managedProcessList = + LocalServices.getService(ActivityManagerInternal.class) + .getMemoryStateForProcesses(); + for (ProcessMemoryState managedProcess : managedProcessList) { + final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid); + if (snapshot == null) { + continue; + } + StatsEvent e = StatsEvent.newBuilder() + .writeInt(managedProcess.uid) + .writeString(managedProcess.processName) + .writeInt(managedProcess.pid) + .writeInt(managedProcess.oomScore) + .writeInt(snapshot.rssInKilobytes) + .writeInt(snapshot.anonRssInKilobytes) + .writeInt(snapshot.swapInKilobytes) + .writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes) + .build(); + pulledData.add(e); + } + forEachPid((pid, cmdLine) -> { + if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) { + return; + } + final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid); + if (snapshot == null) { + return; + } + // Sometimes we get here a process that is not included in the whitelist. It comes + // from forking the zygote for an app. We can ignore that sample because this process + // is collected by ProcessMemoryState. + if (isAppUid(snapshot.uid)) { + return; + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(snapshot.uid) + .writeString(cmdLine) + .writeInt(pid) + .writeInt(-1001) // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1. + .writeInt(snapshot.rssInKilobytes) + .writeInt(snapshot.anonRssInKilobytes) + .writeInt(snapshot.swapInKilobytes) + .writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes) + .build(); + pulledData.add(e); + }); + return StatsManager.PULL_SUCCESS; + } + + private void registerSystemIonHeapSize() { + int tagId = StatsLog.SYSTEM_ION_HEAP_SIZE; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullSystemIonHeapSize(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullSystemIonHeapSize(int atomTag, List<StatsEvent> pulledData) { + final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs(); + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(systemIonHeapSizeInBytes) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; + } + + private void registerIonHeapSize() { + int tagId = StatsLog.ION_HEAP_SIZE; + mStatsManager.registerPullAtomCallback( + tagId, + /* PullAtomMetadata */ null, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullIonHeapSize(atomTag, data) + ); + } + + private int pullIonHeapSize(int atomTag, List<StatsEvent> pulledData) { + int ionHeapSizeInKilobytes = (int) getIonHeapsSizeKb(); + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(ionHeapSizeInKilobytes) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; + } + + private void registerProcessSystemIonHeapSize() { + int tagId = StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullProcessSystemIonHeapSize(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullProcessSystemIonHeapSize(int atomTag, List<StatsEvent> pulledData) { + List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs(); + for (IonAllocations allocations : result) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(getUidForPid(allocations.pid)) + .writeString(readCmdlineFromProcfs(allocations.pid)) + .writeInt((int) (allocations.totalSizeInBytes / 1024)) + .writeInt(allocations.count) + .writeInt((int) (allocations.maxSizeInBytes / 1024)) + .build(); + pulledData.add(e); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerTemperature() { + int tagId = StatsLog.TEMPERATURE; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullTemperature(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullTemperature(int atomTag, List<StatsEvent> pulledData) { + IThermalService thermalService = getIThermalService(); + if (thermalService == null) { + return StatsManager.PULL_SKIP; + } + final long callingToken = Binder.clearCallingIdentity(); + try { + List<Temperature> temperatures = thermalService.getCurrentTemperatures(); + for (Temperature temp : temperatures) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(temp.getType()) + .writeString(temp.getName()) + .writeInt((int) (temp.getValue() * 10)) + .writeInt(temp.getStatus()) + .build(); + pulledData.add(e); + } + } catch (RemoteException e) { + // Should not happen. + Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures."); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(callingToken); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerCoolingDevice() { + int tagId = StatsLog.COOLING_DEVICE; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullCooldownDevice(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullCooldownDevice(int atomTag, List<StatsEvent> pulledData) { + IThermalService thermalService = getIThermalService(); + if (thermalService == null) { + return StatsManager.PULL_SKIP; + } + final long callingToken = Binder.clearCallingIdentity(); + try { + List<CoolingDevice> devices = thermalService.getCurrentCoolingDevices(); + for (CoolingDevice device : devices) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(device.getType()) + .writeString(device.getName()) + .writeInt((int) (device.getValue())) + .build(); + pulledData.add(e); + } + } catch (RemoteException e) { + // Should not happen. + Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures."); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(callingToken); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerBinderCallsStats() { + int tagId = StatsLog.BINDER_CALLS; + PullAtomMetadata metadata = PullAtomMetadata.newBuilder() + .setAdditiveFields(new int[] {4, 5, 6, 8, 12}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + (atomTag, data) -> pullBinderCallsStats(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullBinderCallsStats(int atomTag, List<StatsEvent> pulledData) { + BinderCallsStatsService.Internal binderStats = + LocalServices.getService(BinderCallsStatsService.Internal.class); + if (binderStats == null) { + Slog.e(TAG, "failed to get binderStats"); + return StatsManager.PULL_SKIP; + } + + List<ExportedCallStat> callStats = binderStats.getExportedCallStats(); + binderStats.reset(); + for (ExportedCallStat callStat : callStats) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(callStat.workSourceUid) + .writeString(callStat.className) + .writeString(callStat.methodName) + .writeLong(callStat.callCount) + .writeLong(callStat.exceptionCount) + .writeLong(callStat.latencyMicros) + .writeLong(callStat.maxLatencyMicros) + .writeLong(callStat.cpuTimeMicros) + .writeLong(callStat.maxCpuTimeMicros) + .writeLong(callStat.maxReplySizeBytes) + .writeLong(callStat.maxRequestSizeBytes) + .writeLong(callStat.recordedCallCount) + .writeInt(callStat.screenInteractive ? 1 : 0) + .writeInt(callStat.callingUid) + .build(); + pulledData.add(e); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerBinderCallsStatsExceptions() { + int tagId = StatsLog.BINDER_CALLS_EXCEPTIONS; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullBinderCallsStatsExceptions(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullBinderCallsStatsExceptions(int atomTag, List<StatsEvent> pulledData) { + BinderCallsStatsService.Internal binderStats = + LocalServices.getService(BinderCallsStatsService.Internal.class); + if (binderStats == null) { + Slog.e(TAG, "failed to get binderStats"); + return StatsManager.PULL_SKIP; + } + + ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats(); + // TODO: decouple binder calls exceptions with the rest of the binder calls data so that we + // can reset the exception stats. + for (Map.Entry<String, Integer> entry : exceptionStats.entrySet()) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeString(entry.getKey()) + .writeInt(entry.getValue()) + .build(); + pulledData.add(e); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerLooperStats() { + int tagId = StatsLog.LOOPER_STATS; + PullAtomMetadata metadata = PullAtomMetadata.newBuilder() + .setAdditiveFields(new int[] {5, 6, 7, 8, 9}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + (atomTag, data) -> pullLooperStats(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullLooperStats(int atomTag, List<StatsEvent> pulledData) { + LooperStats looperStats = LocalServices.getService(LooperStats.class); + if (looperStats == null) { + return StatsManager.PULL_SKIP; + } + + List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); + looperStats.reset(); + for (LooperStats.ExportedEntry entry : entries) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(entry.workSourceUid) + .writeString(entry.handlerClassName) + .writeString(entry.threadName) + .writeString(entry.messageName) + .writeLong(entry.messageCount) + .writeLong(entry.exceptionCount) + .writeLong(entry.recordedMessageCount) + .writeLong(entry.totalLatencyMicros) + .writeLong(entry.cpuUsageMicros) + .writeBoolean(entry.isInteractive) + .writeLong(entry.maxCpuUsageMicros) + .writeLong(entry.maxLatencyMicros) + .writeLong(entry.recordedDelayMessageCount) + .writeLong(entry.delayMillis) + .writeLong(entry.maxDelayMillis) + .build(); + pulledData.add(e); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerDiskStats() { + int tagId = StatsLog.DISK_STATS; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullDiskStats(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullDiskStats(int atomTag, List<StatsEvent> pulledData) { + // Run a quick-and-dirty performance test: write 512 bytes + byte[] junk = new byte[512]; + for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes + + File tmp = new File(Environment.getDataDirectory(), "system/statsdperftest.tmp"); + FileOutputStream fos = null; + IOException error = null; + + long before = SystemClock.elapsedRealtime(); + try { + fos = new FileOutputStream(tmp); + fos.write(junk); + } catch (IOException e) { + error = e; + } finally { + try { + if (fos != null) fos.close(); + } catch (IOException e) { + // Do nothing. + } + } + + long latency = SystemClock.elapsedRealtime() - before; + if (tmp.exists()) tmp.delete(); + + if (error != null) { + Slog.e(TAG, "Error performing diskstats latency test"); + latency = -1; + } + // File based encryption. + boolean fileBased = StorageManager.isFileEncryptedNativeOnly(); + + //Recent disk write speed. Binder call to storaged. + int writeSpeed = -1; + IStoraged storaged = getIStoragedService(); + if (storaged == null) { + return StatsManager.PULL_SKIP; + } + try { + writeSpeed = storaged.getRecentPerf(); + } catch (RemoteException e) { + Slog.e(TAG, "storaged not found"); + } + + // Add info pulledData. + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(latency) + .writeBoolean(fileBased) + .writeInt(writeSpeed) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; + } + + private void registerDirectoryUsage() { + int tagId = StatsLog.DIRECTORY_USAGE; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullDirectoryUsage(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullDirectoryUsage(int atomTag, List<StatsEvent> pulledData) { + StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath()); + StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath()); + StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath()); + + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__DATA) + .writeLong(statFsData.getAvailableBytes()) + .writeLong(statFsData.getTotalBytes()) + .build(); + pulledData.add(e); + + e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE) + .writeLong(statFsCache.getAvailableBytes()) + .writeLong(statFsCache.getTotalBytes()) + .build(); + pulledData.add(e); + + e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM) + .writeLong(statFsSystem.getAvailableBytes()) + .writeLong(statFsSystem.getTotalBytes()) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; + } + + private void registerAppSize() { + int tagId = StatsLog.APP_SIZE; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullAppSize(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullAppSize(int atomTag, List<StatsEvent> pulledData) { + try { + String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH); + JSONObject json = new JSONObject(jsonStr); + long cache_time = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L); + JSONArray pkg_names = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY); + JSONArray app_sizes = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY); + JSONArray app_data_sizes = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY); + JSONArray app_cache_sizes = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY); + // Sanity check: Ensure all 4 lists have the same length. + int length = pkg_names.length(); + if (app_sizes.length() != length || app_data_sizes.length() != length + || app_cache_sizes.length() != length) { + Slog.e(TAG, "formatting error in diskstats cache file!"); + return StatsManager.PULL_SKIP; + } + for (int i = 0; i < length; i++) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeString(pkg_names.getString(i)) + .writeLong(app_sizes.optLong(i, /* fallback */ -1L)) + .writeLong(app_data_sizes.optLong(i, /* fallback */ -1L)) + .writeLong(app_cache_sizes.optLong(i, /* fallback */ -1L)) + .writeLong(cache_time) + .build(); + pulledData.add(e); + } + } catch (IOException | JSONException e) { + Slog.e(TAG, "exception reading diskstats cache file", e); + return StatsManager.PULL_SKIP; + } + return StatsManager.PULL_SUCCESS; + } + + private void registerCategorySize() { + int tagId = StatsLog.CATEGORY_SIZE; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullCategorySize(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullCategorySize(int atomTag, List<StatsEvent> pulledData) { + try { + String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH); + JSONObject json = new JSONObject(jsonStr); + long cacheTime = json.optLong( + DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, /* fallback */ -1L); + + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE) + .writeLong(json.optLong( + DiskStatsFileLogger.APP_SIZE_AGG_KEY, /* fallback */ -1L)) + .writeLong(cacheTime) + .build(); + pulledData.add(e); + + e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE) + .writeLong(json.optLong( + DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, /* fallback */ -1L)) + .writeLong(cacheTime) + .build(); + pulledData.add(e); + + e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE) + .writeLong(json.optLong( + DiskStatsFileLogger.APP_CACHE_AGG_KEY, /* fallback */ -1L)) + .writeLong(cacheTime) + .build(); + pulledData.add(e); + + e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS) + .writeLong(json.optLong( + DiskStatsFileLogger.PHOTOS_KEY, /* fallback */ -1L)) + .writeLong(cacheTime) + .build(); + pulledData.add(e); + + e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS) + .writeLong( + json.optLong(DiskStatsFileLogger.VIDEOS_KEY, /* fallback */ -1L)) + .writeLong(cacheTime) + .build(); + pulledData.add(e); + + e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__AUDIO) + .writeLong(json.optLong( + DiskStatsFileLogger.AUDIO_KEY, /* fallback */ -1L)) + .writeLong(cacheTime) + .build(); + pulledData.add(e); + + e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS) + .writeLong( + json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, /* fallback */ -1L)) + .writeLong(cacheTime) + .build(); + pulledData.add(e); + + e = StatsEvent.newBuilder() + .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM) + .writeLong(json.optLong( + DiskStatsFileLogger.SYSTEM_KEY, /* fallback */ -1L)) + .writeLong(cacheTime) + .build(); + pulledData.add(e); + + e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__OTHER) + .writeLong(json.optLong( + DiskStatsFileLogger.MISC_KEY, /* fallback */ -1L)) + .writeLong(cacheTime) + .build(); + pulledData.add(e); + } catch (IOException | JSONException e) { + Slog.e(TAG, "exception reading diskstats cache file", e); + return StatsManager.PULL_SKIP; + } + return StatsManager.PULL_SUCCESS; + } + + private void registerNumFingerprintsEnrolled() { + int tagId = StatsLog.NUM_FINGERPRINTS_ENROLLED; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullNumBiometricsEnrolled( + BiometricsProtoEnums.MODALITY_FINGERPRINT, atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private void registerNumFacesEnrolled() { + int tagId = StatsLog.NUM_FACES_ENROLLED; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullNumBiometricsEnrolled( + BiometricsProtoEnums.MODALITY_FACE, atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullNumBiometricsEnrolled(int modality, int atomTag, List<StatsEvent> pulledData) { + final PackageManager pm = mContext.getPackageManager(); + FingerprintManager fingerprintManager = null; + FaceManager faceManager = null; + + if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + fingerprintManager = mContext.getSystemService(FingerprintManager.class); + } + if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) { + faceManager = mContext.getSystemService(FaceManager.class); + } + + if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT && fingerprintManager == null) { + return StatsManager.PULL_SKIP; + } + if (modality == BiometricsProtoEnums.MODALITY_FACE && faceManager == null) { + return StatsManager.PULL_SKIP; + } + UserManager userManager = mContext.getSystemService(UserManager.class); + if (userManager == null) { + return StatsManager.PULL_SKIP; + } + + final long token = Binder.clearCallingIdentity(); + try { + for (UserInfo user : userManager.getUsers()) { + final int userId = user.getUserHandle().getIdentifier(); + int numEnrolled = 0; + if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) { + numEnrolled = fingerprintManager.getEnrolledFingerprints(userId).size(); + } else if (modality == BiometricsProtoEnums.MODALITY_FACE) { + numEnrolled = faceManager.getEnrolledFaces(userId).size(); + } else { + return StatsManager.PULL_SKIP; + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(userId) + .writeInt(numEnrolled) + .build(); + pulledData.add(e); + } + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerProcStats() { + // No op. + } + + private void pullProcStats() { + // No op. + } + + private void registerProcStatsPkgProc() { + // No op. + } + + private void pullProcStatsPkgProc() { + // No op. + } + + private StoragedUidIoStatsReader mStoragedUidIoStatsReader = + new StoragedUidIoStatsReader(); + + private void registerDiskIO() { + int tagId = StatsLog.DISK_IO; + PullAtomMetadata metadata = PullAtomMetadata.newBuilder() + .setAdditiveFields(new int[] {2, 3, 4, 5, 6, 7, 8, 9, 10, 11}) + .setCoolDownNs(3 * NS_PER_SEC) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + (atomTag, data) -> pullDiskIO(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullDiskIO(int atomTag, List<StatsEvent> pulledData) { + mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead, + fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite, + fgFsync, bgFsync) -> { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(uid) + .writeLong(fgCharsRead) + .writeLong(fgCharsWrite) + .writeLong(fgBytesRead) + .writeLong(fgBytesWrite) + .writeLong(bgCharsRead) + .writeLong(bgCharsWrite) + .writeLong(bgBytesRead) + .writeLong(bgBytesWrite) + .writeLong(fgFsync) + .writeLong(bgFsync) + .build(); + pulledData.add(e); + }); + return StatsManager.PULL_SUCCESS; + } + + private void registerPowerProfile() { + int tagId = StatsLog.POWER_PROFILE; + mStatsManager.registerPullAtomCallback( + tagId, + /* PullAtomMetadata */ null, + BackgroundThread.getExecutor(), + (atomTag, data) -> pullPowerProfile(atomTag, data) + ); + } + + private int pullPowerProfile(int atomTag, List<StatsEvent> pulledData) { + PowerProfile powerProfile = new PowerProfile(mContext); + ProtoOutputStream proto = new ProtoOutputStream(); + powerProfile.dumpDebug(proto); + proto.flush(); + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeByteArray(proto.getBytes()) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; + } + + private final Object mCpuTrackerLock = new Object(); + @GuardedBy("mCpuTrackerLock") + private ProcessCpuTracker mProcessCpuTracker; + + private void registerProcessCpuTime() { + int tagId = StatsLog.PROCESS_CPU_TIME; + // Min cool-down is 5 sec, inline with what ActivityManagerService uses. + PullAtomMetadata metadata = PullAtomMetadata.newBuilder() + .setCoolDownNs(5 * NS_PER_SEC) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + (atomTag, data) -> pullProcessCpuTime(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullProcessCpuTime(int atomTag, List<StatsEvent> pulledData) { + synchronized (mCpuTrackerLock) { + if (mProcessCpuTracker == null) { + mProcessCpuTracker = new ProcessCpuTracker(false); + mProcessCpuTracker.init(); + } + mProcessCpuTracker.update(); + for (int i = 0; i < mProcessCpuTracker.countStats(); i++) { + ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(st.uid) + .writeString(st.name) + .writeLong(st.base_utime) + .writeLong(st.base_stime) + .build(); + pulledData.add(e); + } + } + return StatsManager.PULL_SUCCESS; + } + + @Nullable + private KernelCpuThreadReaderDiff mKernelCpuThreadReader; + private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8; + + private void registerCpuTimePerThreadFreq() { + int tagId = StatsLog.CPU_TIME_PER_THREAD_FREQ; + PullAtomMetadata metadata = PullAtomMetadata.newBuilder() + .setAdditiveFields(new int[] {7, 9, 11, 13, 15, 17, 19, 21}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + (atomTag, data) -> pullCpuTimePerThreadFreq(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullCpuTimePerThreadFreq(int atomTag, List<StatsEvent> pulledData) { + if (this.mKernelCpuThreadReader == null) { + Slog.e(TAG, "mKernelCpuThreadReader is null"); + return StatsManager.PULL_SKIP; + } + ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages = + this.mKernelCpuThreadReader.getProcessCpuUsageDiffed(); + if (processCpuUsages == null) { + Slog.e(TAG, "processCpuUsages is null"); + return StatsManager.PULL_SKIP; + } + int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz(); + if (cpuFrequencies.length > CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES) { + String message = "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES + + " frequencies, but got " + cpuFrequencies.length; + Slog.w(TAG, message); + return StatsManager.PULL_SKIP; + } + for (int i = 0; i < processCpuUsages.size(); i++) { + KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i); + ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = + processCpuUsage.threadCpuUsages; + for (int j = 0; j < threadCpuUsages.size(); j++) { + KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j); + if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) { + String message = "Unexpected number of usage times," + + " expected " + cpuFrequencies.length + + " but got " + threadCpuUsage.usageTimesMillis.length; + Slog.w(TAG, message); + return StatsManager.PULL_SKIP; + } + + StatsEvent.Builder e = StatsEvent.newBuilder(); + e.setAtomId(atomTag); + e.writeInt(processCpuUsage.uid); + e.writeInt(processCpuUsage.processId); + e.writeInt(threadCpuUsage.threadId); + e.writeString(processCpuUsage.processName); + e.writeString(threadCpuUsage.threadName); + for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES; k++) { + if (k < cpuFrequencies.length) { + e.writeInt(cpuFrequencies[k]); + e.writeInt(threadCpuUsage.usageTimesMillis[k]); + } else { + // If we have no more frequencies to write, we still must write empty data. + // We know that this data is empty (and not just zero) because all + // frequencies are expected to be greater than zero + e.writeInt(0); + e.writeInt(0); + } + } + pulledData.add(e.build()); + } + } + return StatsManager.PULL_SUCCESS; + } + + // TODO: move to top of file when all migrations are complete + private BatteryStatsHelper mBatteryStatsHelper = null; + private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000; + private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS; + private static final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L; + + private BatteryStatsHelper getBatteryStatsHelper() { + if (mBatteryStatsHelper == null) { + final long callingToken = Binder.clearCallingIdentity(); + try { + // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly(). + mBatteryStatsHelper = new BatteryStatsHelper(mContext, false); + } finally { + Binder.restoreCallingIdentity(callingToken); + } + mBatteryStatsHelper.create((Bundle) null); + } + long currentTime = SystemClock.elapsedRealtime(); + if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) { + // Load BatteryStats and do all the calculations. + mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL); + // Calculations are done so we don't need to save the raw BatteryStats data in RAM. + mBatteryStatsHelper.clearStats(); + mBatteryStatsHelperTimestampMs = currentTime; + } + return mBatteryStatsHelper; + } + + private long milliAmpHrsToNanoAmpSecs(double mAh) { + return (long) (mAh * MILLI_AMP_HR_TO_NANO_AMP_SECS + 0.5); + } + + private void registerDeviceCalculatedPowerUse() { + int tagId = StatsLog.DEVICE_CALCULATED_POWER_USE; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullDeviceCalculatedPowerUse(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullDeviceCalculatedPowerUse(int atomTag, List<StatsEvent> pulledData) { + BatteryStatsHelper bsHelper = getBatteryStatsHelper(); + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower())) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; + } + + private void registerDeviceCalculatedPowerBlameUid() { + int tagId = StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullDeviceCalculatedPowerBlameUid(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullDeviceCalculatedPowerBlameUid(int atomTag, List<StatsEvent> pulledData) { + final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList(); + if (sippers == null) { + return StatsManager.PULL_SKIP; + } + + for (BatterySipper bs : sippers) { + if (bs.drainType != bs.drainType.APP) { + continue; + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(bs.uidObj.getUid()) + .writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah)) + .build(); + pulledData.add(e); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerDeviceCalculatedPowerBlameOther() { + int tagId = StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullDeviceCalculatedPowerBlameOther(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullDeviceCalculatedPowerBlameOther(int atomTag, List<StatsEvent> pulledData) { + final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList(); + if (sippers == null) { + return StatsManager.PULL_SKIP; + } + + for (BatterySipper bs : sippers) { + if (bs.drainType == bs.drainType.APP) { + continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid(). + } + if (bs.drainType == bs.drainType.USER) { + continue; // This is not supported. We purposefully calculate over USER_ALL. + } + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(bs.drainType.ordinal()) + .writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah)) + .build(); + pulledData.add(e); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerDebugElapsedClock() { + // No op. + } + + private void pullDebugElapsedClock() { + // No op. + } + + private void registerDebugFailingElapsedClock() { + // No op. + } + + private void pullDebugFailingElapsedClock() { + // No op. + } + + private void registerBuildInformation() { + int tagId = StatsLog.BUILD_INFORMATION; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + BackgroundThread.getExecutor(), + (atomTag, data) -> pullBuildInformation(atomTag, data) + ); + } + + private int pullBuildInformation(int atomTag, List<StatsEvent> pulledData) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeString(Build.FINGERPRINT) + .writeString(Build.BRAND) + .writeString(Build.PRODUCT) + .writeString(Build.DEVICE) + .writeString(Build.VERSION.RELEASE) + .writeString(Build.ID) + .writeString(Build.VERSION.INCREMENTAL) + .writeString(Build.TYPE) + .writeString(Build.TAGS) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; + } + + private void registerRoleHolder() { + int tagId = StatsLog.ROLE_HOLDER; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullRoleHolder(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + // Add a RoleHolder atom for each package that holds a role. + private int pullRoleHolder(int atomTag, List<StatsEvent> pulledData) { + long callingToken = Binder.clearCallingIdentity(); + try { + PackageManager pm = mContext.getPackageManager(); + RoleManagerInternal rmi = LocalServices.getService(RoleManagerInternal.class); + + List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers(); + + int numUsers = users.size(); + for (int userNum = 0; userNum < numUsers; userNum++) { + int userId = users.get(userNum).getUserHandle().getIdentifier(); + + ArrayMap<String, ArraySet<String>> roles = rmi.getRolesAndHolders(userId); + + int numRoles = roles.size(); + for (int roleNum = 0; roleNum < numRoles; roleNum++) { + String roleName = roles.keyAt(roleNum); + ArraySet<String> holders = roles.valueAt(roleNum); + + int numHolders = holders.size(); + for (int holderNum = 0; holderNum < numHolders; holderNum++) { + String holderName = holders.valueAt(holderNum); + + PackageInfo pkg; + try { + pkg = pm.getPackageInfoAsUser(holderName, 0, userId); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Role holder " + holderName + " not found"); + return StatsManager.PULL_SKIP; + } + + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(pkg.applicationInfo.uid) + .writeString(holderName) + .writeString(roleName) + .build(); + pulledData.add(e); + } + } + } + } finally { + Binder.restoreCallingIdentity(callingToken); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerDangerousPermissionState() { + int tagId = StatsLog.DANGEROUS_PERMISSION_STATE; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullDangerousPermissionState(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullDangerousPermissionState(int atomTag, List<StatsEvent> pulledData) { + final long token = Binder.clearCallingIdentity(); + Set<Integer> reportedUids = new HashSet<>(); + try { + PackageManager pm = mContext.getPackageManager(); + + List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers(); + + int numUsers = users.size(); + for (int userNum = 0; userNum < numUsers; userNum++) { + UserHandle user = users.get(userNum).getUserHandle(); + + List<PackageInfo> pkgs = pm.getInstalledPackagesAsUser( + PackageManager.GET_PERMISSIONS, user.getIdentifier()); + + int numPkgs = pkgs.size(); + for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) { + PackageInfo pkg = pkgs.get(pkgNum); + + if (pkg.requestedPermissions == null) { + continue; + } + + if (reportedUids.contains(pkg.applicationInfo.uid)) { + // do not report same uid twice + continue; + } + reportedUids.add(pkg.applicationInfo.uid); + + if (atomTag == StatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED + && ThreadLocalRandom.current().nextFloat() > 0.2f) { + continue; + } + + int numPerms = pkg.requestedPermissions.length; + for (int permNum = 0; permNum < numPerms; permNum++) { + String permName = pkg.requestedPermissions[permNum]; + + PermissionInfo permissionInfo; + int permissionFlags = 0; + try { + permissionInfo = pm.getPermissionInfo(permName, 0); + permissionFlags = + pm.getPermissionFlags(permName, pkg.packageName, user); + } catch (PackageManager.NameNotFoundException ignored) { + continue; + } + + if (permissionInfo.getProtection() != PROTECTION_DANGEROUS) { + continue; + } + + StatsEvent.Builder e = StatsEvent.newBuilder(); + e.setAtomId(atomTag); + e.writeString(permName); + e.writeInt(pkg.applicationInfo.uid); + if (atomTag == StatsLog.DANGEROUS_PERMISSION_STATE) { + e.writeString(""); + } + e.writeBoolean((pkg.requestedPermissionsFlags[permNum] + & REQUESTED_PERMISSION_GRANTED) != 0); + e.writeInt(permissionFlags); + + pulledData.add(e.build()); + } + } + } + } catch (Throwable t) { + Log.e(TAG, "Could not read permissions", t); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerTimeZoneDataInfo() { + int tagId = StatsLog.TIME_ZONE_DATA_INFO; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullTimeZoneDataInfo(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullTimeZoneDataInfo(int atomTag, List<StatsEvent> pulledData) { + String tzDbVersion = "Unknown"; + try { + tzDbVersion = android.icu.util.TimeZone.getTZDataVersion(); + } catch (MissingResourceException e) { + Slog.e(TAG, "Getting tzdb version failed: ", e); + return StatsManager.PULL_SKIP; + } + + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeString(tzDbVersion) + .build(); + pulledData.add(e); + return StatsManager.PULL_SUCCESS; + } + + private void registerExternalStorageInfo() { + int tagId = StatsLog.EXTERNAL_STORAGE_INFO; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullExternalStorageInfo(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullExternalStorageInfo(int atomTag, List<StatsEvent> pulledData) { + if (mStorageManager == null) { + return StatsManager.PULL_SKIP; + } + + List<VolumeInfo> volumes = mStorageManager.getVolumes(); + for (VolumeInfo vol : volumes) { + final String envState = VolumeInfo.getEnvironmentForState(vol.getState()); + final DiskInfo diskInfo = vol.getDisk(); + if (diskInfo != null && envState.equals(Environment.MEDIA_MOUNTED)) { + // Get the type of the volume, if it is adoptable or portable. + int volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__OTHER; + if (vol.getType() == TYPE_PUBLIC) { + volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PUBLIC; + } else if (vol.getType() == TYPE_PRIVATE) { + volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PRIVATE; + } + + // Get the type of external storage inserted in the device (sd cards, usb, etc.) + int externalStorageType; + if (diskInfo.isSd()) { + externalStorageType = StorageEnums.SD_CARD; + } else if (diskInfo.isUsb()) { + externalStorageType = StorageEnums.USB; + } else { + externalStorageType = StorageEnums.OTHER; + } + + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(externalStorageType) + .writeInt(volumeType) + .writeLong(diskInfo.size) + .build(); + pulledData.add(e); + } + } + return StatsManager.PULL_SUCCESS; + } + + private void registerAppsOnExternalStorageInfo() { + int tagId = StatsLog.APPS_ON_EXTERNAL_STORAGE_INFO; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullAppsOnExternalStorageInfo(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullAppsOnExternalStorageInfo(int atomTag, List<StatsEvent> pulledData) { + if (mStorageManager == null) { + return StatsManager.PULL_SKIP; + } + + PackageManager pm = mContext.getPackageManager(); + List<ApplicationInfo> apps = pm.getInstalledApplications(/*flags=*/ 0); + for (ApplicationInfo appInfo : apps) { + UUID storageUuid = appInfo.storageUuid; + if (storageUuid == null) { + continue; + } + + VolumeInfo volumeInfo = mStorageManager.findVolumeByUuid( + appInfo.storageUuid.toString()); + if (volumeInfo == null) { + continue; + } + + DiskInfo diskInfo = volumeInfo.getDisk(); + if (diskInfo == null) { + continue; + } + + int externalStorageType = -1; + if (diskInfo.isSd()) { + externalStorageType = StorageEnums.SD_CARD; + } else if (diskInfo.isUsb()) { + externalStorageType = StorageEnums.USB; + } else if (appInfo.isExternal()) { + externalStorageType = StorageEnums.OTHER; + } + + // App is installed on external storage. + if (externalStorageType != -1) { + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeInt(externalStorageType) + .writeString(appInfo.packageName) + .build(); + pulledData.add(e); + } + } + return StatsManager.PULL_SUCCESS; + } + + private void registerFaceSettings() { + // No op. + } + + private void pullRegisterFaceSettings() { + // No op. + } + + private void registerAppOps() { + int tagId = StatsLog.APP_OPS; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullAppOps(atomTag, data), + BackgroundThread.getExecutor() + ); + + } + + private int pullAppOps(int atomTag, List<StatsEvent> pulledData) { + long token = Binder.clearCallingIdentity(); + try { + AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); + + CompletableFuture<HistoricalOps> ops = new CompletableFuture<>(); + HistoricalOpsRequest histOpsRequest = + new HistoricalOpsRequest.Builder(0, Long.MAX_VALUE).build(); + appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete); + + HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, + TimeUnit.MILLISECONDS); + + for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) { + final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx); + final int uid = uidOps.getUid(); + for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) { + final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx); + for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) { + final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx); + + StatsEvent.Builder e = StatsEvent.newBuilder(); + e.setAtomId(atomTag); + e.writeInt(uid); + e.writeString(packageOps.getPackageName()); + e.writeInt(op.getOpCode()); + e.writeLong(op.getForegroundAccessCount(OP_FLAGS_ALL_TRUSTED)); + e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_ALL_TRUSTED)); + e.writeLong(op.getForegroundRejectCount(OP_FLAGS_ALL_TRUSTED)); + e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_ALL_TRUSTED)); + e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_ALL_TRUSTED)); + e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_ALL_TRUSTED)); + + String perm = AppOpsManager.opToPermission(op.getOpCode()); + if (perm == null) { + e.writeBoolean(false); + } else { + PermissionInfo permInfo; + try { + permInfo = mContext.getPackageManager().getPermissionInfo(perm, 0); + e.writeBoolean(permInfo.getProtection() == PROTECTION_DANGEROUS); + } catch (PackageManager.NameNotFoundException exception) { + e.writeBoolean(false); + } + } + + pulledData.add(e.build()); + } + } + } + } catch (Throwable t) { + // TODO: catch exceptions at a more granular level + Slog.e(TAG, "Could not read appops", t); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(token); + } + return StatsManager.PULL_SUCCESS; + } + + static void unpackStreamedData(int atomTag, List<StatsEvent> pulledData, + List<ParcelFileDescriptor> statsFiles) throws IOException { + InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0)); + int[] len = new int[1]; + byte[] stats = readFully(stream, len); + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeByteArray(Arrays.copyOf(stats, len[0])) + .build(); + pulledData.add(e); + } + + static byte[] readFully(InputStream stream, int[] outLen) throws IOException { + int pos = 0; + final int initialAvail = stream.available(); + byte[] data = new byte[initialAvail > 0 ? (initialAvail + 1) : 16384]; + while (true) { + int amt = stream.read(data, pos, data.length - pos); + if (DEBUG) { + Slog.i(TAG, "Read " + amt + " bytes at " + pos + " of avail " + data.length); + } + if (amt < 0) { + if (DEBUG) { + Slog.i(TAG, "**** FINISHED READING: pos=" + pos + " len=" + data.length); + } + outLen[0] = pos; + return data; + } + pos += amt; + if (pos >= data.length) { + byte[] newData = new byte[pos + 16384]; + if (DEBUG) { + Slog.i(TAG, "Copying " + pos + " bytes to new array len " + newData.length); + } + System.arraycopy(data, 0, newData, 0, pos); + data = newData; + } + } + } + + private void registerNotificationRemoteViews() { + int tagId = StatsLog.NOTIFICATION_REMOTE_VIEWS; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullNotificationRemoteViews(atomTag, data), + BackgroundThread.getExecutor() + ); + } + + private int pullNotificationRemoteViews(int atomTag, List<StatsEvent> pulledData) { + INotificationManager notificationManagerService = getINotificationManagerService(); + if (notificationManagerService == null) { + return StatsManager.PULL_SKIP; + } + final long callingToken = Binder.clearCallingIdentity(); + try { + // determine last pull tine. Copy file trick from pullProcessStats? + long wallClockNanos = SystemClock.currentTimeMicro() * 1000L; + long lastNotificationStatsNs = wallClockNanos - + TimeUnit.NANOSECONDS.convert(1, TimeUnit.DAYS); + + List<ParcelFileDescriptor> statsFiles = new ArrayList<>(); + notificationManagerService.pullStats(lastNotificationStatsNs, + NotificationManagerService.REPORT_REMOTE_VIEWS, true, statsFiles); + if (statsFiles.size() != 1) { + return StatsManager.PULL_SKIP; + } + unpackStreamedData(atomTag, pulledData, statsFiles); + } catch (IOException e) { + Slog.e(TAG, "Getting notistats failed: ", e); + return StatsManager.PULL_SKIP; + } catch (RemoteException e) { + Slog.e(TAG, "Getting notistats failed: ", e); + return StatsManager.PULL_SKIP; + } catch (SecurityException e) { + Slog.e(TAG, "Getting notistats failed: ", e); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(callingToken); + } + return StatsManager.PULL_SUCCESS; + } + + private void registerDangerousPermissionStateSampled() { + int tagId = StatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED; + mStatsManager.registerPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + (atomTag, data) -> pullDangerousPermissionState(atomTag, data), + BackgroundThread.getExecutor() + ); + } +} diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 3f3408f4d202..b596d2a207f0 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1680,6 +1680,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (root == this) { task.setRootProcess(proc); } + proc.addActivityIfNeeded(this); } boolean hasProcess() { @@ -6918,7 +6919,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Update last reported values. final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration(); - setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig); + setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig); if (mState == INITIALIZING) { // No need to relaunch or schedule new config for activity that hasn't been launched @@ -7017,6 +7018,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return true; } + /** Get process configuration, or global config if the process is not set. */ + private Configuration getProcessGlobalConfiguration() { + return app != null ? app.getConfiguration() : mAtmService.getGlobalConfiguration(); + } + /** * When assessing a configuration change, decide if the changes flags and the new configurations * should cause the Activity to relaunch. @@ -7129,7 +7135,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A startRelaunching(); final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults, pendingNewIntents, configChangeFlags, - new MergedConfiguration(mAtmService.getGlobalConfiguration(), + new MergedConfiguration(getProcessGlobalConfiguration(), getMergedOverrideConfiguration()), preserveWindow); final ActivityLifecycleItem lifecycleItem; diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index aa90248b97f8..0a68408d49a5 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -848,8 +848,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r); - proc.addActivityIfNeeded(r); - final LockTaskController lockTaskController = mService.getLockTaskController(); if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index e308f6b8f3ce..47e8b87dcf08 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -6743,7 +6743,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return; } process.mIsImeProcess = true; - process.registerDisplayConfigurationListenerLocked(displayContent); + process.registerDisplayConfigurationListener(displayContent); } } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 1e60ce83b5fb..7b23e2d383b6 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -134,8 +134,8 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { resolveOverrideConfiguration(newParentConfig); mFullConfiguration.setTo(newParentConfig); mFullConfiguration.updateFrom(mResolvedOverrideConfiguration); + onMergedOverrideConfigurationChanged(); if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) { - onMergedOverrideConfigurationChanged(); // This depends on the assumption that change-listeners don't do // their own override resolution. This way, dependent hierarchies // can stay properly synced-up with a primary hierarchy's constraints. @@ -147,6 +147,10 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { mResolvedOverrideConfiguration); } } + for (int i = mChangeListeners.size() - 1; i >= 0; --i) { + mChangeListeners.get(i).onMergedOverrideConfigurationChanged( + mMergedOverrideConfiguration); + } if (forwardToChildren) { for (int i = getChildCount() - 1; i >= 0; --i) { final ConfigurationContainer child = getChildAt(i); @@ -545,6 +549,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { } mChangeListeners.add(listener); listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration); + listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration); } void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) { diff --git a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java index dc4939d55bfa..3d84e1752e6a 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java @@ -24,5 +24,8 @@ import android.content.res.Configuration; public interface ConfigurationContainerListener { /** {@see ConfigurationContainer#onRequestedOverrideConfigurationChanged} */ - void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration); + default void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {} + + /** Called when new merged override configuration is reported. */ + default void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfiguration) {} } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 9a40b1b1a74a..ceb38f7d9789 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.os.Build.VERSION_CODES.Q; import static android.view.Display.INVALID_DISPLAY; @@ -178,8 +179,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // Last configuration that was reported to the process. private final Configuration mLastReportedConfiguration; + private final Configuration mNewOverrideConfig = new Configuration(); // Registered display id as a listener to override config change private int mDisplayId; + private ActivityRecord mConfigActivityRecord; /** Whether our process is currently running a {@link RecentsAnimation} */ private boolean mRunningRecentsAnimation; @@ -327,6 +330,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mDisplayId != INVALID_DISPLAY; } + /** @return {@code true} if the process registered to an activity as a config listener. */ + @VisibleForTesting + boolean registeredForActivityConfigChanges() { + return mConfigActivityRecord != null; + } + void postPendingUiCleanMsg(boolean pendingUiClean) { if (mListener == null) return; // Posting on handler so WM lock isn't held when we call into AM. @@ -483,7 +492,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio @Override protected ConfigurationContainer getParent() { - return null; + // Returning RootWindowContainer as the parent, so that this process controller always + // has full configuration and overrides (e.g. from display) are always added on top of + // global config. + return mAtm.mRootWindowContainer; } @HotPath(caller = HotPath.PROCESS_CHANGE) @@ -507,10 +519,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return; } mActivities.add(r); + updateActivityConfigurationListener(); } void removeActivity(ActivityRecord r) { mActivities.remove(r); + updateActivityConfigurationListener(); } void makeFinishingForProcessRemoved() { @@ -521,6 +535,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio void clearActivities() { mActivities.clear(); + updateActivityConfigurationListener(); } @HotPath(caller = HotPath.OOM_ADJUSTMENT) @@ -964,19 +979,20 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mAtm.mH.sendMessage(m); } - void registerDisplayConfigurationListenerLocked(DisplayContent displayContent) { + void registerDisplayConfigurationListener(DisplayContent displayContent) { if (displayContent == null) { return; } - // A process can only register to one display to listener to the override configuration + // A process can only register to one display to listen to the override configuration // change. Unregister existing listener if it has one before register the new one. - unregisterDisplayConfigurationListenerLocked(); + unregisterDisplayConfigurationListener(); + unregisterActivityConfigurationListener(); mDisplayId = displayContent.mDisplayId; displayContent.registerConfigurationChangeListener(this); } @VisibleForTesting - void unregisterDisplayConfigurationListenerLocked() { + void unregisterDisplayConfigurationListener() { if (mDisplayId == INVALID_DISPLAY) { return; } @@ -986,6 +1002,48 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio displayContent.unregisterConfigurationChangeListener(this); } mDisplayId = INVALID_DISPLAY; + onMergedOverrideConfigurationChanged(Configuration.EMPTY); + } + + private void registerActivityConfigurationListener(ActivityRecord activityRecord) { + if (activityRecord == null) { + return; + } + // A process can only register to one activityRecord to listen to the override configuration + // change. Unregister existing listener if it has one before register the new one. + unregisterDisplayConfigurationListener(); + unregisterActivityConfigurationListener(); + mConfigActivityRecord = activityRecord; + activityRecord.registerConfigurationChangeListener(this); + } + + private void unregisterActivityConfigurationListener() { + if (mConfigActivityRecord == null) { + return; + } + mConfigActivityRecord.unregisterConfigurationChangeListener(this); + mConfigActivityRecord = null; + onMergedOverrideConfigurationChanged(Configuration.EMPTY); + } + + /** + * Check if activity configuration override for the activity process needs an update and perform + * if needed. By default we try to override the process configuration to match the top activity + * config to increase app compatibility with multi-window and multi-display. The process will + * always track the configuration of the non-finishing activity last added to the process. + */ + private void updateActivityConfigurationListener() { + for (int i = mActivities.size() - 1; i >= 0; i--) { + final ActivityRecord activityRecord = mActivities.get(i); + if (!activityRecord.finishing && !activityRecord.containsListener(this)) { + // Eligible activity is found, update listener. + registerActivityConfigurationListener(activityRecord); + return; + } + } + + // No eligible activities found, let's remove the configuration listener. + unregisterActivityConfigurationListener(); } @Override @@ -995,8 +1053,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } @Override - public void onRequestedOverrideConfigurationChanged(Configuration newOverrideConfig) { - super.onRequestedOverrideConfigurationChanged(newOverrideConfig); + public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) { + // Make sure that we don't accidentally override the activity type. + mNewOverrideConfig.setTo(mergedOverrideConfig); + mNewOverrideConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); + super.onRequestedOverrideConfigurationChanged(mNewOverrideConfig); updateConfiguration(); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 36e9273a0904..a429741607e4 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3402,7 +3402,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP "Reporting new frame to %s: %s", this, mWindowFrames.mCompatFrame); final MergedConfiguration mergedConfiguration = - new MergedConfiguration(mWmService.mRoot.getConfiguration(), + new MergedConfiguration(getProcessGlobalConfiguration(), getMergedOverrideConfiguration()); setLastReportedMergedConfiguration(mergedConfiguration); diff --git a/services/core/jni/com_android_server_net_NetworkStatsFactory.cpp b/services/core/jni/com_android_server_net_NetworkStatsFactory.cpp index 9cd743b3466a..8b6526ff49f0 100644 --- a/services/core/jni/com_android_server_net_NetworkStatsFactory.cpp +++ b/services/core/jni/com_android_server_net_NetworkStatsFactory.cpp @@ -20,6 +20,7 @@ #include <inttypes.h> #include <sys/stat.h> #include <sys/types.h> +#include <vector> #include <jni.h> diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index b8b0dbf9157f..af57c29fda82 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2556,7 +2556,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } for (final String restriction : doAdmin.userRestrictions.keySet()) { if (UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(restriction)) { - parentAdmin.userRestrictions.putBoolean( + parentAdmin.ensureUserRestrictions().putBoolean( restriction, doAdmin.userRestrictions.getBoolean(restriction)); } } @@ -7983,12 +7983,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return false; } - if (parent) { - ActiveAdmin ap = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA, parent); - enforceProfileOwnerOfOrganizationOwnedDevice(ap); - } synchronized (getLockObject()) { + if (parent) { + final ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA, parent); + enforceProfileOwnerOfOrganizationOwnedDevice(ap); + } if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); return (admin != null) ? admin.disableCamera : false; @@ -15051,4 +15051,29 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slog.d(LOG_TAG, message); } } + + @Override + public void setCommonCriteriaModeEnabled(ComponentName admin, boolean enabled) { + synchronized (getLockObject()) { + getActiveAdminForCallerLocked(admin, + DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER); + } + mInjector.binderWithCleanCallingIdentity( + () -> mInjector.settingsGlobalPutInt(Settings.Global.COMMON_CRITERIA_MODE, + enabled ? 1 : 0)); + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.SET_COMMON_CRITERIA_MODE) + .setAdmin(admin) + .setBoolean(enabled) + .write(); + } + + @Override + public boolean isCommonCriteriaModeEnabled(ComponentName admin) { + synchronized (getLockObject()) { + getActiveAdminForCallerLocked(admin, + DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER); + } + return mInjector.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0) != 0; + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 3dee913ac035..92bdba03fa09 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -218,13 +218,19 @@ public final class SystemServer { private static final String STATS_COMPANION_LIFECYCLE_CLASS = "com.android.server.stats.StatsCompanion$Lifecycle"; private static final String STATS_PULL_ATOM_SERVICE_CLASS = - "com.android.server.stats.StatsPullAtomService"; + "com.android.server.stats.pull.StatsPullAtomService"; private static final String USB_SERVICE_CLASS = "com.android.server.usb.UsbService$Lifecycle"; private static final String MIDI_SERVICE_CLASS = "com.android.server.midi.MidiService$Lifecycle"; + private static final String WIFI_APEX_SERVICE_JAR_PATH = + "/apex/com.android.wifi/javalib/wifi-service.jar"; private static final String WIFI_SERVICE_CLASS = "com.android.server.wifi.WifiService"; + private static final String WIFI_SCANNING_SERVICE_CLASS = + "com.android.server.wifi.scanner.WifiScanningService"; + private static final String WIFI_RTT_SERVICE_CLASS = + "com.android.server.wifi.rtt.RttService"; private static final String WIFI_AWARE_SERVICE_CLASS = "com.android.server.wifi.aware.WifiAwareService"; private static final String WIFI_P2P_SERVICE_CLASS = @@ -1426,33 +1432,36 @@ public final class SystemServer { PackageManager.FEATURE_WIFI)) { // Wifi Service must be started first for wifi-related services. t.traceBegin("StartWifi"); - mSystemServiceManager.startService(WIFI_SERVICE_CLASS); + mSystemServiceManager.startServiceFromJar( + WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); t.traceEnd(); t.traceBegin("StartWifiScanning"); - mSystemServiceManager.startService( - "com.android.server.wifi.scanner.WifiScanningService"); + mSystemServiceManager.startServiceFromJar( + WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); t.traceEnd(); } if (context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_RTT)) { t.traceBegin("StartRttService"); - mSystemServiceManager.startService( - "com.android.server.wifi.rtt.RttService"); + mSystemServiceManager.startServiceFromJar( + WIFI_RTT_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); t.traceEnd(); } if (context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_AWARE)) { t.traceBegin("StartWifiAware"); - mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS); + mSystemServiceManager.startServiceFromJar( + WIFI_AWARE_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); t.traceEnd(); } if (context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_DIRECT)) { t.traceBegin("StartWifiP2P"); - mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS); + mSystemServiceManager.startServiceFromJar( + WIFI_P2P_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); t.traceEnd(); } diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp index 3ce514a56b60..d2f86eeabb47 100644 --- a/services/robotests/Android.bp +++ b/services/robotests/Android.bp @@ -43,6 +43,9 @@ android_robolectric_test { "platform-test-annotations", "testng", ], + static_libs: [ + "androidx.test.ext.truth", + ], instrumentation_for: "FrameworksServicesLib", } diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp index 9d384e90d253..ef0ca66610c9 100644 --- a/services/robotests/backup/Android.bp +++ b/services/robotests/backup/Android.bp @@ -44,8 +44,10 @@ android_robolectric_test { // Include the testing libraries libs: [ + "mockito-robolectric-prebuilt", "platform-test-annotations", "testng", + "truth-prebuilt", ], instrumentation_for: "BackupFrameworksServicesLib", diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java new file mode 100644 index 000000000000..ce0886435906 --- /dev/null +++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java @@ -0,0 +1,464 @@ +/* + * 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.pm; + +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES; +import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY; +import static android.content.pm.CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; + +import android.Manifest; +import android.annotation.UserIdInt; +import android.app.ActivityManagerInternal; +import android.app.AppOpsManager; +import android.app.AppOpsManager.Mode; +import android.app.admin.DevicePolicyManagerInternal; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.pm.parsing.AndroidPackage; +import android.content.pm.parsing.PackageImpl; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.server.LocalServices; +import com.android.server.testing.shadows.ShadowApplicationPackageManager; +import com.android.server.testing.shadows.ShadowUserManager; +import com.android.server.wm.ActivityTaskManagerInternal; + +import com.google.android.collect.Lists; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** Unit tests for {@link CrossProfileAppsServiceImpl}. */ +@RunWith(RobolectricTestRunner.class) +@Presubmit +@Config(shadows = {ShadowUserManager.class, ShadowApplicationPackageManager.class}) +public class CrossProfileAppsServiceImplRoboTest { + private static final int CALLING_UID = 1111; + private static final String CROSS_PROFILE_APP_PACKAGE_NAME = + "com.android.server.pm.crossprofileappsserviceimplrobotest.crossprofileapp"; + private static final int PERSONAL_PROFILE_USER_ID = 0; + private static final int PERSONAL_PROFILE_UID = 2222; + private static final int WORK_PROFILE_USER_ID = 10; + private static final int WORK_PROFILE_UID = 3333; + private static final int OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID = 20; + + private final ContextWrapper mContext = ApplicationProvider.getApplicationContext(); + private final UserManager mUserManager = mContext.getSystemService(UserManager.class); + private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + private final PackageManager mPackageManager = mContext.getPackageManager(); + private final TestInjector mInjector = new TestInjector(); + private final CrossProfileAppsServiceImpl mCrossProfileAppsServiceImpl = + new CrossProfileAppsServiceImpl(mContext, mInjector); + private final Map<UserHandle, Set<Intent>> mSentUserBroadcasts = new HashMap<>(); + + @Mock private PackageManagerInternal mPackageManagerInternal; + @Mock private IPackageManager mIPackageManager; + @Mock private DevicePolicyManagerInternal mDevicePolicyManagerInternal; + + @Before + public void initializeMocks() throws Exception { + MockitoAnnotations.initMocks(this); + mockCrossProfileAppInstalledAndEnabledOnEachProfile(); + mockCrossProfileAppRequestsInteractAcrossProfiles(); + mockCrossProfileAppWhitelisted(); + } + + private void mockCrossProfileAppInstalledAndEnabledOnEachProfile() { + // They are enabled by default, so we simply have to ensure that a package info with an + // application info is returned. + final PackageInfo packageInfo = buildTestPackageInfo(); + when(mPackageManagerInternal.getPackageInfo( + eq(CROSS_PROFILE_APP_PACKAGE_NAME), + /* flags= */ anyInt(), + /* filterCallingUid= */ anyInt(), + eq(PERSONAL_PROFILE_USER_ID))) + .thenReturn(packageInfo); + when(mPackageManagerInternal.getPackageInfo( + eq(CROSS_PROFILE_APP_PACKAGE_NAME), + /* flags= */ anyInt(), + /* filterCallingUid= */ anyInt(), + eq(WORK_PROFILE_USER_ID))) + .thenReturn(packageInfo); + mockCrossProfileAndroidPackage(PackageImpl.forParsing(CROSS_PROFILE_APP_PACKAGE_NAME)); + } + + private PackageInfo buildTestPackageInfo() { + PackageInfo packageInfo = new PackageInfo(); + packageInfo.applicationInfo = new ApplicationInfo(); + return packageInfo; + } + + private void mockCrossProfileAppRequestsInteractAcrossProfiles() throws Exception { + final String permissionName = Manifest.permission.INTERACT_ACROSS_PROFILES; + when(mIPackageManager.getAppOpPermissionPackages(permissionName)) + .thenReturn(new String[] {CROSS_PROFILE_APP_PACKAGE_NAME}); + } + + private void mockCrossProfileAppWhitelisted() { + when(mDevicePolicyManagerInternal.getAllCrossProfilePackages()) + .thenReturn(Lists.newArrayList(CROSS_PROFILE_APP_PACKAGE_NAME)); + } + + @Before + public void setUpCrossProfileAppUidsAndPackageNames() { + ShadowApplicationPackageManager.setPackageUidAsUser( + CROSS_PROFILE_APP_PACKAGE_NAME, PERSONAL_PROFILE_UID, PERSONAL_PROFILE_USER_ID); + ShadowApplicationPackageManager.setPackageUidAsUser( + CROSS_PROFILE_APP_PACKAGE_NAME, WORK_PROFILE_UID, WORK_PROFILE_USER_ID); + } + + @Before + public void grantPermissions() { + grantPermissions( + Manifest.permission.MANAGE_APP_OPS_MODES, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_USERS_FULL); + } + + @Before + public void setUpProfiles() { + final ShadowUserManager shadowUserManager = Shadow.extract(mUserManager); + shadowUserManager.addProfileIds( + PERSONAL_PROFILE_USER_ID, + WORK_PROFILE_USER_ID, + OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID); + } + + @Before + public void setInteractAcrossProfilesAppOpDefault() { + // It seems to be necessary to provide the shadow with the default already specified in + // AppOpsManager. + final int defaultMode = AppOpsManager.opToDefaultMode(OP_INTERACT_ACROSS_PROFILES); + explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, defaultMode); + explicitlySetInteractAcrossProfilesAppOp(WORK_PROFILE_UID, defaultMode); + } + + @Test + public void setInteractAcrossProfilesAppOp_missingInteractAcrossUsersAndFull_throwsSecurityException() { + denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS); + denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); + try { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + fail(); + } catch (SecurityException expected) {} + } + + @Test + public void setInteractAcrossProfilesAppOp_missingManageAppOpsModes_throwsSecurityException() { + denyPermissions(Manifest.permission.MANAGE_APP_OPS_MODES); + try { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + fail(); + } catch (SecurityException expected) {} + } + + @Test + public void setInteractAcrossProfilesAppOp_setsAppOp() { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_setsAppOpWithUsersAndWithoutFull() { + denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS_FULL); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_setsAppOpWithFullAndWithoutUsers() { + denyPermissions(Manifest.permission.INTERACT_ACROSS_USERS); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_setsAppOpOnOtherProfile() { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(getCrossProfileAppOp(WORK_PROFILE_UID)).isEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_sendsBroadcast() { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isTrue(); + } + + @Test + public void setInteractAcrossProfilesAppOp_sendsBroadcastToOtherProfile() { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedCanInteractAcrossProfilesChangedBroadcast(WORK_PROFILE_USER_ID)) + .isTrue(); + } + + @Test + public void setInteractAcrossProfilesAppOp_doesNotSendBroadcastToProfileWithoutPackage() { + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedCanInteractAcrossProfilesChangedBroadcast( + OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID)) + .isFalse(); + } + + @Test + public void setInteractAcrossProfilesAppOp_toSameAsCurrent_doesNotSendBroadcast() { + explicitlySetInteractAcrossProfilesAppOp(MODE_ALLOWED); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isFalse(); + } + + @Test + public void setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSet() { + mockCrossProfileAppNotWhitelisted(); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(getCrossProfileAppOp()).isNotEqualTo(MODE_ALLOWED); + } + + @Test + public void setInteractAcrossProfilesAppOp_toAllowed_whenNotAbleToRequest_doesNotSendBroadcast() { + mockCrossProfileAppNotWhitelisted(); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedCanInteractAcrossProfilesChangedBroadcast()).isFalse(); + } + + @Test + public void setInteractAcrossProfilesAppOp_withoutCrossProfileAttribute_manifestReceiversDoNotGetBroadcast() { + declareCrossProfileAttributeOnCrossProfileApp(false); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isFalse(); + } + + @Test + public void setInteractAcrossProfilesAppOp_withCrossProfileAttribute_manifestReceiversGetBroadcast() { + declareCrossProfileAttributeOnCrossProfileApp(true); + mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( + CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); + assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isTrue(); + } + + private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) { + explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode); + } + + private void explicitlySetInteractAcrossProfilesAppOp(int uid, @Mode int mode) { + shadowOf(mAppOpsManager).setMode( + OP_INTERACT_ACROSS_PROFILES, uid, CROSS_PROFILE_APP_PACKAGE_NAME, mode); + } + + private void grantPermissions(String... permissions) { + shadowOf(mContext).grantPermissions(Process.myPid(), CALLING_UID, permissions); + } + + private void denyPermissions(String... permissions) { + shadowOf(mContext).denyPermissions(Process.myPid(), CALLING_UID, permissions); + } + + + private @Mode int getCrossProfileAppOp() { + return getCrossProfileAppOp(PERSONAL_PROFILE_UID); + } + + private @Mode int getCrossProfileAppOp(int uid) { + return mAppOpsManager.unsafeCheckOpNoThrow( + AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES), + uid, + CROSS_PROFILE_APP_PACKAGE_NAME); + } + + private boolean receivedCanInteractAcrossProfilesChangedBroadcast() { + return receivedCanInteractAcrossProfilesChangedBroadcast(PERSONAL_PROFILE_USER_ID); + } + + private boolean receivedCanInteractAcrossProfilesChangedBroadcast(@UserIdInt int userId) { + final UserHandle userHandle = UserHandle.of(userId); + if (!mSentUserBroadcasts.containsKey(userHandle)) { + return false; + } + return mSentUserBroadcasts.get(userHandle) + .stream() + .anyMatch(this::isBroadcastCanInteractAcrossProfilesChanged); + } + + private boolean isBroadcastCanInteractAcrossProfilesChanged(Intent intent) { + return intent.getAction().equals(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED) + && CROSS_PROFILE_APP_PACKAGE_NAME.equals(intent.getPackage()); + } + + private void mockCrossProfileAndroidPackage(AndroidPackage androidPackage) { + when(mPackageManagerInternal.getPackage(CROSS_PROFILE_APP_PACKAGE_NAME)) + .thenReturn(androidPackage); + when(mPackageManagerInternal.getPackage(PERSONAL_PROFILE_UID)).thenReturn(androidPackage); + when(mPackageManagerInternal.getPackage(WORK_PROFILE_UID)).thenReturn(androidPackage); + } + + private void mockCrossProfileAppNotWhitelisted() { + when(mDevicePolicyManagerInternal.getAllCrossProfilePackages()) + .thenReturn(new ArrayList<>()); + } + + private boolean receivedManifestCanInteractAcrossProfilesChangedBroadcast() { + final UserHandle userHandle = UserHandle.of(PERSONAL_PROFILE_USER_ID); + if (!mSentUserBroadcasts.containsKey(userHandle)) { + return false; + } + return mSentUserBroadcasts.get(userHandle) + .stream() + .anyMatch(this::isBroadcastManifestCanInteractAcrossProfilesChanged); + } + + private boolean isBroadcastManifestCanInteractAcrossProfilesChanged(Intent intent) { + // The manifest check is negative since the FLAG_RECEIVER_REGISTERED_ONLY flag means that + // manifest receivers can NOT receive the broadcast. + return isBroadcastCanInteractAcrossProfilesChanged(intent) + && (intent.getFlags() & FLAG_RECEIVER_REGISTERED_ONLY) == 0; + } + + private void declareCrossProfileAttributeOnCrossProfileApp(boolean value) { + mockCrossProfileAndroidPackage( + PackageImpl.forParsing(CROSS_PROFILE_APP_PACKAGE_NAME).setCrossProfile(value)); + } + + private class TestInjector implements CrossProfileAppsServiceImpl.Injector { + + @Override + public int getCallingUid() { + return CALLING_UID; + } + + @Override + public @UserIdInt int getCallingUserId() { + return PERSONAL_PROFILE_USER_ID; + } + + @Override + public UserHandle getCallingUserHandle() { + return UserHandle.of(getCallingUserId()); + } + + @Override + public long clearCallingIdentity() { + return 0; + } + + @Override + public void restoreCallingIdentity(long token) {} + + @Override + public UserManager getUserManager() { + return mUserManager; + } + + @Override + public PackageManagerInternal getPackageManagerInternal() { + return mPackageManagerInternal; + } + + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + + @Override + public AppOpsManager getAppOpsManager() { + return mAppOpsManager; + } + + @Override + public ActivityManagerInternal getActivityManagerInternal() { + return LocalServices.getService(ActivityManagerInternal.class); + } + + @Override + public ActivityTaskManagerInternal getActivityTaskManagerInternal() { + return LocalServices.getService(ActivityTaskManagerInternal.class); + } + + @Override + public IPackageManager getIPackageManager() { + return mIPackageManager; + } + + @Override + public DevicePolicyManagerInternal getDevicePolicyManagerInternal() { + return mDevicePolicyManagerInternal; + } + + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user) { + // Robolectric's shadows do not currently support sendBroadcastAsUser. + final Set<Intent> broadcasts = + mSentUserBroadcasts.containsKey(user) + ? mSentUserBroadcasts.get(user) + : new HashSet<>(); + broadcasts.add(intent); + mSentUserBroadcasts.put(user, broadcasts); + mContext.sendBroadcastAsUser(intent, user); + } + + @Override + public int checkComponentPermission( + String permission, int uid, int owningUid, boolean exported) { + // ActivityManager#checkComponentPermission calls through to + // AppGlobals.getPackageManager()#checkUidPermission, which calls through to + // ShadowActivityThread with Robolectric. This method is currently not supported there. + return mContext.checkPermission(permission, Process.myPid(), uid); + } + } +} diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java index ab121eddff06..1443eabf07d5 100644 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java @@ -26,6 +26,7 @@ import org.robolectric.annotation.Implements; import org.robolectric.annotation.Resetter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -39,6 +40,7 @@ public class ShadowApplicationPackageManager private static final Map<String, PackageInfo> sPackageInfos = new ArrayMap<>(); private static final List<PackageInfo> sInstalledPackages = new ArrayList<>(); private static final Map<String, Integer> sPackageUids = new ArrayMap<>(); + private static final Map<Integer, Map<String, Integer>> sUserPackageUids = new ArrayMap<>(); /** * Registers the package {@code packageName} to be returned when invoking {@link @@ -58,6 +60,19 @@ public class ShadowApplicationPackageManager sPackageUids.put(packageName, packageUid); } + /** + * Sets the package uid {@code packageUid} for the package {@code packageName} to be returned + * when invoking {@link ApplicationPackageManager#getPackageUidAsUser(String, int, int)}. + */ + public static void setPackageUidAsUser(String packageName, int packageUid, int userId) { + final Map<String, Integer> userPackageUids = + sUserPackageUids.containsKey(userId) + ? sUserPackageUids.get(userId) + : new HashMap<>(); + userPackageUids.put(packageName, packageUid); + sUserPackageUids.put(userId, userPackageUids); + } + @Override protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) throws NameNotFoundException { @@ -75,6 +90,10 @@ public class ShadowApplicationPackageManager @Override protected int getPackageUidAsUser(String packageName, int flags, int userId) throws NameNotFoundException { + if (sUserPackageUids.containsKey(userId) + && sUserPackageUids.get(userId).containsKey(packageName)) { + return sUserPackageUids.get(userId).get(packageName); + } if (!sPackageUids.containsKey(packageName)) { throw new NameNotFoundException(packageName); } diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowUserManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowUserManager.java index c6ae1a1b6863..a9e4ee521f90 100644 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowUserManager.java +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowUserManager.java @@ -16,18 +16,46 @@ package com.android.server.testing.shadows; +import android.annotation.NonNull; import android.annotation.UserIdInt; import android.os.UserManager; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + /** Shadow for {@link UserManager}. */ @Implements(UserManager.class) public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager { + private final Map<Integer, Set<Integer>> profileIds = new HashMap<>(); + /** @see UserManager#isUserUnlocked() */ @Implementation public boolean isUserUnlocked(@UserIdInt int userId) { return false; } + + /** @see UserManager#getProfileIds(int, boolean) () */ + @Implementation + @NonNull + public int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) { + // Currently, enabledOnly is ignored. + if (!profileIds.containsKey(userId)) { + return new int[] {userId}; + } + return profileIds.get(userId).stream().mapToInt(Number::intValue).toArray(); + } + + /** Add a collection of profile IDs, all within the same profile group. */ + public void addProfileIds(@UserIdInt int... userIds) { + final Set<Integer> profileGroup = new HashSet<>(); + for (int userId : userIds) { + profileGroup.add(userId); + profileIds.put(userId, profileGroup); + } + } } diff --git a/services/tests/servicestests/res/raw/comp_policies_primary.xml b/services/tests/servicestests/res/raw/comp_policies_primary.xml index 1e1a0eff874c..d30f479195e3 100644 --- a/services/tests/servicestests/res/raw/comp_policies_primary.xml +++ b/services/tests/servicestests/res/raw/comp_policies_primary.xml @@ -3,5 +3,6 @@ <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"> <policies flags="991"/> <password-history-length value="33" /> + <user-restrictions no_bluetooth="true" /> </admin> </policies> diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index e1e9b7e7b7cb..cf10559c8198 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -495,6 +495,13 @@ public class AbstractAccessibilityServiceConnectionTest { } @Test + public void getSystemActions() { + List<AccessibilityNodeInfo.AccessibilityAction> actions = + mServiceConnection.getSystemActions(); + verify(mMockSystemActionPerformer).getSystemActions(); + } + + @Test public void isFingerprintGestureDetectionAvailable_hasFingerPrintSupport_returnTrue() { when(mMockFingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable()) .thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 75239db92121..ec928fb278be 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -16,16 +16,28 @@ package com.android.server.accessibility; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.Manifest; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.IAccessibilityServiceClient; import android.app.PendingIntent; import android.app.RemoteAction; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.graphics.drawable.Icon; +import android.os.IBinder; +import android.os.UserHandle; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; @@ -34,6 +46,7 @@ import androidx.test.InstrumentationRegistry; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener; +import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -59,6 +72,14 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION = new AccessibilityAction(ACTION_ID, LABEL); + static final ComponentName COMPONENT_NAME = new ComponentName( + "com.android.server.accessibility", "AccessibilityManagerServiceTest"); + static final int SERVICE_ID = 42; + + @Mock private Context mMockContext; + @Mock private AccessibilityServiceInfo mMockServiceInfo; + @Mock private ResolveInfo mMockResolveInfo; + @Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport; @Mock private PackageManager mMockPackageManager; @Mock private WindowManagerInternal mMockWindowManagerService; @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy; @@ -66,7 +87,12 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { @Mock private AccessibilityWindowManager mMockA11yWindowManager; @Mock private AccessibilityDisplayListener mMockA11yDisplayListener; @Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal; + @Mock private IBinder mMockBinder; + @Mock private IAccessibilityServiceClient mMockServiceClient; + private AccessibilityUserState mUserState; + private MessageCapturingHandler mHandler = new MessageCapturingHandler(null); + private AccessibilityServiceConnection mAccessibilityServiceConnection; private AccessibilityManagerService mA11yms; @Override @@ -74,9 +100,11 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { MockitoAnnotations.initMocks(this); LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); - LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerService); + LocalServices.addService( + WindowManagerInternal.class, mMockWindowManagerService); LocalServices.addService( ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal); + mA11yms = new AccessibilityManagerService( InstrumentationRegistry.getContext(), mMockPackageManager, @@ -86,6 +114,35 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { mMockA11yDisplayListener); } + private void setupAccessibilityServiceConnection() { + when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn( + InstrumentationRegistry.getContext().getSystemService( + Context.DISPLAY_SERVICE)); + mUserState = new AccessibilityUserState(UserHandle.USER_SYSTEM, mMockContext, mA11yms); + + when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo); + mMockResolveInfo.serviceInfo = mock(ServiceInfo.class); + mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class); + + when(mMockBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient); + mAccessibilityServiceConnection = new AccessibilityServiceConnection( + mUserState, + mMockContext, + COMPONENT_NAME, + mMockServiceInfo, + SERVICE_ID, + mHandler, + new Object(), + mMockSecurityPolicy, + mMockSystemSupport, + mMockWindowManagerService, + mMockSystemActionPerformer, + mMockA11yWindowManager, + mMockActivityTaskManagerInternal); + mAccessibilityServiceConnection.bindLocked(); + mAccessibilityServiceConnection.onServiceConnected(COMPONENT_NAME, mMockBinder); + } + @SmallTest public void testRegisterSystemActionWithoutPermission() throws Exception { doThrow(SecurityException.class).when(mMockSecurityPolicy).enforceCallingPermission( @@ -125,4 +182,11 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { mA11yms.unregisterSystemAction(ACTION_ID); verify(mMockSystemActionPerformer).unregisterSystemAction(ACTION_ID); } + + @SmallTest + public void testOnSystemActionsChanged() throws Exception { + setupAccessibilityServiceConnection(); + mA11yms.notifySystemActionsChangedLocked(mUserState); + verify(mMockServiceClient).onSystemActionsChanged(); + } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java new file mode 100644 index 000000000000..09466e7dd668 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.magnification; + + +import static org.mockito.Mockito.verify; + +import android.os.RemoteException; +import android.view.Display; +import android.view.accessibility.IWindowMagnificationConnection; +import android.view.accessibility.IWindowMagnificationConnectionCallback; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for WindowMagnificationConnectionWrapper. We don't test {@code + * WindowMagnificationConnectionWrapper#linkToDeath(IBinder.DeathRecipient)} since it's tested in + * {@link WindowMagnificationManagerTest}. + */ +public class WindowMagnificationConnectionWrapperTest { + + private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; + + @Mock + private IWindowMagnificationConnection mConnection; + @Mock + private IWindowMagnificationConnectionCallback mCallback; + private WindowMagnificationConnectionWrapper mConnectionWrapper; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection); + } + + @Test + public void enableWindowMagnification() throws RemoteException { + mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f); + verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f); + } + + @Test + public void setScale() throws RemoteException { + mConnectionWrapper.setScale(TEST_DISPLAY, 3.0f); + verify(mConnection).setScale(TEST_DISPLAY, 3.0f); + } + + @Test + public void disableWindowMagnification() throws RemoteException { + mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY); + verify(mConnection).disableWindowMagnification(TEST_DISPLAY); + } + + @Test + public void moveWindowMagnifier() throws RemoteException { + mConnectionWrapper.moveWindowMagnifier(0, 100, 150); + verify(mConnection).moveWindowMagnifier(0, 100, 150); + } + + @Test + public void setMirrorWindowCallback() throws RemoteException { + mConnectionWrapper.setConnectionCallback(mCallback); + verify(mConnection).setConnectionCallback(mCallback); + } + +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java new file mode 100644 index 000000000000..780a6c08687a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.magnification; + + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; + +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.view.accessibility.IWindowMagnificationConnection; +import android.view.accessibility.IWindowMagnificationConnectionCallback; + +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for WindowMagnificationManager. + */ +public class WindowMagnificationManagerTest { + + private MockWindowMagnificationConnection mMockConnection; + private WindowMagnificationManager mWindowMagnificationManager; + + @Before + public void setUp() { + mMockConnection = new MockWindowMagnificationConnection(); + mWindowMagnificationManager = new WindowMagnificationManager(); + } + + @Test + public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + assertNotNull(mWindowMagnificationManager.mConnectionWrapper); + verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0)); + } + + @Test + public void setConnection_connectionIsNull_setMirrorWindowCallbackAndHasWrapper() + throws RemoteException { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + + assertNotNull(mWindowMagnificationManager.mConnectionWrapper); + verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0)); + verify(mMockConnection.getConnection()).setConnectionCallback( + any(IWindowMagnificationConnectionCallback.class)); + } + + @Test + public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + + mMockConnection.getDeathRecipient().binderDied(); + + assertNull(mWindowMagnificationManager.mConnectionWrapper); + verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), + 0); + } + + /** + * This test simulates {@link WindowMagnificationManager#setConnection} is called by thread A + * and then the former connection is called by thread B. In this situation we should keep the + * new connection. + */ + @Test + public void + setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath() { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + MockWindowMagnificationConnection secondConnection = + new MockWindowMagnificationConnection(); + + mWindowMagnificationManager.setConnection(secondConnection.getConnection()); + mMockConnection.getDeathRecipient().binderDied(); + + assertNotNull(mWindowMagnificationManager.mConnectionWrapper); + verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0); + verify(secondConnection.asBinder(), never()).unlinkToDeath( + secondConnection.getDeathRecipient(), 0); + } + + @Test + public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + + mWindowMagnificationManager.setConnection(null); + + assertNull(mWindowMagnificationManager.mConnectionWrapper); + verify(mMockConnection.getConnection()).setConnectionCallback(null); + } + + private static class MockWindowMagnificationConnection { + + private final IWindowMagnificationConnection mConnection; + private final Binder mBinder; + private IBinder.DeathRecipient mDeathRecipient; + + MockWindowMagnificationConnection() { + mConnection = mock(IWindowMagnificationConnection.class); + mBinder = mock(Binder.class); + when(mConnection.asBinder()).thenReturn(mBinder); + doAnswer((invocation) -> { + mDeathRecipient = invocation.getArgument(0); + return null; + }).when(mBinder).linkToDeath( + any(IBinder.DeathRecipient.class), eq(0)); + } + + IWindowMagnificationConnection getConnection() { + return mConnection; + } + + public IBinder.DeathRecipient getDeathRecipient() { + return mDeathRecipient; + } + + Binder asBinder() { + return mBinder; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java index 41956794aaf2..6b0f557ad9ed 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java @@ -101,7 +101,9 @@ public class AppSearchImplTest { IllegalStateException e = expectThrows( IllegalStateException.class, () -> impl.setSchema( - /*callingUid=*/Integer.MAX_VALUE, SchemaProto.getDefaultInstance())); + /*callingUid=*/Integer.MAX_VALUE, + SchemaProto.getDefaultInstance(), + /*force=*/false)); assertThat(e).hasMessageThat().contains("Failed to look up package name"); } } diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java index ecd07bdc4544..b14291b34195 100644 --- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java @@ -188,7 +188,7 @@ public class OverrideValidatorImplTest { } @Test - public void getOverrideAllowedState_betaBuildEnabledChangeDebugApp_rejectOverride() + public void getOverrideAllowedState_betaBuildEnabledChangeDebugApp_allowOverride() throws Exception { CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) .addEnabledChangeWithId(1).build(); @@ -203,11 +203,11 @@ public class OverrideValidatorImplTest { overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); assertThat(allowedState) - .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1)); + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); } @Test - public void getOverrideAllowedState_betaBuildDisabledChangeDebugApp_rejectOverride() + public void getOverrideAllowedState_betaBuildDisabledChangeDebugApp_allowOverride() throws Exception { CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) .addDisabledChangeWithId(1).build(); @@ -221,7 +221,7 @@ public class OverrideValidatorImplTest { overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); assertThat(allowedState) - .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1)); + .isEqualTo(new OverrideAllowedState(ALLOWED, -1, -1)); } @Test 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 46b83713c159..9574a086c74b 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -371,10 +371,16 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID); runAsCaller(poContext, dpms, dpm -> { - // Check that DO policy is now set on parent instance. - assertEquals(33, dpm.getParentProfileInstance(admin1).getPasswordHistoryLength(admin1)); - // And NOT set on profile instance. - assertEquals(0, dpm.getPasswordHistoryLength(admin1)); + 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("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)); // TODO(b/143516163): verify more policies. }); 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 bfadeea40034..632a2c1edfae 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -5721,6 +5721,31 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.getAllCrossProfilePackages()); } + public void testSetCommonCriteriaMode_asDeviceOwner() throws Exception { + setDeviceOwner(); + + dpm.setCommonCriteriaModeEnabled(admin1, true); + verify(getServices().settings).settingsGlobalPutInt( + Settings.Global.COMMON_CRITERIA_MODE, 1); + + when(getServices().settings.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0)) + .thenReturn(1); + assertTrue(dpm.isCommonCriteriaModeEnabled(admin1)); + } + + public void testSetCommonCriteriaMode_asPoOfOrgOwnedDevice() throws Exception { + setupProfileOwner(); + configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE); + + dpm.setCommonCriteriaModeEnabled(admin1, true); + verify(getServices().settings).settingsGlobalPutInt( + Settings.Global.COMMON_CRITERIA_MODE, 1); + + when(getServices().settings.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0)) + .thenReturn(1); + assertTrue(dpm.isCommonCriteriaModeEnabled(admin1)); + } + private void setCrossProfileAppsList(String... packages) { when(mContext.getResources() .getStringArray(eq(R.array.cross_profile_apps))) diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 1f660742122d..9e98427db709 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.hdmi; import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM; import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK; +import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; import static com.google.common.truth.Truth.assertThat; @@ -25,8 +26,17 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.ContextWrapper; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; +import android.os.IPowerManager; import android.os.Looper; +import android.os.PowerManager; +import android.os.RemoteException; import android.os.test.TestLooper; import androidx.test.InstrumentationRegistry; @@ -36,6 +46,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.ArrayList; @@ -98,6 +110,7 @@ public class HdmiControlServiceTest { } private static final String TAG = "HdmiControlServiceTest"; + private Context mContextSpy; private HdmiControlService mHdmiControlService; private HdmiCecController mHdmiCecController; private HdmiCecLocalDeviceMyDevice mMyAudioSystemDevice; @@ -109,15 +122,24 @@ public class HdmiControlServiceTest { private boolean mStandbyMessageReceived; private HdmiPortInfo[] mHdmiPortInfo; + @Mock private IPowerManager mIPowerManagerMock; + @Before - public void SetUp() { - mHdmiControlService = - new HdmiControlService(InstrumentationRegistry.getTargetContext()) { - @Override - boolean isStandbyMessageReceived() { - return mStandbyMessageReceived; - } - }; + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); + + PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, null); + when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager); + when(mIPowerManagerMock.isInteractive()).thenReturn(true); + + mHdmiControlService = new HdmiControlService(mContextSpy) { + @Override + boolean isStandbyMessageReceived() { + return mStandbyMessageReceived; + } + }; mMyLooper = mTestLooper.getLooper(); mMyAudioSystemDevice = @@ -198,4 +220,33 @@ public class HdmiControlServiceTest { mHdmiControlService.initPortInfo(); assertThat(mHdmiControlService.pathToPortId(0x1000)).isEqualTo(Constants.INVALID_PORT_ID); } + + @Test + public void initialPowerStatus_normalBoot_isTransientToStandby() { + assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo( + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY); + } + + @Test + public void initialPowerStatus_quiescentBoot_isTransientToStandby() throws RemoteException { + when(mIPowerManagerMock.isInteractive()).thenReturn(false); + assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo( + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY); + } + + @Test + public void powerStatusAfterBootComplete_normalBoot_isOn() { + mHdmiControlService.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON); + mHdmiControlService.onBootPhase(PHASE_BOOT_COMPLETED); + assertThat(mHdmiControlService.getPowerStatus()).isEqualTo( + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON); + } + + @Test + public void powerStatusAfterBootComplete_quiescentBoot_isStandby() throws RemoteException { + when(mIPowerManagerMock.isInteractive()).thenReturn(false); + mHdmiControlService.onBootPhase(PHASE_BOOT_COMPLETED); + assertThat(mHdmiControlService.getPowerStatus()).isEqualTo( + HdmiControlManager.POWER_STATUS_STANDBY); + } } diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java index 8ee5f5fb9de8..e5cbeee2860d 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java @@ -30,6 +30,8 @@ import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BIT import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY; import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE; import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY; +import static com.android.server.integrity.serializer.RuleBinarySerializer.INDEXED_RULE_SIZE_LIMIT; +import static com.android.server.integrity.serializer.RuleBinarySerializer.NONINDEXED_RULE_SIZE_LIMIT; import static com.android.server.integrity.utils.TestUtils.getBits; import static com.android.server.integrity.utils.TestUtils.getBytes; import static com.android.server.integrity.utils.TestUtils.getValueBits; @@ -112,8 +114,7 @@ public class RuleBinarySerializerTest { assertExpectException( RuleSerializeException.class, - /* expectedExceptionMessageRegex= */ "Index buckets cannot be created for null" - + " rule list.", + /* expectedExceptionMessageRegex= */ "Null rules cannot be serialized.", () -> binarySerializer.serialize(null, /* formatVersion= */ Optional.empty())); } @@ -203,8 +204,8 @@ public class RuleBinarySerializerTest { + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32) + SERIALIZED_END_INDEXING_KEY + getBits( - DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length, - /* numOfBits= */ 32); + DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length, + /* numOfBits= */ 32); expectedIndexingOutputStream.write( getBytes( expectedIndexingBitsForIndexed @@ -618,6 +619,131 @@ public class RuleBinarySerializerTest { .isEqualTo(expectedIndexingOutputStream.toByteArray()); } + @Test + public void testBinaryString_totalRuleSizeLimitReached() { + int ruleCount = INDEXED_RULE_SIZE_LIMIT - 1; + String packagePrefix = "package.name."; + String appCertificatePrefix = "app.cert."; + String installerNamePrefix = "installer."; + + // Create the rule set with more rules than the system can handle in total. + List<Rule> ruleList = new ArrayList(); + for (int count = 0; count < ruleCount; count++) { + ruleList.add( + getRuleWithPackageNameAndSampleInstallerName( + String.format("%s%04d", packagePrefix, count))); + } + for (int count = 0; count < ruleCount; count++) { + ruleList.add( + getRuleWithAppCertificateAndSampleInstallerName( + String.format("%s%04d", appCertificatePrefix, count))); + } + for (int count = 0; count < NONINDEXED_RULE_SIZE_LIMIT - 1; count++) { + ruleList.add( + getNonIndexedRuleWithInstallerName( + String.format("%s%04d", installerNamePrefix, count))); + } + + // Serialize the rules. + ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream(); + ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream(); + RuleSerializer binarySerializer = new RuleBinarySerializer(); + + assertExpectException( + RuleSerializeException.class, + "Too many rules provided", + () -> + binarySerializer.serialize( + ruleList, + /* formatVersion= */ Optional.empty(), + ruleOutputStream, + indexingOutputStream)); + } + + @Test + public void testBinaryString_tooManyPackageNameIndexedRules() { + String packagePrefix = "package.name."; + + // Create a rule set with too many package name indexed rules. + List<Rule> ruleList = new ArrayList(); + for (int count = 0; count < INDEXED_RULE_SIZE_LIMIT + 1; count++) { + ruleList.add( + getRuleWithPackageNameAndSampleInstallerName( + String.format("%s%04d", packagePrefix, count))); + } + + // Serialize the rules. + ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream(); + ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream(); + RuleSerializer binarySerializer = new RuleBinarySerializer(); + + assertExpectException( + RuleSerializeException.class, + "Too many rules provided in the indexing group.", + () -> + binarySerializer.serialize( + ruleList, + /* formatVersion= */ Optional.empty(), + ruleOutputStream, + indexingOutputStream)); + } + + @Test + public void testBinaryString_tooManyAppCertificateIndexedRules() { + String appCertificatePrefix = "app.cert."; + + // Create a rule set with too many app certificate indexed rules. + List<Rule> ruleList = new ArrayList(); + for (int count = 0; count < INDEXED_RULE_SIZE_LIMIT + 1; count++) { + ruleList.add( + getRuleWithAppCertificateAndSampleInstallerName( + String.format("%s%04d", appCertificatePrefix, count))); + } + + // Serialize the rules. + ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream(); + ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream(); + RuleSerializer binarySerializer = new RuleBinarySerializer(); + + assertExpectException( + RuleSerializeException.class, + "Too many rules provided in the indexing group.", + () -> + binarySerializer.serialize( + ruleList, + /* formatVersion= */ Optional.empty(), + ruleOutputStream, + indexingOutputStream)); + } + + @Test + public void testBinaryString_tooManyNonIndexedRules() { + String installerNamePrefix = "installer."; + + // Create a rule set with too many unindexed rules. + List<Rule> ruleList = new ArrayList(); + for (int count = 0; count < NONINDEXED_RULE_SIZE_LIMIT + 1; count++) { + ruleList.add( + getNonIndexedRuleWithInstallerName( + String.format("%s%04d", installerNamePrefix, count))); + } + + // Serialize the rules. + ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream(); + ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream(); + RuleSerializer binarySerializer = new RuleBinarySerializer(); + + assertExpectException( + RuleSerializeException.class, + "Too many rules provided in the indexing group.", + () -> + binarySerializer.serialize( + ruleList, + /* formatVersion= */ Optional.empty(), + ruleOutputStream, + indexingOutputStream)); + } + private Rule getRuleWithPackageNameAndSampleInstallerName(String packageName) { return new Rule( new CompoundFormula( diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java index e18ff61e32dc..68f60b4ff67a 100644 --- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java @@ -13,14 +13,17 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; +import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.IApplicationThread; +import android.app.admin.DevicePolicyManagerInternal; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; @@ -76,6 +79,10 @@ public class CrossProfileAppsServiceImplTest { private ActivityManagerInternal mActivityManagerInternal; @Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal; + @Mock + private IPackageManager mIPackageManager; + @Mock + private DevicePolicyManagerInternal mDevicePolicyManagerInternal; private TestInjector mTestInjector; private ActivityInfo mActivityInfo; @@ -578,5 +585,26 @@ public class CrossProfileAppsServiceImplTest { public ActivityTaskManagerInternal getActivityTaskManagerInternal() { return mActivityTaskManagerInternal; } + + @Override + public IPackageManager getIPackageManager() { + return mIPackageManager; + } + + @Override + public DevicePolicyManagerInternal getDevicePolicyManagerInternal() { + return mDevicePolicyManagerInternal; + } + + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user) { + mContext.sendBroadcastAsUser(intent, user); + } + + @Override + public int checkComponentPermission( + String permission, int uid, int owningUid, boolean exported) { + return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported); + } } } diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 13643a09daaa..25cef56cd21e 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -28,6 +28,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; @@ -754,4 +755,55 @@ public class PowerManagerServiceTest { SystemClock.sleep(11); assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); } + + @Test + public void testBoot_ShouldBeAwake() throws Exception { + createService(); + startSystem(); + + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE); + verify(mNotifierMock, never()).onWakefulnessChangeStarted(anyInt(), anyInt(), anyLong()); + } + + @Test + public void testBoot_DesiredScreenPolicyShouldBeBright() throws Exception { + createService(); + startSystem(); + + assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + DisplayPowerRequest.POLICY_BRIGHT); + } + + @Test + public void testQuiescentBoot_ShouldBeAsleep() throws Exception { + when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1"); + createService(); + startSystem(); + + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + verify(mNotifierMock).onWakefulnessChangeStarted(eq(WAKEFULNESS_ASLEEP), anyInt(), + anyLong()); + } + + @Test + public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() throws Exception { + when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1"); + createService(); + assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + DisplayPowerRequest.POLICY_OFF); + + startSystem(); + assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + DisplayPowerRequest.POLICY_OFF); + } + + @Test + public void testQuiescentBoot_WakeUp_DesiredScreenPolicyShouldBeBright() throws Exception { + when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1"); + createService(); + startSystem(); + forceAwake(); + assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + DisplayPowerRequest.POLICY_BRIGHT); + } } diff --git a/services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java b/services/tests/servicestests/src/com/android/server/stats/pull/IonMemoryUtilTest.java index 8cbf8e56edeb..d4d4b4d6a8c9 100644 --- a/services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java +++ b/services/tests/servicestests/src/com/android/server/stats/pull/IonMemoryUtilTest.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.stats; +package com.android.server.stats.pull; -import static com.android.server.stats.IonMemoryUtil.parseIonHeapSizeFromDebugfs; -import static com.android.server.stats.IonMemoryUtil.parseProcessIonHeapSizesFromDebugfs; +import static com.android.server.stats.pull.IonMemoryUtil.parseIonHeapSizeFromDebugfs; +import static com.android.server.stats.pull.IonMemoryUtil.parseProcessIonHeapSizesFromDebugfs; import static com.google.common.truth.Truth.assertThat; import androidx.test.filters.SmallTest; -import com.android.server.stats.IonMemoryUtil.IonAllocations; +import com.android.server.stats.pull.IonMemoryUtil.IonAllocations; import org.junit.Test; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java index f5af3ec76788..d16c232afea9 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java @@ -18,6 +18,7 @@ package com.android.server.notification; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; +import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; @@ -30,6 +31,7 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.app.NotificationChannel; import android.os.UserHandle; +import android.provider.Settings; import android.service.notification.StatusBarNotification; import com.android.server.UiServiceTestCase; @@ -52,6 +54,7 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { public void testExtractsUpdatedChannel() { NotificationChannelExtractor extractor = new NotificationChannelExtractor(); extractor.setConfig(mConfig); + extractor.initialize(mContext, null); NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW); final Notification.Builder builder = new Notification.Builder(getContext()) @@ -71,4 +74,62 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { assertNull(extractor.process(r)); assertEquals(updatedChannel, r.getChannel()); } + + @Test + public void testInvalidShortcutFlagEnabled_looksUpCorrectChannel() { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true"); + + NotificationChannelExtractor extractor = new NotificationChannelExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW); + final Notification.Builder builder = new Notification.Builder(getContext()) + .setContentTitle("foo") + .setStyle(new Notification.MessagingStyle("name")) + .setSmallIcon(android.R.drawable.sym_def_app_icon); + Notification n = builder.build(); + StatusBarNotification sbn = new StatusBarNotification("", "", 0, "tag", 0, + 0, n, UserHandle.ALL, null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + + NotificationChannel updatedChannel = + new NotificationChannel("a", "", IMPORTANCE_HIGH); + when(mConfig.getConversationNotificationChannel( + any(), anyInt(), eq("a"), eq(r.sbn.getShortcutId(mContext)), eq(true), eq(false))) + .thenReturn(updatedChannel); + + assertNull(extractor.process(r)); + assertEquals(updatedChannel, r.getChannel()); + } + + @Test + public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + + NotificationChannelExtractor extractor = new NotificationChannelExtractor(); + extractor.setConfig(mConfig); + extractor.initialize(mContext, null); + + NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW); + final Notification.Builder builder = new Notification.Builder(getContext()) + .setContentTitle("foo") + .setStyle(new Notification.MessagingStyle("name")) + .setSmallIcon(android.R.drawable.sym_def_app_icon); + Notification n = builder.build(); + StatusBarNotification sbn = new StatusBarNotification("", "", 0, "tag", 0, + 0, n, UserHandle.ALL, null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + + NotificationChannel updatedChannel = + new NotificationChannel("a", "", IMPORTANCE_HIGH); + when(mConfig.getConversationNotificationChannel( + any(), anyInt(), eq("a"), eq(null), eq(true), eq(false))) + .thenReturn(updatedChannel); + + assertNull(extractor.process(r)); + assertEquals(updatedChannel, r.getChannel()); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java index 3b6a4bde8e20..b5eb1bf31848 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java @@ -40,8 +40,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.UiServiceTestCase; +import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -115,6 +115,11 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase { mHistoryManager.onBootPhaseAppsCanStart(); } + @After + public void tearDown() { + mHistoryManager.onDestroy(); + } + @Test public void testOnUserUnlocked() { assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isFalse(); @@ -126,22 +131,52 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase { } @Test - @Ignore("b/147012298") public void testOnUserUnlocked_historyDisabled() { + // create a history + mHistoryManager.onUserUnlocked(USER_SYSTEM); + assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isTrue(); + // lock user + mHistoryManager.onUserStopped(USER_SYSTEM); + + // turn off history Settings.Secure.putIntForUser(getContext().getContentResolver(), Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, USER_SYSTEM); mHistoryManager.mSettingsObserver.update(null, USER_SYSTEM); - assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isFalse(); - assertThat(mHistoryManager.isUserUnlocked(USER_SYSTEM)).isFalse(); + // unlock user, verify that history is disabled mHistoryManager.onUserUnlocked(USER_SYSTEM); assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isFalse(); - assertThat(mHistoryManager.isUserUnlocked(USER_SYSTEM)).isFalse(); verify(mDb, times(1)).disableHistory(); } @Test + public void testOnUserUnlocked_historyDisabledThenEnabled() { + // create a history + mHistoryManager.onUserUnlocked(USER_SYSTEM); + assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isTrue(); + + // lock user + mHistoryManager.onUserStopped(USER_SYSTEM); + + // turn off history + Settings.Secure.putIntForUser(getContext().getContentResolver(), + Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, USER_SYSTEM); + mHistoryManager.mSettingsObserver.update(null, USER_SYSTEM); + + // turn on history + Settings.Secure.putIntForUser(getContext().getContentResolver(), + Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 1, USER_SYSTEM); + mHistoryManager.mSettingsObserver.update(null, USER_SYSTEM); + + // unlock user, verify that history is NOT disabled + mHistoryManager.onUserUnlocked(USER_SYSTEM); + + assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isTrue(); + verify(mDb, never()).disableHistory(); + } + + @Test public void testOnUserUnlocked_cleansUpRemovedPackages() { String pkg = "pkg"; mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg); @@ -223,6 +258,8 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase { @Test public void testOnPackageRemoved_historyDisabled() { + mHistoryManager.onUserUnlocked(USER_SYSTEM); + Settings.Secure.putIntForUser(getContext().getContentResolver(), Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, USER_SYSTEM); mHistoryManager.mSettingsObserver.update(null, USER_SYSTEM); @@ -427,6 +464,8 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase { public void testIsHistoryEnabled() { assertThat(mHistoryManager.isHistoryEnabled(USER_SYSTEM)).isTrue(); + mHistoryManager.onUserUnlocked(USER_SYSTEM); + Settings.Secure.putIntForUser(getContext().getContentResolver(), Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, USER_SYSTEM); mHistoryManager.mSettingsObserver.update(null, USER_SYSTEM); 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 2ac464251c73..c6c64c9e0f6b 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -5834,7 +5834,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList( orig))); - mBinderService.createConversationNotificationChannelForPackage(PKG, mUid, orig, "friend"); + mBinderService.createConversationNotificationChannelForPackage( + PKG, mUid, "key", orig, "friend"); NotificationChannel friendChannel = mBinderService.getConversationNotificationChannel( PKG, 0, PKG, original.getId(), false, "friend"); @@ -5869,9 +5870,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { String conversationId = "friend"; mBinderService.createConversationNotificationChannelForPackage( - PKG, mUid, NotificationChannel.CREATOR.createFromParcel(msgParcel), conversationId); + PKG, mUid, "key", NotificationChannel.CREATOR.createFromParcel(msgParcel), + conversationId); mBinderService.createConversationNotificationChannelForPackage( - PKG, mUid, NotificationChannel.CREATOR.createFromParcel(callParcel), + PKG, mUid, "key", NotificationChannel.CREATOR.createFromParcel(callParcel), conversationId); NotificationChannel messagesChild = mBinderService.getConversationNotificationChannel( diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 6f1657441fb5..c1c74da50c7c 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -22,6 +22,7 @@ import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MAX; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; +import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ; import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT; @@ -2858,4 +2859,84 @@ public class PreferencesHelperTest extends UiServiceTestCase { // good } } + + @Test + public void testPlaceholderConversationId_flagOn() throws Exception { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true"); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" + + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>" + + "</package>" + + "</ranking>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true)); + } + + @Test + public void testPlaceholderConversationId_flagOff() throws Exception { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" + + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>" + + "</package>" + + "</ranking>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true)); + } + + @Test + public void testNormalConversationId_flagOff() throws Exception { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" + + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"other\"/>" + + "</package>" + + "</ranking>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true)); + } + + @Test + public void testNoConversationId_flagOff() throws Exception { + Settings.Global.putString( + mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); + + final String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" + + "<channel id=\"id\" name=\"hi\" importance=\"3\"/>" + + "</package>" + + "</ranking>"; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), + null); + parser.nextTag(); + mHelper.readXml(parser, false, UserHandle.USER_ALL); + + assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true)); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index a3e94599cad3..ad63d078fa67 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; @@ -56,6 +58,7 @@ 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; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -1175,4 +1178,160 @@ public class ActivityRecordTests extends ActivityTestsBase { verify(mActivity).removeFromHistory(anyString()); } + + @Test + public void testActivityOverridesProcessConfig() { + final WindowProcessController wpc = mActivity.app; + assertTrue(wpc.registeredForActivityConfigChanges()); + assertFalse(wpc.registeredForDisplayConfigChanges()); + + final ActivityRecord secondaryDisplayActivity = + createActivityOnDisplay(false /* defaultDisplay */, null /* process */); + + assertTrue(wpc.registeredForActivityConfigChanges()); + assertEquals(0, mActivity.getMergedOverrideConfiguration() + .diff(wpc.getRequestedOverrideConfiguration())); + assertNotEquals(mActivity.getConfiguration(), + secondaryDisplayActivity.getConfiguration()); + } + + @Test + public void testActivityOverridesProcessConfig_TwoActivities() { + final WindowProcessController wpc = mActivity.app; + assertTrue(wpc.registeredForActivityConfigChanges()); + + final Task firstTaskRecord = mActivity.getTask(); + final ActivityRecord secondActivityRecord = + new ActivityBuilder(mService).setTask(firstTaskRecord).setUseProcess(wpc).build(); + + assertTrue(wpc.registeredForActivityConfigChanges()); + assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration() + .diff(wpc.getRequestedOverrideConfiguration())); + } + + @Test + public void testActivityOverridesProcessConfig_TwoActivities_SecondaryDisplay() { + final WindowProcessController wpc = mActivity.app; + assertTrue(wpc.registeredForActivityConfigChanges()); + + final ActivityRecord secondActivityRecord = + new ActivityBuilder(mService).setTask(mTask).setUseProcess(wpc).build(); + + assertTrue(wpc.registeredForActivityConfigChanges()); + assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration() + .diff(wpc.getRequestedOverrideConfiguration())); + } + + @Test + public void testActivityOverridesProcessConfig_TwoActivities_DifferentTasks() { + final WindowProcessController wpc = mActivity.app; + assertTrue(wpc.registeredForActivityConfigChanges()); + + final ActivityRecord secondActivityRecord = + createActivityOnDisplay(true /* defaultDisplay */, wpc); + + assertTrue(wpc.registeredForActivityConfigChanges()); + assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration() + .diff(wpc.getRequestedOverrideConfiguration())); + } + + @Test + public void testActivityOnDifferentDisplayUpdatesProcessOverride() { + final ActivityRecord secondaryDisplayActivity = + createActivityOnDisplay(false /* defaultDisplay */, null /* process */); + final WindowProcessController wpc = secondaryDisplayActivity.app; + assertTrue(wpc.registeredForActivityConfigChanges()); + + final ActivityRecord secondActivityRecord = + createActivityOnDisplay(true /* defaultDisplay */, wpc); + + assertTrue(wpc.registeredForActivityConfigChanges()); + assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration() + .diff(wpc.getRequestedOverrideConfiguration())); + assertFalse(wpc.registeredForDisplayConfigChanges()); + } + + @Test + public void testActivityReparentChangesProcessOverride() { + final WindowProcessController wpc = mActivity.app; + final Task initialTask = mActivity.getTask(); + final Configuration initialConf = + new Configuration(mActivity.getMergedOverrideConfiguration()); + assertEquals(0, mActivity.getMergedOverrideConfiguration() + .diff(wpc.getRequestedOverrideConfiguration())); + assertTrue(wpc.registeredForActivityConfigChanges()); + + // Create a new task with custom config to reparent the activity to. + final Task newTask = + new TaskBuilder(mSupervisor).setStack(initialTask.getStack()).build(); + final Configuration newConfig = newTask.getConfiguration(); + newConfig.densityDpi += 100; + newTask.onRequestedOverrideConfigurationChanged(newConfig); + assertEquals(newTask.getConfiguration().densityDpi, newConfig.densityDpi); + + // Reparent the activity and verify that config override changed. + mActivity.reparent(newTask, 0 /* top */, "test"); + assertEquals(mActivity.getConfiguration().densityDpi, newConfig.densityDpi); + assertEquals(mActivity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi); + + assertTrue(wpc.registeredForActivityConfigChanges()); + assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration()); + assertEquals(0, mActivity.getMergedOverrideConfiguration() + .diff(wpc.getRequestedOverrideConfiguration())); + } + + @Test + public void testActivityReparentDoesntClearProcessOverride_TwoActivities() { + final WindowProcessController wpc = mActivity.app; + final Configuration initialConf = + new Configuration(mActivity.getMergedOverrideConfiguration()); + final Task initialTask = mActivity.getTask(); + final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(initialTask) + .setUseProcess(wpc).build(); + + assertTrue(wpc.registeredForActivityConfigChanges()); + assertEquals(0, secondActivity.getMergedOverrideConfiguration() + .diff(wpc.getRequestedOverrideConfiguration())); + + // Create a new task with custom config to reparent the second activity to. + final Task newTask = + new TaskBuilder(mSupervisor).setStack(initialTask.getStack()).build(); + final Configuration newConfig = newTask.getConfiguration(); + newConfig.densityDpi += 100; + newTask.onRequestedOverrideConfigurationChanged(newConfig); + + // Reparent the activity and verify that config override changed. + secondActivity.reparent(newTask, 0 /* top */, "test"); + + assertTrue(wpc.registeredForActivityConfigChanges()); + assertEquals(0, secondActivity.getMergedOverrideConfiguration() + .diff(wpc.getRequestedOverrideConfiguration())); + assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration()); + + // Reparent the first activity and verify that config override didn't change. + mActivity.reparent(newTask, 1 /* top */, "test"); + assertTrue(wpc.registeredForActivityConfigChanges()); + assertEquals(0, secondActivity.getMergedOverrideConfiguration() + .diff(wpc.getRequestedOverrideConfiguration())); + assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration()); + } + + /** + * Creates an activity on display. For non-default display request it will also create a new + * display with custom DisplayInfo. + */ + private ActivityRecord createActivityOnDisplay(boolean defaultDisplay, + WindowProcessController process) { + final DisplayContent display; + if (defaultDisplay) { + display = mRootWindowContainer.getDefaultDisplay(); + } else { + display = new TestDisplayContent.Builder(mService, 2000, 1000).setDensityDpi(300) + .setPosition(DisplayContent.POSITION_TOP).build(); + } + final ActivityStack stack = display.createStack(WINDOWING_MODE_UNDEFINED, + ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); + return new ActivityBuilder(mService).setTask(task).setUseProcess(process).build(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 0f227246b468..4beede93aea2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -111,6 +111,7 @@ class ActivityTestsBase extends SystemServiceTestsBase { private int mConfigChanges; private int mLaunchedFromPid; private int mLaunchedFromUid; + private WindowProcessController mWpc; ActivityBuilder(ActivityTaskManagerService service) { mService = service; @@ -201,6 +202,11 @@ class ActivityTestsBase extends SystemServiceTestsBase { return this; } + ActivityBuilder setUseProcess(WindowProcessController wpc) { + mWpc = wpc; + return this; + } + ActivityRecord build() { try { mService.deferWindowLayout(); @@ -263,10 +269,16 @@ class ActivityTestsBase extends SystemServiceTestsBase { activity.setVisible(true); } - final WindowProcessController wpc = new WindowProcessController(mService, - mService.mContext.getApplicationInfo(), mProcessName, mUid, - UserHandle.getUserId(12345), mock(Object.class), - mock(WindowProcessListener.class)); + final WindowProcessController wpc; + if (mWpc != null) { + wpc = mWpc; + } else { + wpc = new WindowProcessController(mService, + mService.mContext.getApplicationInfo(), mProcessName, mUid, + UserHandle.getUserId(12345), mock(Object.class), + mock(WindowProcessListener.class)); + wpc.setThread(mock(IApplicationThread.class)); + } wpc.setThread(mock(IApplicationThread.class)); activity.setProcess(wpc); doReturn(wpc).when(mService).getProcessController( diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 421a458256af..db4fdc77064b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import android.content.pm.ApplicationInfo; +import android.content.res.Configuration; import android.platform.test.annotations.Presubmit; import org.junit.Before; @@ -62,33 +63,33 @@ public class WindowProcessControllerTests extends ActivityTestsBase { // Register to display 1 as a listener. TestDisplayContent testDisplayContent1 = createTestDisplayContentInContainer(); - mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent1); + mWpc.registerDisplayConfigurationListener(testDisplayContent1); assertTrue(testDisplayContent1.containsListener(mWpc)); assertEquals(testDisplayContent1.mDisplayId, mWpc.getDisplayId()); // Move to display 2. TestDisplayContent testDisplayContent2 = createTestDisplayContentInContainer(); - mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent2); + mWpc.registerDisplayConfigurationListener(testDisplayContent2); assertFalse(testDisplayContent1.containsListener(mWpc)); assertTrue(testDisplayContent2.containsListener(mWpc)); assertEquals(testDisplayContent2.mDisplayId, mWpc.getDisplayId()); // Null DisplayContent will not change anything. - mWpc.registerDisplayConfigurationListenerLocked(null); + mWpc.registerDisplayConfigurationListener(null); assertTrue(testDisplayContent2.containsListener(mWpc)); assertEquals(testDisplayContent2.mDisplayId, mWpc.getDisplayId()); // Unregister listener will remove the wpc from registered displays. - mWpc.unregisterDisplayConfigurationListenerLocked(); + mWpc.unregisterDisplayConfigurationListener(); assertFalse(testDisplayContent1.containsListener(mWpc)); assertFalse(testDisplayContent2.containsListener(mWpc)); assertEquals(INVALID_DISPLAY, mWpc.getDisplayId()); // Unregistration still work even if the display was removed. - mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent1); + mWpc.registerDisplayConfigurationListener(testDisplayContent1); assertEquals(testDisplayContent1.mDisplayId, mWpc.getDisplayId()); mRootWindowContainer.removeChild(testDisplayContent1); - mWpc.unregisterDisplayConfigurationListenerLocked(); + mWpc.unregisterDisplayConfigurationListener(); assertEquals(INVALID_DISPLAY, mWpc.getDisplayId()); } @@ -129,6 +130,24 @@ public class WindowProcessControllerTests extends ActivityTestsBase { orderVerifier.verifyNoMoreInteractions(); } + @Test + public void testConfigurationForSecondaryScreen() { + final WindowProcessController wpc = new WindowProcessController( + mService, mock(ApplicationInfo.class), null, 0, -1, null, null); + // By default, the process should not listen to any display. + assertEquals(INVALID_DISPLAY, wpc.getDisplayId()); + + // Register to a new display as a listener. + final DisplayContent display = new TestDisplayContent.Builder(mService, 2000, 1000) + .setDensityDpi(300).setPosition(DisplayContent.POSITION_TOP).build(); + wpc.registerDisplayConfigurationListener(display); + + assertEquals(display.mDisplayId, wpc.getDisplayId()); + final Configuration expectedConfig = mService.mRootWindowContainer.getConfiguration(); + expectedConfig.updateFrom(display.getConfiguration()); + assertEquals(expectedConfig, wpc.getConfiguration()); + } + private TestDisplayContent createTestDisplayContentInContainer() { return new TestDisplayContent.Builder(mService, 1000, 1500).build(); } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index c4c1e21e7c41..c3fb51076bba 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -849,6 +849,17 @@ public class TelecomManager { */ public static final int PRESENTATION_PAYPHONE = 4; + + /* + * Values for the adb property "persist.radio.videocall.audio.output" + */ + /** @hide */ + public static final int AUDIO_OUTPUT_ENABLE_SPEAKER = 0; + /** @hide */ + public static final int AUDIO_OUTPUT_DISABLE_SPEAKER = 1; + /** @hide */ + public static final int AUDIO_OUTPUT_DEFAULT = AUDIO_OUTPUT_ENABLE_SPEAKER; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef( diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java index f39981fdf25d..f3e9de0d2688 100644 --- a/telephony/common/android/telephony/LocationAccessPolicy.java +++ b/telephony/common/android/telephony/LocationAccessPolicy.java @@ -24,20 +24,16 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; -import android.content.pm.UserInfo; import android.location.LocationManager; import android.os.Binder; import android.os.Build; import android.os.Process; import android.os.UserHandle; -import android.os.UserManager; import android.util.Log; import android.widget.Toast; import com.android.internal.telephony.util.TelephonyUtils; -import java.util.List; - /** * Helper for performing location access checks. * @hide @@ -309,7 +305,7 @@ public final class LocationAccessPolicy { } private static boolean checkSystemLocationAccess(@NonNull Context context, int uid, int pid) { - if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) { + if (!isLocationModeEnabled(context, UserHandle.getUserHandleForUid(uid).getIdentifier())) { if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")"); return false; } @@ -336,20 +332,17 @@ public final class LocationAccessPolicy { private static boolean isCurrentProfile(@NonNull Context context, int uid) { long token = Binder.clearCallingIdentity(); try { - final int currentUser = ActivityManager.getCurrentUser(); - final int callingUserId = UserHandle.getUserId(uid); - if (callingUserId == currentUser) { + if (UserHandle.getUserHandleForUid(uid).getIdentifier() + == ActivityManager.getCurrentUser()) { return true; + } + ActivityManager activityManager = context.getSystemService(ActivityManager.class); + if (activityManager != null) { + return activityManager.isProfileForeground( + UserHandle.getUserHandleForUid(ActivityManager.getCurrentUser())); } else { - List<UserInfo> userProfiles = context.getSystemService( - UserManager.class).getProfiles(currentUser); - for (UserInfo user : userProfiles) { - if (user.id == callingUserId) { - return true; - } - } + return false; } - return false; } finally { Binder.restoreCallingIdentity(token); } diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index b8b203def6b8..89cd4615637b 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -558,7 +558,7 @@ public final class TelephonyPermissions { } if (DBG) { - Log.d(LOG_TAG, "No READ_PRIVILEDED_PHONE_STATE permission, " + Log.d(LOG_TAG, "No READ_PRIVILEGED_PHONE_STATE permission, " + "check carrier privilege next."); } @@ -566,6 +566,33 @@ public final class TelephonyPermissions { } /** + * Ensure the caller (or self, if not processing an IPC) has + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or + * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or carrier privileges. + * + * @throws SecurityException if the caller does not have the required permission/privileges + */ + public static void enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( + Context context, int subId, String message) { + if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + == PERMISSION_GRANTED) { + return; + } + + if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + == PERMISSION_GRANTED) { + return; + } + + if (DBG) { + Log.d(LOG_TAG, "No READ_PRIVILEGED_PHONE_STATE nor READ_PRECISE_PHONE_STATE permission" + + ", check carrier privilege next."); + } + + enforceCallingOrSelfCarrierPrivilege(context, subId, message); + } + + /** * Make sure the caller (or self, if not processing an IPC) has carrier privileges. * * @throws SecurityException if the caller does not have the required privileges diff --git a/telephony/framework-telephony-jarjar-rules.txt b/telephony/framework-telephony-jarjar-rules.txt index 7cab806298a4..212eba153a15 100644 --- a/telephony/framework-telephony-jarjar-rules.txt +++ b/telephony/framework-telephony-jarjar-rules.txt @@ -1,4 +1,9 @@ rule android.telephony.Annotation* android.telephony.framework.Annotation@1 +rule android.util.RecurrenceRule* android.telephony.RecurrenceRule@1 rule com.android.i18n.phonenumbers.** com.android.telephony.framework.phonenumbers.@1 -#TODO: add jarjar rules for statically linked util classes - +rule com.android.internal.os.SomeArgs* android.telephony.SomeArgs@1 +rule com.android.internal.util.BitwiseInputStream* android.telephony.BitwiseInputStream@1 +rule com.android.internal.util.BitwiseOutputStream* android.telephony.BitwiseOutputStream@1 +rule com.android.internal.util.Preconditions* android.telephony.Preconditions@1 +rule com.android.internal.util.IndentingPrintWriter* android.telephony.IndentingPrintWriter@1 +rule com.android.internal.util.HexDump* android.telephony.HexDump@1 diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index bb28df2b64eb..d325cd84855c 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -50,18 +50,12 @@ public final class AccessNetworkConstants { /** * Transport type for Wireless Wide Area Networks (i.e. Cellular) - * @hide */ - @SystemApi - @TestApi public static final int TRANSPORT_TYPE_WWAN = 1; /** * Transport type for Wireless Local Area Networks (i.e. Wifi) - * @hide */ - @SystemApi - @TestApi public static final int TRANSPORT_TYPE_WLAN = 2; /** @hide */ diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 5a7c3b373b60..ff31d3e95997 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1575,7 +1575,10 @@ public class CarrierConfigManager { public static final String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName"; public static final String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl"; public static final String KEY_MMS_USER_AGENT_STRING = "userAgent"; - /** @hide */ + /** + * If true, add "Connection: close" header to MMS HTTP requests so the connection + * is immediately closed (disabling keep-alive). + */ public static final String KEY_MMS_CLOSE_CONNECTION_BOOL = "mmsCloseConnection"; /** diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java index 475c99b7fae2..ec86c144c08e 100644 --- a/telephony/java/android/telephony/CellInfo.java +++ b/telephony/java/android/telephony/CellInfo.java @@ -18,6 +18,7 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.radio.V1_4.CellInfo.Info; import android.os.Parcel; @@ -179,6 +180,18 @@ public abstract class CellInfo implements Parcelable { * * @return a time stamp in nanos since boot. */ + @SuppressLint("MethodNameUnits") + public long getTimestampNanos() { + return mTimeStamp; + } + + /** + * Approximate time this cell information was received from the modem. + * + * @return a time stamp in nanos since boot. + * @deprecated Use {@link #getTimestampNanos} instead. + */ + @Deprecated public long getTimeStamp() { return mTimeStamp; } diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index cbd5ed665898..32ffb75f373c 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -36,10 +36,7 @@ import java.util.stream.Collectors; /** * Description of a mobile network registration info - * @hide */ -@SystemApi -@TestApi public final class NetworkRegistrationInfo implements Parcelable { /** * Network domain @@ -51,9 +48,9 @@ public final class NetworkRegistrationInfo implements Parcelable { /** Unknown / Unspecified domain */ public static final int DOMAIN_UNKNOWN = 0; - /** Circuit switching domain */ + /** Circuit switched domain */ public static final int DOMAIN_CS = android.hardware.radio.V1_5.Domain.CS; - /** Packet switching domain */ + /** Packet switched domain */ public static final int DOMAIN_PS = android.hardware.radio.V1_5.Domain.PS; /** Applicable to both CS and PS Domain */ public static final int DOMAIN_CS_PS = DOMAIN_CS | DOMAIN_PS; @@ -69,17 +66,41 @@ public final class NetworkRegistrationInfo implements Parcelable { REGISTRATION_STATE_UNKNOWN, REGISTRATION_STATE_ROAMING}) public @interface RegistrationState {} - /** Not registered. The device is not currently searching a new operator to register. */ + /** + * Not registered. The device is not currently searching a new operator to register. + * @hide + */ + @SystemApi @TestApi public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; - /** Registered on home network. */ + /** + * Registered on home network. + * @hide + */ + @SystemApi @TestApi public static final int REGISTRATION_STATE_HOME = 1; - /** Not registered. The device is currently searching a new operator to register. */ + /** + * Not registered. The device is currently searching a new operator to register. + * @hide + */ + @SystemApi @TestApi public static final int REGISTRATION_STATE_NOT_REGISTERED_SEARCHING = 2; - /** Registration denied. */ + /** + * Registration denied. + * @hide + */ + @SystemApi @TestApi public static final int REGISTRATION_STATE_DENIED = 3; - /** Registration state is unknown. */ + /** + * Registration state is unknown. + * @hide + */ + @SystemApi @TestApi public static final int REGISTRATION_STATE_UNKNOWN = 4; - /** Registered on roaming network. */ + /** + * Registered on roaming network. + * @hide + */ + @SystemApi @TestApi public static final int REGISTRATION_STATE_ROAMING = 5; /** @hide */ @@ -92,7 +113,6 @@ public final class NetworkRegistrationInfo implements Parcelable { /** * The device isn't camped on an LTE cell or the LTE cell doesn't support E-UTRA-NR * Dual Connectivity(EN-DC). - * @hide */ public static final int NR_STATE_NONE = 0; @@ -100,7 +120,6 @@ public final class NetworkRegistrationInfo implements Parcelable { * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) but * either the use of dual connectivity with NR(DCNR) is restricted or NR is not supported by * the selected PLMN. - * @hide */ public static final int NR_STATE_RESTRICTED = 1; @@ -108,14 +127,12 @@ public final class NetworkRegistrationInfo implements Parcelable { * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) and both * the use of dual connectivity with NR(DCNR) is not restricted and NR is supported by the * selected PLMN. - * @hide */ public static final int NR_STATE_NOT_RESTRICTED = 2; /** * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) and * also connected to at least one 5G cell as a secondary serving cell. - * @hide */ public static final int NR_STATE_CONNECTED = 3; @@ -129,22 +146,34 @@ public final class NetworkRegistrationInfo implements Parcelable { SERVICE_TYPE_VIDEO, SERVICE_TYPE_EMERGENCY}) public @interface ServiceType {} - /** Unkown service */ + /** + * Unknown service + */ public static final int SERVICE_TYPE_UNKNOWN = 0; - /** Voice service */ + /** + * Voice service + */ public static final int SERVICE_TYPE_VOICE = 1; - /** Data service */ + /** + * Data service + */ public static final int SERVICE_TYPE_DATA = 2; - /** SMS service */ + /** + * SMS service + */ public static final int SERVICE_TYPE_SMS = 3; - /** Video service */ + /** + * Video service + */ public static final int SERVICE_TYPE_VIDEO = 4; - /** Emergency service */ + /** + * Emergency service + */ public static final int SERVICE_TYPE_EMERGENCY = 5; @Domain @@ -330,9 +359,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * Get the 5G NR connection state. * * @return the 5G NR connection state. - * @hide */ - @SystemApi public @NRState int getNrState() { return mNrState; } @@ -344,7 +371,10 @@ public final class NetworkRegistrationInfo implements Parcelable { /** * @return The registration state. + * + * @hide */ + @SystemApi @TestApi public @RegistrationState int getRegistrationState() { return mRegistrationState; } @@ -352,6 +382,21 @@ public final class NetworkRegistrationInfo implements Parcelable { /** * @return {@code true} if registered on roaming network, {@code false} otherwise. */ + public boolean isRegistered() { + return mRegistrationState == REGISTRATION_STATE_HOME + || mRegistrationState == REGISTRATION_STATE_ROAMING; + } + + /** + * @return {@code true} if registered on roaming network, {@code false} otherwise. + */ + public boolean isSearching() { + return mRegistrationState == REGISTRATION_STATE_NOT_REGISTERED_SEARCHING; + } + + /** + * @return {@code true} if registered on roaming network, {@code false} otherwise. + */ public boolean isRoaming() { return mRoamingType != ServiceState.ROAMING_TYPE_NOT_ROAMING; } @@ -376,15 +421,18 @@ public final class NetworkRegistrationInfo implements Parcelable { /** * @return the current network roaming type. + * @hide */ - + @SystemApi @TestApi public @ServiceState.RoamingType int getRoamingType() { return mRoamingType; } /** * @return Whether emergency is enabled. + * @hide */ + @SystemApi @TestApi public boolean isEmergencyEnabled() { return mEmergencyOnly; } /** @@ -422,7 +470,9 @@ public final class NetworkRegistrationInfo implements Parcelable { * @return Reason for denial if the registration state is {@link #REGISTRATION_STATE_DENIED}. * Depending on {@code accessNetworkTechnology}, the values are defined in 3GPP TS 24.008 * 10.5.3.6 for UMTS, 3GPP TS 24.301 9.9.3.9 for LTE, and 3GPP2 A.S0001 6.2.2.44 for CDMA + * @hide */ + @SystemApi @TestApi public int getRejectCause() { return mRejectCause; } @@ -445,8 +495,10 @@ public final class NetworkRegistrationInfo implements Parcelable { /** * @return Data registration related info + * @hide */ @Nullable + @SystemApi @TestApi public DataSpecificRegistrationInfo getDataSpecificInfo() { return mDataSpecificInfo; } @@ -571,7 +623,11 @@ public final class NetworkRegistrationInfo implements Parcelable { && mNrState == other.mNrState; } + /** + * @hide + */ @Override + @SystemApi @TestApi public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mDomain); dest.writeInt(mTransportType); @@ -659,7 +715,9 @@ public final class NetworkRegistrationInfo implements Parcelable { * .setRegistrationState(REGISTRATION_STATE_HOME) * .build(); * </code></pre> + * @hide */ + @SystemApi @TestApi public static final class Builder { @Domain private int mDomain; @@ -759,7 +817,9 @@ public final class NetworkRegistrationInfo implements Parcelable { * @param emergencyOnly True if this network registration is for emergency use only. * * @return The same instance of the builder. + * @hide */ + @SystemApi @TestApi public @NonNull Builder setEmergencyOnly(boolean emergencyOnly) { mEmergencyOnly = emergencyOnly; return this; @@ -771,7 +831,9 @@ public final class NetworkRegistrationInfo implements Parcelable { * @param availableServices Available services. * * @return The same instance of the builder. + * @hide */ + @SystemApi @TestApi public @NonNull Builder setAvailableServices( @NonNull @ServiceType List<Integer> availableServices) { mAvailableServices = availableServices; @@ -784,7 +846,9 @@ public final class NetworkRegistrationInfo implements Parcelable { * @param cellIdentity The cell identity. * * @return The same instance of the builder. + * @hide */ + @SystemApi @TestApi public @NonNull Builder setCellIdentity(@Nullable CellIdentity cellIdentity) { mCellIdentity = cellIdentity; return this; @@ -792,9 +856,10 @@ public final class NetworkRegistrationInfo implements Parcelable { /** * Build the NetworkRegistrationInfo. - * * @return the NetworkRegistrationInfo object. + * @hide */ + @SystemApi @TestApi public @NonNull NetworkRegistrationInfo build() { return new NetworkRegistrationInfo(mDomain, mTransportType, mRegistrationState, mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices, diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 2c8014e4a84d..1e64a8182e3b 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -1855,10 +1855,8 @@ public class ServiceState implements Parcelable { * Get all of the available network registration info. * * @return List of {@link NetworkRegistrationInfo} - * @hide */ @NonNull - @SystemApi public List<NetworkRegistrationInfo> getNetworkRegistrationInfoList() { synchronized (mNetworkRegistrationInfos) { List<NetworkRegistrationInfo> newList = new ArrayList<>(); diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index 1c58f8faf7cf..5a4dac5220a0 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -16,9 +16,8 @@ package android.telephony; -import com.android.telephony.Rlog; - import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; @@ -26,6 +25,9 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; +import android.os.SystemClock; + +import com.android.telephony.Rlog; import java.util.ArrayList; import java.util.List; @@ -77,6 +79,9 @@ public class SignalStrength implements Parcelable { /* The type of signal measurement */ private static final String MEASUREMENT_TYPE_RSCP = "rscp"; + // timeStamp of signalStrength in nanoseconds since boot + private long mTimestamp = Long.MAX_VALUE; + CellSignalStrengthCdma mCdma; CellSignalStrengthGsm mGsm; CellSignalStrengthWcdma mWcdma; @@ -134,6 +139,7 @@ public class SignalStrength implements Parcelable { mTdscdma = tdscdma; mLte = lte; mNr = nr; + mTimestamp = SystemClock.elapsedRealtimeNanos(); } /** @@ -268,6 +274,7 @@ public class SignalStrength implements Parcelable { mTdscdma.updateLevel(cc, ss); mLte.updateLevel(cc, ss); mNr.updateLevel(cc, ss); + mTimestamp = SystemClock.elapsedRealtimeNanos(); } /** @@ -293,6 +300,7 @@ public class SignalStrength implements Parcelable { mTdscdma = new CellSignalStrengthTdscdma(s.mTdscdma); mLte = new CellSignalStrengthLte(s.mLte); mNr = new CellSignalStrengthNr(s.mNr); + mTimestamp = s.getTimestampNanos(); } /** @@ -310,6 +318,7 @@ public class SignalStrength implements Parcelable { mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader()); mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader()); mNr = in.readParcelable(CellSignalStrengthLte.class.getClassLoader()); + mTimestamp = in.readLong(); } /** @@ -322,9 +331,18 @@ public class SignalStrength implements Parcelable { out.writeParcelable(mTdscdma, flags); out.writeParcelable(mLte, flags); out.writeParcelable(mNr, flags); + out.writeLong(mTimestamp); } /** + * @return mTimestamp in nanoseconds + */ + @SuppressLint("MethodNameUnits") + public long getTimestampNanos() { + return mTimestamp; + } + + /** * {@link Parcelable#describeContents} */ public int describeContents() { diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index fee6d3fc4795..2f95a501ce2f 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -424,7 +424,26 @@ public final class SmsManager { String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, - true /* persistMessage*/, null); + true /* persistMessage*/, null, 0L /* messageId */); + } + + + /** + * Send a text based SMS. Same as {@link #sendTextMessage( String destinationAddress, + * String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent)}, but + * adds an optional messageId. + * @param messageId An id that uniquely identifies the message requested to be sent. + * Used for logging and diagnostics purposes. The id may be 0. + * + * @throws IllegalArgumentException if destinationAddress or text are empty + * + */ + public void sendTextMessage( + @NonNull String destinationAddress, @Nullable String scAddress, @NonNull String text, + @Nullable PendingIntent sentIntent, @Nullable PendingIntent deliveryIntent, + long messageId) { + sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, + true /* persistMessage*/, null, messageId); } /** @@ -542,7 +561,7 @@ public final class SmsManager { private void sendTextMessageInternal(String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, - boolean persistMessage, String packageName) { + boolean persistMessage, String packageName, long messageId) { if (TextUtils.isEmpty(destinationAddress)) { throw new IllegalArgumentException("Invalid destinationAddress"); } @@ -569,10 +588,10 @@ public final class SmsManager { try { iSms.sendTextForSubscriber(subId, packageName, destinationAddress, scAddress, text, sentIntent, deliveryIntent, - persistMessage); + persistMessage, messageId); } catch (RemoteException e) { Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - " - + e.getMessage()); + + e.getMessage() + " id: " + messageId); notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION); } } @@ -589,10 +608,10 @@ public final class SmsManager { try { iSms.sendTextForSubscriber(getSubscriptionId(), packageName, destinationAddress, scAddress, text, sentIntent, deliveryIntent, - persistMessage); + persistMessage, messageId); } catch (RemoteException e) { Log.e(TAG, "sendTextMessageInternal (no persist): Couldn't send SMS, exception - " - + e.getMessage()); + + e.getMessage() + " id: " + messageId); notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION); } } @@ -634,7 +653,8 @@ public final class SmsManager { String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, - false /* persistMessage */, null); + false /* persistMessage */, null, + 0L /* messageId */); } private void sendTextMessageInternal( @@ -921,7 +941,26 @@ public final class SmsManager { String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, true /* persistMessage*/, null); + deliveryIntents, true /* persistMessage*/, null, + 0L /* messageId */); + } + + /** + * Send a multi-part text based SMS. Same as #sendMultipartTextMessage(String, String, + * ArrayList, ArrayList, ArrayList), but adds an optional messageId. + * @param messageId An id that uniquely identifies the message requested to be sent. + * Used for logging and diagnostics purposes. The id may be 0. + * + * @throws IllegalArgumentException if destinationAddress or data are empty + * + */ + public void sendMultipartTextMessage( + @NonNull String destinationAddress, @Nullable String scAddress, + @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents, + @Nullable List<PendingIntent> deliveryIntents, long messageId) { + sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, + deliveryIntents, true /* persistMessage*/, null, + messageId); } /** @@ -946,17 +985,17 @@ public final class SmsManager { @SystemApi @TestApi public void sendMultipartTextMessage( - @NonNull String destinationAddress, @NonNull String scAddress, + @NonNull String destinationAddress, @Nullable String scAddress, @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents, @Nullable List<PendingIntent> deliveryIntents, @NonNull String packageName) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, true /* persistMessage*/, packageName); + deliveryIntents, true /* persistMessage*/, packageName, 0L /* messageId */); } private void sendMultipartTextMessageInternal( String destinationAddress, String scAddress, List<String> parts, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents, - boolean persistMessage, String packageName) { + boolean persistMessage, String packageName, long messageId) { if (TextUtils.isEmpty(destinationAddress)) { throw new IllegalArgumentException("Invalid destinationAddress"); } @@ -983,10 +1022,11 @@ public final class SmsManager { ISms iSms = getISmsServiceOrThrow(); iSms.sendMultipartTextForSubscriber(subId, packageName, destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, persistMessage); + deliveryIntents, persistMessage, messageId); } catch (RemoteException e) { Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - " - + e.getMessage()); + + e.getMessage() + " id: " + + messageId); notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION); } } @@ -1003,11 +1043,11 @@ public final class SmsManager { if (iSms != null) { iSms.sendMultipartTextForSubscriber(getSubscriptionId(), packageName, destinationAddress, scAddress, parts, sentIntents, deliveryIntents, - persistMessage); + persistMessage, messageId); } } catch (RemoteException e) { Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - " - + e.getMessage()); + + e.getMessage() + " id: " + messageId); notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION); } } @@ -1021,7 +1061,7 @@ public final class SmsManager { deliveryIntent = deliveryIntents.get(0); } sendTextMessageInternal(destinationAddress, scAddress, parts.get(0), - sentIntent, deliveryIntent, true, packageName); + sentIntent, deliveryIntent, true, packageName, messageId); } } @@ -1051,7 +1091,8 @@ public final class SmsManager { String destinationAddress, String scAddress, List<String> parts, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, false /* persistMessage*/, null); + deliveryIntents, false /* persistMessage*/, null, + 0L /* messageId */); } /** @@ -2545,16 +2586,6 @@ public final class SmsManager { /** Intent extra name for HTTP status code for MMS HTTP failure in integer type */ public static final String EXTRA_MMS_HTTP_STATUS = "android.telephony.extra.MMS_HTTP_STATUS"; - /** Represents the received SMS message for importing {@hide} */ - public static final int SMS_TYPE_INCOMING = 0; - /** Represents the sent SMS message for importing {@hide} */ - public static final int SMS_TYPE_OUTGOING = 1; - - /** Message status property: whether the message has been seen. 1 means seen, 0 not {@hide} */ - public static final String MESSAGE_STATUS_SEEN = "seen"; - /** Message status property: whether the message has been read. 1 means read, 0 not {@hide} */ - public static final String MESSAGE_STATUS_READ = "read"; - /** * Get carrier-dependent MMS configuration values. * diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 56ca8c7790cb..4aedf67ac94b 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -3859,21 +3859,20 @@ public class TelephonyManager { } /** - * Return if the current radio is LTE on CDMA. This is a tri-state return value as for a period - * of time the mode may be unknown. + * Return if the current radio has global mode enabled, meaning it supports + * both 3GPP and 3GPP2 radio technologies at the same time. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the - * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} - * - * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE} - * or {@link PhoneConstants#LTE_ON_CDMA_TRUE} + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}. * + * @return {@code true} if global mode is enabled + * {@code false} if global mode is not enabled or unknown * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - @UnsupportedAppUsage - public int getLteOnCdmaMode() { - return getLteOnCdmaMode(getSubId()); + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isGlobalModeEnabled() { + return getLteOnCdmaMode(getSubId()) == PhoneConstants.LTE_ON_CDMA_TRUE; } /** @@ -3886,7 +3885,7 @@ public class TelephonyManager { * or {@link PhoneConstants#LTE_ON_CDMA_TRUE} * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @UnsupportedAppUsage public int getLteOnCdmaMode(int subId) { try { @@ -9535,15 +9534,20 @@ public class TelephonyManager { } /** - * Requested state of SIM - * - * CARD_POWER_DOWN * Powers down the SIM. SIM must be up prior. - * - * CARD_POWER_UP + * @hide + */ + @SystemApi + public static final int CARD_POWER_DOWN = 0; + + /** * Powers up the SIM normally. SIM must be down prior. - * - * CARD_POWER_UP_PASS_THROUGH + * @hide + */ + @SystemApi + public static final int CARD_POWER_UP = 1; + + /** * Powers up the SIM in PASS_THROUGH mode. SIM must be down prior. * When SIM is powered up in PASS_THOUGH mode, the modem does not send * any command to it (for example SELECT of MF, or TERMINAL CAPABILITY), @@ -9556,12 +9560,9 @@ public class TelephonyManager { * is activated, and normal behavior occurs at the next SIM initialization, * unless PASS_THROUGH mode is requested again. Hence, the last power-up mode * is NOT persistent across boots. On reboot, SIM will power up normally. + * @hide */ - /** @hide */ - public static final int CARD_POWER_DOWN = 0; - /** @hide */ - public static final int CARD_POWER_UP = 1; - /** @hide */ + @SystemApi public static final int CARD_POWER_UP_PASS_THROUGH = 2; /** @@ -10981,9 +10982,15 @@ public class TelephonyManager { } /** - * Checks if FEATURE_TELEPHONY_DATA is enabled. - * - * @hide + * @return true if the current device is "data capable" over a radio on the device. + * <p> + * "Data capable" means that this device supports packet-switched + * data connections over the telephony network. + * <p> + * Note: the meaning of this flag is subtly different from the + * PackageManager.FEATURE_TELEPHONY system feature, which is available + * on any device with a telephony radio, even if the device is + * voice-only. */ public boolean isDataCapable() { if (mContext == null) return true; @@ -12416,15 +12423,21 @@ public class TelephonyManager { } /** - * Set allowing mobile data during voice call. + * Set allowing mobile data during voice call. This is used for allowing data on the non-default + * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will + * not be able to use mobile data. By calling this API, data will be temporarily enabled on the + * non-default data SIM during the life cycle of the voice call. * * @param allow {@code true} if allowing using data during voice call, {@code false} if - * disallowed + * disallowed. + * + * @return {@code true} if operation is successful. otherwise {@code false}. * - * @return {@code false} if the setting is changed. + * @throws SecurityException if the caller doesn't have the permission. * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setDataAllowedDuringVoiceCall(boolean allow) { try { @@ -12433,6 +12446,7 @@ public class TelephonyManager { return service.setDataAllowedDuringVoiceCall(getSubId(), allow); } } catch (RemoteException ex) { + // This could happen if binder process crashes. if (!isSystemProcess()) { ex.rethrowAsRuntimeException(); } @@ -12441,13 +12455,18 @@ public class TelephonyManager { } /** - * Check whether data is allowed during voice call. Note this is for dual sim device that - * data might be disabled on non-default data subscription but explicitly turned on by settings. + * Check whether data is allowed during voice call. This is used for allowing data on the + * non-default data SIM. When a voice call is placed on the non-default data SIM on DSDS + * devices, users will not be able to use mobile data. By calling this API, data will be + * temporarily enabled on the non-default data SIM during the life cycle of the voice call. * * @return {@code true} if data is allowed during voice call. * + * @throws SecurityException if the caller doesn't have the permission. + * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataAllowedInVoiceCall() { try { @@ -12456,6 +12475,7 @@ public class TelephonyManager { return service.isDataAllowedInVoiceCall(getSubId()); } } catch (RemoteException ex) { + // This could happen if binder process crashes. if (!isSystemProcess()) { ex.rethrowAsRuntimeException(); } diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index fab1bf2215af..6e630e3a7024 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -18,6 +18,7 @@ package android.telephony.data; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.content.ContentValues; import android.database.Cursor; import android.hardware.radio.V1_5.ApnTypes; @@ -28,13 +29,14 @@ import android.provider.Telephony; import android.provider.Telephony.Carriers; import android.telephony.Annotation.ApnType; import android.telephony.Annotation.NetworkType; -import com.android.telephony.Rlog; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; +import com.android.telephony.Rlog; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.InetAddress; @@ -123,6 +125,122 @@ public class ApnSetting implements Parcelable { /** Authentication type for PAP or CHAP. */ public static final int AUTH_TYPE_PAP_OR_CHAP = 3; + /** + * APN types for data connections. These are usage categories for an APN + * entry. One APN entry may support multiple APN types, eg, a single APN + * may service regular internet traffic ("default") as well as MMS-specific + * connections.<br/> + * APN_TYPE_ALL is a special type to indicate that this APN entry can + * service all data connections. + * <p> + * Note: The goal is to deprecate this. Due to the Carrier Table being used + * directly, this isn't feasible right now. + * + * @hide + */ + @SystemApi + public static final String TYPE_ALL_STRING = "*"; + + /** + * APN type for default data traffic + * + * @hide + */ + @SystemApi + public static final String TYPE_DEFAULT_STRING = "default"; + + + /** + * APN type for MMS traffic + * + * @hide + */ + @SystemApi + public static final String TYPE_MMS_STRING = "mms"; + + + /** + * APN type for SUPL assisted GPS + * + * @hide + */ + @SystemApi + public static final String TYPE_SUPL_STRING = "supl"; + + /** + * APN type for DUN traffic + * + * @hide + */ + @SystemApi + public static final String TYPE_DUN_STRING = "dun"; + + /** + * APN type for HiPri traffic + * + * @hide + */ + @SystemApi + public static final String TYPE_HIPRI_STRING = "hipri"; + + /** + * APN type for FOTA + * + * @hide + */ + @SystemApi + public static final String TYPE_FOTA_STRING = "fota"; + + /** + * APN type for IMS + * + * @hide + */ + @SystemApi + public static final String TYPE_IMS_STRING = "ims"; + + /** + * APN type for CBS + * + * @hide + */ + @SystemApi + public static final String TYPE_CBS_STRING = "cbs"; + + /** + * APN type for IA Initial Attach APN + * + * @hide + */ + @SystemApi + public static final String TYPE_IA_STRING = "ia"; + + /** + * APN type for Emergency PDN. This is not an IA apn, but is used + * for access to carrier services in an emergency call situation. + * + * @hide + */ + @SystemApi + public static final String TYPE_EMERGENCY_STRING = "emergency"; + + /** + * APN type for Mission Critical Services + * + * @hide + */ + @SystemApi + public static final String TYPE_MCX_STRING = "mcx"; + + /** + * APN type for XCAP + * + * @hide + */ + @SystemApi + public static final String TYPE_XCAP_STRING = "xcap"; + + /** @hide */ @IntDef(prefix = { "AUTH_TYPE_" }, value = { AUTH_TYPE_NONE, @@ -1289,10 +1407,13 @@ public class ApnSetting implements Parcelable { } /** + * Converts the integer value of an APN type to the string version. * @param apnTypeBitmask bitmask of APN types. * @return comma delimited list of APN types. * @hide */ + @SystemApi + @NonNull public static String getApnTypesStringFromBitmask(int apnTypeBitmask) { List<String> types = new ArrayList<>(); for (Integer type : APN_TYPE_INT_MAP.keySet()) { diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index bc60d81cdc47..3f260eb7d80f 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -18,6 +18,7 @@ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; @@ -40,10 +41,11 @@ import java.util.ArrayList; import java.util.List; /** - * Parcelable object to handle IMS call profile. - * It is created from GSMA IR.92/IR.94, 3GPP TS 24.229/TS 26.114/TS26.111. - * It provides the service and call type, the additional information related to the call. - * + * A Parcelable object to handle the IMS call profile, which provides the service, call type, and + * additional information related to the call. + * <p> + * See the following specifications for more information about this class: GSMA IR.92/IR.94, + * 3GPP TS 24.229/TS 26.114/TS26.111. * @hide */ @SystemApi @@ -151,12 +153,13 @@ public final class ImsCallProfile implements Parcelable { */ public static final String EXTRA_CONFERENCE_AVAIL = "conference_avail"; - // Extra string for internal use only. OEMs should not use - // this for packing extras. /** + * Extra key used to store a Bundle containing proprietary extras to send to the ImsService. + * Use {@link #getProprietaryCallExtras()} instead. * @hide */ - public static final String EXTRA_OEM_EXTRAS = "OemCallExtras"; + @TestApi + public static final String EXTRA_OEM_EXTRAS = "android.telephony.ims.extra.OEM_EXTRAS"; /** * Rule for originating identity (number) presentation, MO/MT. @@ -687,6 +690,18 @@ public final class ImsCallProfile implements Parcelable { return mCallExtras; } + /** + * Get the proprietary extras set for this ImsCallProfile. + * @return A {@link Bundle} containing proprietary call extras that were not set by the + * platform. + */ + public @Nullable Bundle getProprietaryCallExtras() { + if (mCallExtras == null) { + return null; + } + return mCallExtras.getBundle(EXTRA_OEM_EXTRAS); + } + public ImsStreamMediaProfile getMediaProfile() { return mMediaProfile; } diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 9b739d3acbbc..8c11df41cf64 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -27,6 +27,7 @@ import android.annotation.TestApi; import android.annotation.WorkerThread; import android.os.Binder; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; @@ -383,6 +384,8 @@ public class ProvisioningManager { callback.setExecutor(executor); try { getITelephony().registerImsProvisioningChangedCallback(mSubId, callback.getBinder()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException | IllegalStateException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index 893a311e646b..3e2903fa6f47 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -83,8 +83,23 @@ public final class RcsContactUceCapability implements Parcelable { public static final int CAPABILITY_RCS_VOICE_CALL = (1 << 19); /** Supports RCS video calling */ public static final int CAPABILITY_RCS_VIDEO_CALL = (1 << 20); - /** Supports RCS video calling, where video media can not be dropped */ + /** Supports RCS video calling, where video media can not be dropped. */ public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = (1 << 21); + /** Supports call composer, where outgoing calls can be enriched with pre-call content.*/ + public static final int CAPABILITY_CALL_COMPOSER = (1 << 22); + /** Supports post call information that is included in the call if the call is missed.*/ + public static final int CAPABILITY_POST_CALL = (1 << 23); + /** Supports sharing a map where the user can draw, share markers, and share their position. */ + public static final int CAPABILITY_SHARED_MAP = (1 << 24); + /** Supports sharing a canvas, where users can draw, add images, and change background colors.*/ + public static final int CAPABILITY_SHARED_SKETCH = (1 << 25); + /** Supports communication with Chatbots. */ + public static final int CAPABILITY_CHAT_BOT = (1 << 26); + /** Supports Chatbot roles. */ + public static final int CAPABILITY_CHAT_BOT_ROLE = (1 << 27); + /** Supports the unidirectional plug-ins framework. */ + public static final int CAPABILITY_PLUG_IN = (1 << 28); + /** @hide*/ @Retention(RetentionPolicy.SOURCE) @@ -110,7 +125,14 @@ public final class RcsContactUceCapability implements Parcelable { CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER, CAPABILITY_RCS_VOICE_CALL, CAPABILITY_RCS_VIDEO_CALL, - CAPABILITY_RCS_VIDEO_ONLY_CALL + CAPABILITY_RCS_VIDEO_ONLY_CALL, + CAPABILITY_CALL_COMPOSER, + CAPABILITY_POST_CALL, + CAPABILITY_SHARED_MAP, + CAPABILITY_SHARED_SKETCH, + CAPABILITY_CHAT_BOT, + CAPABILITY_CHAT_BOT_ROLE, + CAPABILITY_PLUG_IN }) public @interface CapabilityFlag {} @@ -183,7 +205,7 @@ public final class RcsContactUceCapability implements Parcelable { } private final Uri mContactUri; - private int mCapabilities; + private long mCapabilities; private List<String> mExtensionTags = new ArrayList<>(); private Map<Integer, Uri> mServiceMap = new HashMap<>(); @@ -198,7 +220,7 @@ public final class RcsContactUceCapability implements Parcelable { private RcsContactUceCapability(Parcel in) { mContactUri = in.readParcelable(Uri.class.getClassLoader()); - mCapabilities = in.readInt(); + mCapabilities = in.readLong(); in.readStringList(mExtensionTags); // read mServiceMap as key,value pair int mapSize = in.readInt(); @@ -223,7 +245,7 @@ public final class RcsContactUceCapability implements Parcelable { @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeParcelable(mContactUri, 0); - out.writeInt(mCapabilities); + out.writeLong(mCapabilities); out.writeStringList(mExtensionTags); // write mServiceMap as key,value pairs int mapSize = mServiceMap.keySet().size(); diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index 4cb8575b8e93..1004e1b4acb7 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -337,11 +337,10 @@ public abstract class ImsFeature { } /** - * @return The current state of the feature, defined as {@link #STATE_UNAVAILABLE}, - * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}. - * @hide + * @return The current state of the ImsFeature, set previously by {@link #setFeatureState(int)} + * or {@link #STATE_UNAVAILABLE} if it has not been updated yet. */ - public int getFeatureState() { + public @ImsState int getFeatureState() { synchronized (mLock) { return mState; } diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java index feac3c2ebfde..3ec4f3468497 100644 --- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java @@ -16,6 +16,7 @@ package android.telephony.ims.stub; +import android.annotation.IntDef; import android.annotation.SystemApi; import android.annotation.TestApi; import android.os.Bundle; @@ -25,6 +26,9 @@ import android.telephony.ims.ImsUtListener; import com.android.ims.internal.IImsUt; import com.android.ims.internal.IImsUtListener; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Base implementation of IMS UT interface, which implements stubs. Override these methods to * implement functionality. @@ -36,6 +40,70 @@ import com.android.ims.internal.IImsUtListener; @SystemApi @TestApi public class ImsUtImplBase { + /** + * Bar all incoming calls. (See 3GPP TS 24.611) + */ + public static final int CALL_BARRING_ALL_INCOMING = 1; + + /** + * Bar all outgoing calls. (See 3GPP TS 24.611) + */ + public static final int CALL_BARRING_ALL_OUTGOING = 2; + + /** + * Bar all outgoing international calls. (See 3GPP TS 24.611) + */ + public static final int CALL_BARRING_OUTGOING_INTL = 3; + + /** + * Bar all outgoing international calls, excluding those to the home PLMN country + * (See 3GPP TS 24.611) + */ + public static final int CALL_BARRING_OUTGOING_INTL_EXCL_HOME = 4; + + /** + * Bar all incoming calls when roaming (See 3GPP TS 24.611) + */ + public static final int CALL_BLOCKING_INCOMING_WHEN_ROAMING = 5; + + /** + * Enable Anonymous Communication Rejection (See 3GPP TS 24.611) + */ + public static final int CALL_BARRING_ANONYMOUS_INCOMING = 6; + + /** + * Bar all incoming and outgoing calls. (See 3GPP TS 24.611) + */ + public static final int CALL_BARRING_ALL = 7; + + /** + * Bar all outgoing service requests, including calls. (See 3GPP TS 24.611) + */ + public static final int CALL_BARRING_OUTGOING_ALL_SERVICES = 8; + + /** + * Bar all incoming service requests, including calls. (See 3GPP TS 24.611) + */ + public static final int CALL_BARRING_INCOMING_ALL_SERVICES = 9; + + /** + * Bar specific incoming calls. (See 3GPP TS 24.611) + */ + public static final int CALL_BARRING_SPECIFIC_INCOMING_CALLS = 10; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CALL_BARRING_", value = {CALL_BARRING_ALL_INCOMING, CALL_BARRING_ALL_OUTGOING, + CALL_BARRING_OUTGOING_INTL, CALL_BARRING_OUTGOING_INTL_EXCL_HOME, + CALL_BLOCKING_INCOMING_WHEN_ROAMING, CALL_BARRING_ANONYMOUS_INCOMING, + CALL_BARRING_ALL, CALL_BARRING_OUTGOING_ALL_SERVICES, + CALL_BARRING_INCOMING_ALL_SERVICES, CALL_BARRING_SPECIFIC_INCOMING_CALLS}) + public @interface CallBarringMode {} + + /** + * Constant used to denote an invalid return value. + */ + public static final int INVALID_RESULT = -1; private IImsUt.Stub mServiceImpl = new IImsUt.Stub() { @Override @@ -247,15 +315,15 @@ public class ImsUtImplBase { /** * Updates the configuration of the call barring. */ - public int updateCallBarring(int cbType, int action, String[] barrList) { + public int updateCallBarring(@CallBarringMode int cbType, int action, String[] barrList) { return -1; } /** * Updates the configuration of the call barring for specified service class. */ - public int updateCallBarringForServiceClass(int cbType, int action, String[] barrList, - int serviceClass) { + public int updateCallBarringForServiceClass(@CallBarringMode int cbType, int action, + String[] barrList, int serviceClass) { return -1; } diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java index 50b63bd25435..15f837189843 100644 --- a/telephony/java/com/android/ims/ImsUtInterface.java +++ b/telephony/java/com/android/ims/ImsUtInterface.java @@ -21,6 +21,7 @@ import android.os.Handler; import android.os.Message; import android.telephony.ims.ImsCallForwardInfo; import android.telephony.ims.ImsSsInfo; +import android.telephony.ims.stub.ImsUtImplBase; /** * Provides APIs for the supplementary service settings using IMS (Ut interface). @@ -58,47 +59,48 @@ public interface ImsUtInterface { * CDIV (Communication Diversion, 3GPP TS 24.604) * actions: target, no reply timer */ - public static final int CDIV_CF_UNCONDITIONAL = 0; - public static final int CDIV_CF_BUSY = 1; - public static final int CDIV_CF_NO_REPLY = 2; - public static final int CDIV_CF_NOT_REACHABLE = 3; + public static final int CDIV_CF_UNCONDITIONAL = ImsCallForwardInfo.CDIV_CF_REASON_UNCONDITIONAL; + public static final int CDIV_CF_BUSY = ImsCallForwardInfo.CDIV_CF_REASON_BUSY; + public static final int CDIV_CF_NO_REPLY = ImsCallForwardInfo.CDIV_CF_REASON_NO_REPLY; + public static final int CDIV_CF_NOT_REACHABLE = ImsCallForwardInfo.CDIV_CF_REASON_NOT_REACHABLE; // For CS service code: 002 - public static final int CDIV_CF_ALL = 4; + public static final int CDIV_CF_ALL = ImsCallForwardInfo.CDIV_CF_REASON_ALL; // For CS service code: 004 - public static final int CDIV_CF_ALL_CONDITIONAL = 5; + public static final int CDIV_CF_ALL_CONDITIONAL = + ImsCallForwardInfo.CDIV_CF_REASON_ALL_CONDITIONAL; // It's only supported in the IMS service (CS does not define it). // IR.92 recommends that an UE activates both the CFNRc and the CFNL // (CDIV using condition not-registered) to the same target. - public static final int CDIV_CF_NOT_LOGGED_IN = 6; + public static final int CDIV_CF_NOT_LOGGED_IN = ImsCallForwardInfo.CDIV_CF_REASON_NOT_LOGGED_IN; /** * CB (Communication Barring, 3GPP TS 24.611) */ // Barring of All Incoming Calls - public static final int CB_BAIC = 1; + public static final int CB_BAIC = ImsUtImplBase.CALL_BARRING_ALL_INCOMING; // Barring of All Outgoing Calls - public static final int CB_BAOC = 2; + public static final int CB_BAOC = ImsUtImplBase.CALL_BARRING_ALL_OUTGOING; // Barring of Outgoing International Calls - public static final int CB_BOIC = 3; + public static final int CB_BOIC = ImsUtImplBase.CALL_BARRING_OUTGOING_INTL; // Barring of Outgoing International Calls - excluding Home Country - public static final int CB_BOIC_EXHC = 4; + public static final int CB_BOIC_EXHC = ImsUtImplBase.CALL_BARRING_OUTGOING_INTL_EXCL_HOME; // Barring of Incoming Calls - when roaming - public static final int CB_BIC_WR = 5; + public static final int CB_BIC_WR = ImsUtImplBase.CALL_BLOCKING_INCOMING_WHEN_ROAMING; // Barring of Anonymous Communication Rejection (ACR) - a particular case of ICB service - public static final int CB_BIC_ACR = 6; + public static final int CB_BIC_ACR = ImsUtImplBase.CALL_BARRING_ANONYMOUS_INCOMING; // Barring of All Calls - public static final int CB_BA_ALL = 7; + public static final int CB_BA_ALL = ImsUtImplBase.CALL_BARRING_ALL; // Barring of Outgoing Services (Service Code 333 - 3GPP TS 22.030 Table B-1) - public static final int CB_BA_MO = 8; + public static final int CB_BA_MO = ImsUtImplBase.CALL_BARRING_OUTGOING_ALL_SERVICES; // Barring of Incoming Services (Service Code 353 - 3GPP TS 22.030 Table B-1) - public static final int CB_BA_MT = 9; + public static final int CB_BA_MT = ImsUtImplBase.CALL_BARRING_INCOMING_ALL_SERVICES; // Barring of Specific Incoming calls - public static final int CB_BS_MT = 10; + public static final int CB_BS_MT = ImsUtImplBase.CALL_BARRING_SPECIFIC_INCOMING_CALLS; /** * Invalid result value. */ - public static final int INVALID = (-1); + public static final int INVALID = ImsUtImplBase.INVALID_RESULT; diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl index c07a1711b32e..79cdce8cba46 100644 --- a/telephony/java/com/android/internal/telephony/ISms.aidl +++ b/telephony/java/com/android/internal/telephony/ISms.aidl @@ -145,10 +145,13 @@ interface ISms { * be automatically persisted in the SMS db. It only affects messages sent * by a non-default SMS app. Currently only the carrier app can set this * parameter to false to skip auto message persistence. + * @param messageId An id that uniquely identifies the message requested to be sent. + * Used for logging and diagnostics purposes. The id may be 0. */ void sendTextForSubscriber(in int subId, String callingPkg, in String destAddr, in String scAddr, in String text, in PendingIntent sentIntent, - in PendingIntent deliveryIntent, in boolean persistMessageForNonDefaultSmsApp); + in PendingIntent deliveryIntent, in boolean persistMessageForNonDefaultSmsApp, + in long messageId); /** * Send an SMS. Internal use only. @@ -270,11 +273,14 @@ interface ISms { * be automatically persisted in the SMS db. It only affects messages sent * by a non-default SMS app. Currently only the carrier app can set this * parameter to false to skip auto message persistence. + * @param messageId An id that uniquely identifies the message requested to be sent. + * Used for logging and diagnostics purposes. The id may be 0. */ void sendMultipartTextForSubscriber(in int subId, String callingPkg, in String destinationAddress, in String scAddress, in List<String> parts, in List<PendingIntent> sentIntents, - in List<PendingIntent> deliveryIntents, in boolean persistMessageForNonDefaultSmsApp); + in List<PendingIntent> deliveryIntents, in boolean persistMessageForNonDefaultSmsApp, + in long messageId); /** * Send a multi-part text based SMS with options using Subscription Id. diff --git a/telephony/java/com/android/internal/telephony/ISmsImplBase.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java index ddd3457b3b4d..db0b8e528c87 100644 --- a/telephony/java/com/android/internal/telephony/ISmsImplBase.java +++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java @@ -61,7 +61,8 @@ public class ISmsImplBase extends ISms.Stub { @Override public void sendTextForSubscriber(int subId, String callingPkg, String destAddr, String scAddr, String text, PendingIntent sentIntent, - PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp) { + PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp, + long messageId) { throw new UnsupportedOperationException(); } @@ -90,7 +91,8 @@ public class ISmsImplBase extends ISms.Stub { public void sendMultipartTextForSubscriber(int subId, String callingPkg, String destinationAddress, String scAddress, List<String> parts, List<PendingIntent> sentIntents, - List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) { + List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp, + long messageId) { throw new UnsupportedOperationException(); } diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index db8c84560282..4d677545bc39 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -16,6 +16,7 @@ package com.android.internal.telephony; import android.compat.annotation.UnsupportedAppUsage; +import android.telephony.data.ApnSetting; /** * @hide @@ -125,32 +126,32 @@ public class PhoneConstants { * APN_TYPE_ALL is a special type to indicate that this APN entry can * service all data connections. */ - public static final String APN_TYPE_ALL = "*"; + public static final String APN_TYPE_ALL = ApnSetting.TYPE_ALL_STRING; /** APN type for default data traffic */ - public static final String APN_TYPE_DEFAULT = "default"; + public static final String APN_TYPE_DEFAULT = ApnSetting.TYPE_DEFAULT_STRING; /** APN type for MMS traffic */ - public static final String APN_TYPE_MMS = "mms"; + public static final String APN_TYPE_MMS = ApnSetting.TYPE_MMS_STRING; /** APN type for SUPL assisted GPS */ - public static final String APN_TYPE_SUPL = "supl"; + public static final String APN_TYPE_SUPL = ApnSetting.TYPE_SUPL_STRING; /** APN type for DUN traffic */ - public static final String APN_TYPE_DUN = "dun"; + public static final String APN_TYPE_DUN = ApnSetting.TYPE_DUN_STRING; /** APN type for HiPri traffic */ - public static final String APN_TYPE_HIPRI = "hipri"; + public static final String APN_TYPE_HIPRI = ApnSetting.TYPE_HIPRI_STRING; /** APN type for FOTA */ - public static final String APN_TYPE_FOTA = "fota"; + public static final String APN_TYPE_FOTA = ApnSetting.TYPE_FOTA_STRING; /** APN type for IMS */ - public static final String APN_TYPE_IMS = "ims"; + public static final String APN_TYPE_IMS = ApnSetting.TYPE_IMS_STRING; /** APN type for CBS */ - public static final String APN_TYPE_CBS = "cbs"; + public static final String APN_TYPE_CBS = ApnSetting.TYPE_CBS_STRING; /** APN type for IA Initial Attach APN */ - public static final String APN_TYPE_IA = "ia"; + public static final String APN_TYPE_IA = ApnSetting.TYPE_IA_STRING; /** APN type for Emergency PDN. This is not an IA apn, but is used * for access to carrier services in an emergency call situation. */ - public static final String APN_TYPE_EMERGENCY = "emergency"; + public static final String APN_TYPE_EMERGENCY = ApnSetting.TYPE_EMERGENCY_STRING; /** APN type for Mission Critical Services */ - public static final String APN_TYPE_MCX = "mcx"; + public static final String APN_TYPE_MCX = ApnSetting.TYPE_MCX_STRING; /** APN type for XCAP */ - public static final String APN_TYPE_XCAP = "xcap"; + public static final String APN_TYPE_XCAP = ApnSetting.TYPE_XCAP_STRING; /** Array of all APN types */ public static final String[] APN_TYPES = {APN_TYPE_DEFAULT, APN_TYPE_MMS, diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java index 11d5b250d752..1c6920986318 100644 --- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java +++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; @@ -74,6 +75,7 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { final String typeName = ConnectivityManager.getNetworkTypeName(type); mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock"); mNetworkCapabilities = new NetworkCapabilities(); + mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED); mNetworkCapabilities.addTransportType(transport); switch (transport) { case TRANSPORT_ETHERNET: @@ -206,13 +208,11 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { } public void suspend() { - mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null); - mNetworkAgent.sendNetworkInfo(mNetworkInfo); + removeCapability(NET_CAPABILITY_NOT_SUSPENDED); } public void resume() { - mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); - mNetworkAgent.sendNetworkInfo(mNetworkInfo); + addCapability(NET_CAPABILITY_NOT_SUSPENDED); } public void disconnect() { diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index 7bbac137f998..d290acae1967 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -443,7 +443,7 @@ int collate_atoms(const Descriptor *descriptor, Atoms *atoms) { AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name()); vector<java_type_t> nonChainedSignature; if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) { - auto it = atoms->non_chained_signatures_to_modules.find(signature); + auto it = atoms->non_chained_signatures_to_modules.find(nonChainedSignature); if (it == atoms->non_chained_signatures_to_modules.end()) { set<string> modules_non_chained; if (atomDecl.hasModule) { diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp index b09dcd5efb9c..c29936b96c14 100644 --- a/tools/stats_log_api_gen/java_writer.cpp +++ b/tools/stats_log_api_gen/java_writer.cpp @@ -73,7 +73,7 @@ static int write_java_methods( java_type_name(chainField.javaType), chainField.name.c_str()); } } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", SparseArray<Object> valueMap"); + fprintf(out, ", android.util.SparseArray<Object> valueMap"); } else { fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex); } @@ -142,16 +142,16 @@ static int write_java_methods( fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str()); fprintf(out, - "%s SparseIntArray intMap = null;\n", + "%s android.util.SparseIntArray intMap = null;\n", indent.c_str()); fprintf(out, - "%s SparseLongArray longMap = null;\n", + "%s android.util.SparseLongArray longMap = null;\n", indent.c_str()); fprintf(out, - "%s SparseArray<String> stringMap = null;\n", + "%s android.util.SparseArray<String> stringMap = null;\n", indent.c_str()); fprintf(out, - "%s SparseArray<Float> floatMap = null;\n", + "%s android.util.SparseArray<Float> floatMap = null;\n", indent.c_str()); fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str()); @@ -165,7 +165,7 @@ static int write_java_methods( fprintf(out, "%s if (null == intMap) {\n", indent.c_str()); fprintf(out, - "%s intMap = new SparseIntArray();\n", indent.c_str()); + "%s intMap = new android.util.SparseIntArray();\n", indent.c_str()); fprintf(out, "%s }\n", indent.c_str()); fprintf(out, @@ -175,7 +175,7 @@ static int write_java_methods( fprintf(out, "%s if (null == longMap) {\n", indent.c_str()); fprintf(out, - "%s longMap = new SparseLongArray();\n", indent.c_str()); + "%s longMap = new android.util.SparseLongArray();\n", indent.c_str()); fprintf(out, "%s }\n", indent.c_str()); fprintf(out, @@ -185,7 +185,7 @@ static int write_java_methods( fprintf(out, "%s if (null == stringMap) {\n", indent.c_str()); fprintf(out, - "%s stringMap = new SparseArray<>();\n", indent.c_str()); + "%s stringMap = new android.util.SparseArray<>();\n", indent.c_str()); fprintf(out, "%s }\n", indent.c_str()); fprintf(out, @@ -195,7 +195,7 @@ static int write_java_methods( fprintf(out, "%s if (null == floatMap) {\n", indent.c_str()); fprintf(out, - "%s floatMap = new SparseArray<>();\n", indent.c_str()); + "%s floatMap = new android.util.SparseArray<>();\n", indent.c_str()); fprintf(out, "%s }\n", indent.c_str()); fprintf(out, @@ -253,7 +253,8 @@ static int write_java_methods( int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl, const string& moduleName, const string& javaClass, - const string& javaPackage, const bool supportQ) { + const string& javaPackage, const bool supportQ, + const bool supportWorkSource) { // Print prelude fprintf(out, "// This file is autogenerated\n"); fprintf(out, "\n"); @@ -265,25 +266,9 @@ int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attribut fprintf(out, "import android.os.SystemClock;\n"); } - if (DEFAULT_MODULE_NAME == moduleName) { - // Mainline modules don't use WorkSource logging. - fprintf(out, "import android.os.WorkSource;\n"); - - // SparseArray is used for writing KeyValuePairs; not supported for Mainline modules. - fprintf(out, "import android.util.SparseArray;\n"); - fprintf(out, "import android.util.SparseIntArray;\n"); - fprintf(out, "import android.util.SparseLongArray;\n"); - } - fprintf(out, "import android.util.StatsEvent;\n"); fprintf(out, "import android.util.StatsLog;\n"); - if (DEFAULT_MODULE_NAME == moduleName) { - // List is used for WorkSource writing. Only needed for default module. - fprintf(out, "\n"); - fprintf(out, "import java.util.List;\n"); - } - fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "/**\n"); @@ -305,7 +290,7 @@ int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attribut out, atoms.signatures_to_modules, attributionDecl, moduleName, supportQ); errors += write_java_non_chained_methods( out, atoms.non_chained_signatures_to_modules, moduleName); - if (DEFAULT_MODULE_NAME == moduleName) { + if (supportWorkSource) { errors += write_java_work_source_methods(out, atoms.signatures_to_modules, moduleName); } diff --git a/tools/stats_log_api_gen/java_writer.h b/tools/stats_log_api_gen/java_writer.h index 9324b23e4025..5b78f059c5b9 100644 --- a/tools/stats_log_api_gen/java_writer.h +++ b/tools/stats_log_api_gen/java_writer.h @@ -31,8 +31,9 @@ namespace stats_log_api_gen { using namespace std; int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl, - const string& moduleName, const string& javaClass, - const string& javaPackage, const bool supportQ); + const string& moduleName, const string& javaClass, + const string& javaPackage, const bool supportQ, + const bool supportWorkSource); } // namespace stats_log_api_gen } // namespace android diff --git a/tools/stats_log_api_gen/java_writer_q.cpp b/tools/stats_log_api_gen/java_writer_q.cpp index af5055a2e685..f8661294821e 100644 --- a/tools/stats_log_api_gen/java_writer_q.cpp +++ b/tools/stats_log_api_gen/java_writer_q.cpp @@ -382,7 +382,7 @@ static void write_java_method( java_type_name(chainField.javaType), chainField.name.c_str()); } } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", SparseArray<Object> value_map"); + fprintf(out, ", android.util.SparseArray<Object> value_map"); } else { fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex); } @@ -393,16 +393,13 @@ static void write_java_method( } } -int write_stats_log_java_q(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl) { +int write_stats_log_java_q(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl, + const bool supportWorkSource) { // Print prelude fprintf(out, "// This file is autogenerated\n"); fprintf(out, "\n"); fprintf(out, "package android.util;\n"); fprintf(out, "\n"); - fprintf(out, "import android.os.WorkSource;\n"); - fprintf(out, "import android.util.SparseArray;\n"); - fprintf(out, "import java.util.List;\n"); - fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "/**\n"); fprintf(out, " * API For logging statistics events.\n"); @@ -418,16 +415,19 @@ int write_stats_log_java_q(FILE* out, const Atoms& atoms, const AtomDecl &attrib write_java_method(out, "write", atoms.signatures_to_modules, attributionDecl); write_java_method(out, "write_non_chained", atoms.non_chained_signatures_to_modules, attributionDecl); - write_java_work_source_methods(out, atoms.signatures_to_modules, DEFAULT_MODULE_NAME); + if (supportWorkSource) { + write_java_work_source_methods(out, atoms.signatures_to_modules, DEFAULT_MODULE_NAME); + } fprintf(out, "}\n"); return 0; } -int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl, - const string& moduleName, const string& javaClass, - const string& javaPackage) { +int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms, + const AtomDecl &attributionDecl, const string& moduleName, + const string& javaClass, const string& javaPackage, + const bool supportWorkSource) { // Print prelude fprintf(out, "// This file is autogenerated\n"); fprintf(out, "\n"); @@ -438,8 +438,6 @@ int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms, const AtomD fprintf(out, "import android.util.StatsLog;\n"); fprintf(out, "import android.os.SystemClock;\n"); fprintf(out, "\n"); - fprintf(out, "import java.util.ArrayList;\n"); - fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "/**\n"); fprintf(out, " * Utility class for logging statistics events.\n"); @@ -459,6 +457,9 @@ int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms, const AtomD moduleName, " "); errors += write_java_non_chained_methods(out, atoms.non_chained_signatures_to_modules, moduleName); + if (supportWorkSource) { + errors += write_java_work_source_methods(out, atoms.signatures_to_modules, moduleName); + } fprintf(out, "}\n"); diff --git a/tools/stats_log_api_gen/java_writer_q.h b/tools/stats_log_api_gen/java_writer_q.h index 96ac745beef8..36df1d8edbee 100644 --- a/tools/stats_log_api_gen/java_writer_q.h +++ b/tools/stats_log_api_gen/java_writer_q.h @@ -46,11 +46,12 @@ void write_java_helpers_for_q_schema_methods( const string& indent); #if defined(STATS_SCHEMA_LEGACY) -int write_stats_log_java_q(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl); +int write_stats_log_java_q(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl, + const bool supportWorkSource); int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl, const string& moduleName, const string& javaClass, - const string& javaPackage); + const string& javaPackage, const bool supportWorkSource); #endif } // namespace stats_log_api_gen } // namespace android diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 00a370484823..6089532e1ca4 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -512,6 +512,7 @@ print_usage() fprintf(stderr, " Optional for Java with module.\n"); fprintf(stderr, " Default is \"StatsLogInternal\"\n"); fprintf(stderr, " --supportQ Include support for Android Q.\n"); + fprintf(stderr, " --worksource Include support for logging WorkSource objects.\n"); } /** @@ -534,6 +535,7 @@ run(int argc, char const*const* argv) string javaPackage = DEFAULT_JAVA_PACKAGE; string javaClass = DEFAULT_JAVA_CLASS; bool supportQ = false; + bool supportWorkSource = false; int index = 1; while (index < argc) { @@ -626,6 +628,8 @@ run(int argc, char const*const* argv) atomsInfoCppHeaderImport = argv[index]; } else if (0 == strcmp("--supportQ", argv[index])) { supportQ = true; + } else if (0 == strcmp("--worksource", argv[index])) { + supportWorkSource = true; } index++; @@ -728,19 +732,15 @@ run(int argc, char const*const* argv) fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str()); return 1; } - // If this is for a specific module, the java package must also be provided. - if (moduleName != DEFAULT_MODULE_NAME && javaPackage== DEFAULT_JAVA_PACKAGE) { - fprintf(stderr, "Must supply --javaPackage if supplying a specific module\n"); - return 1; - } #if defined(STATS_SCHEMA_LEGACY) if (moduleName == DEFAULT_MODULE_NAME) { errorCount = android::stats_log_api_gen::write_stats_log_java_q( - out, atoms, attributionDecl); + out, atoms, attributionDecl, supportWorkSource); } else { errorCount = android::stats_log_api_gen::write_stats_log_java_q_for_module( - out, atoms, attributionDecl, moduleName, javaClass, javaPackage); + out, atoms, attributionDecl, moduleName, javaClass, javaPackage, + supportWorkSource); } #else @@ -749,7 +749,8 @@ run(int argc, char const*const* argv) javaPackage = "android.util"; } errorCount = android::stats_log_api_gen::write_stats_log_java( - out, atoms, attributionDecl, moduleName, javaClass, javaPackage, supportQ); + out, atoms, attributionDecl, moduleName, javaClass, javaPackage, supportQ, + supportWorkSource); #endif fclose(out); diff --git a/tools/stats_log_api_gen/utils.cpp b/tools/stats_log_api_gen/utils.cpp index c65d1901ea7e..8c4abe43a49b 100644 --- a/tools/stats_log_api_gen/utils.cpp +++ b/tools/stats_log_api_gen/utils.cpp @@ -334,7 +334,7 @@ void write_java_usage(FILE* out, const string& method_name, const string& atom_c if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { fprintf(out, ", android.os.WorkSource workSource"); } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", SparseArray<Object> value_map"); + fprintf(out, ", android.util.SparseArray<Object> value_map"); } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) { fprintf(out, ", byte[] %s", field->name.c_str()); } else { @@ -442,7 +442,7 @@ int write_java_work_source_methods( for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); arg++) { if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { - fprintf(out, ", WorkSource ws"); + fprintf(out, ", android.os.WorkSource ws"); } else { fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex); } @@ -464,9 +464,10 @@ int write_java_work_source_methods( fprintf(out, " }\n"); // close for-loop // write() component. - fprintf(out, " List<WorkSource.WorkChain> workChains = ws.getWorkChains();\n"); + fprintf(out, " java.util.List<android.os.WorkSource.WorkChain> workChains = " + "ws.getWorkChains();\n"); fprintf(out, " if (workChains != null) {\n"); - fprintf(out, " for (WorkSource.WorkChain wc : workChains) {\n"); + fprintf(out, " for (android.os.WorkSource.WorkChain wc : workChains) {\n"); fprintf(out, " write(code"); for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) { if (argIndex == attributionArg) { diff --git a/wifi/Android.bp b/wifi/Android.bp index 4c9ee854536e..5ef892d005ac 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -114,6 +114,8 @@ droidstubs { ":framework-annotations", ":framework-wifi-updatable-sources", ], + // This is needed as IOnWifiActivityEnergyInfoListener.aidl in framework-wifi depends on + // WifiActivityEnergyInfo.aidl in framework-minus-apex aidl: { include_dirs: ["frameworks/base/core/java"], }, @@ -125,11 +127,6 @@ droidstubs { java_library { name: "framework-wifi-stubs", srcs: [":framework-wifi-stubs-srcs"], - aidl: { - export_include_dirs: [ - "java", - ], - }, sdk_version: "core_current", libs: ["android_system_stubs_current"], installable: false, diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt index 8f720407f4d7..950361c1b244 100644 --- a/wifi/jarjar-rules.txt +++ b/wifi/jarjar-rules.txt @@ -1,5 +1,7 @@ rule android.net.InterfaceConfigurationParcel* @0 rule android.net.InterfaceConfiguration* com.android.server.x.wifi.net.InterfaceConfiguration@1 +rule android.net.NetworkFactory* com.android.server.x.wifi.net.NetworkFactory@1 +rule android.net.util.NetUtils* com.android.server.x.wifi.net.util.NetUtils@1 # We don't jar-jar the entire package because, we still use some classes (like # AsyncChannel in com.android.internal.util) from these packages which are not @@ -32,6 +34,8 @@ rule android.sysprop.** com.android.server.x.wifi.sysprop.@1 rule android.content.pm.BaseParceledListSlice* android.x.net.wifi.util.BaseParceledListSlice@1 rule android.content.pm.ParceledListSlice* android.x.net.wifi.util.ParceledListSlice@1 rule android.net.shared.Inet4AddressUtils* android.x.net.wifi.util.Inet4AddressUtils@1 +rule android.net.util.MacAddressUtils* android.x.net.wifi.util.MacAddressUtils@1 +rule android.net.util.nsd.DnsSdTxtRecord* android.x.net.wifi.util.nsd.DnsSdTxtRecord@1 rule android.os.HandlerExecutor* android.x.net.wifi.util.HandlerExecutor@1 rule android.telephony.Annotation* android.x.net.wifi.util.TelephonyAnnotation@1 rule com.android.internal.util.AsyncChannel* android.x.net.wifi.util.AsyncChannel@1 diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl index 9c4f4606a9bc..fd236107bc6e 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java +++ b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl @@ -14,17 +14,16 @@ * limitations under the License. */ -package android.media.tv.tuner.frontend; +package android.net.wifi; /** - * Frontend Callback. + * Interface for Wi-Fi network score callback. * * @hide */ -public interface FrontendCallback { +oneway interface IScoreChangeCallback +{ + void onStatusChange(int sessionId, boolean exiting); - /** - * Invoked when there is a frontend event. - */ - void onEvent(int frontendEventType); + void onTriggerUpdateOfWifiUsabilityStats(int sessionId); } diff --git a/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl new file mode 100644 index 000000000000..d9a3b0109a09 --- /dev/null +++ b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl @@ -0,0 +1,33 @@ +/* + * 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.wifi; + +import android.net.wifi.IScoreChangeCallback; + +/** + * Interface for Wi-Fi connected network scorer. + * + * @hide + */ +oneway interface IWifiConnectedNetworkScorer +{ + void start(int sessionId); + + void stop(int sessionId); + + void setScoreChangeCallback(IScoreChangeCallback cbImpl); +} diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 67f166327b56..5a98ac86e783 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -35,6 +35,7 @@ import android.net.wifi.ISoftApCallback; import android.net.wifi.ISuggestionConnectionStatusListener; import android.net.wifi.ITrafficStateCallback; import android.net.wifi.ITxPacketCountListener; +import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.ScanResult; import android.net.wifi.SoftApConfiguration; import android.net.wifi.WifiConfiguration; @@ -254,4 +255,8 @@ interface IWifiManager int calculateSignalLevel(int rssi); List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(in List<ScanResult> scanResults); + + boolean setWifiConnectedNetworkScorer(in IBinder binder, in IWifiConnectedNetworkScorer scorer); + + void clearWifiConnectedNetworkScorer(); } diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 7cd00b9dbb56..f2a875b43538 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -124,11 +124,21 @@ public class WifiInfo implements Parcelable { private int mTxLinkSpeed; /** + * Max supported Tx(transmit) link speed in Mbps + */ + private int mMaxSupportedTxLinkSpeed; + + /** * Rx(receive) Link speed in Mbps */ private int mRxLinkSpeed; /** + * Max supported Rx(receive) link speed in Mbps + */ + private int mMaxSupportedRxLinkSpeed; + + /** * Frequency in MHz */ public static final String FREQUENCY_UNITS = "MHz"; @@ -303,6 +313,8 @@ public class WifiInfo implements Parcelable { setLinkSpeed(LINK_SPEED_UNKNOWN); setTxLinkSpeedMbps(LINK_SPEED_UNKNOWN); setRxLinkSpeedMbps(LINK_SPEED_UNKNOWN); + setMaxSupportedTxLinkSpeedMbps(LINK_SPEED_UNKNOWN); + setMaxSupportedRxLinkSpeedMbps(LINK_SPEED_UNKNOWN); setFrequency(-1); setMeteredHint(false); setEphemeral(false); @@ -356,6 +368,8 @@ public class WifiInfo implements Parcelable { mRxSuccessRate = source.mRxSuccessRate; score = source.score; mWifiStandard = source.mWifiStandard; + mMaxSupportedTxLinkSpeed = source.mMaxSupportedTxLinkSpeed; + mMaxSupportedRxLinkSpeed = source.mMaxSupportedRxLinkSpeed; } } @@ -552,6 +566,15 @@ public class WifiInfo implements Parcelable { } /** + * Returns the maximum supported transmit link speed in Mbps + * @return the max supported tx link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is + * unknown. @see #LINK_SPEED_UNKNOWN + */ + public int getMaxSupportedTxLinkSpeedMbps() { + return mMaxSupportedTxLinkSpeed; + } + + /** * Update the last transmitted packet bit rate in Mbps. * @hide */ @@ -560,6 +583,14 @@ public class WifiInfo implements Parcelable { } /** + * Set the maximum supported transmit link speed in Mbps + * @hide + */ + public void setMaxSupportedTxLinkSpeedMbps(int maxSupportedTxLinkSpeed) { + mMaxSupportedTxLinkSpeed = maxSupportedTxLinkSpeed; + } + + /** * Returns the current receive link speed in Mbps. * @return the Rx link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is unknown. * @see #LINK_SPEED_UNKNOWN @@ -570,6 +601,15 @@ public class WifiInfo implements Parcelable { } /** + * Returns the maximum supported receive link speed in Mbps + * @return the max supported Rx link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is + * unknown. @see #LINK_SPEED_UNKNOWN + */ + public int getMaxSupportedRxLinkSpeedMbps() { + return mMaxSupportedRxLinkSpeed; + } + + /** * Update the last received packet bit rate in Mbps. * @hide */ @@ -578,6 +618,14 @@ public class WifiInfo implements Parcelable { } /** + * Set the maximum supported receive link speed in Mbps + * @hide + */ + public void setMaxSupportedRxLinkSpeedMbps(int maxSupportedRxLinkSpeed) { + mMaxSupportedRxLinkSpeed = maxSupportedRxLinkSpeed; + } + + /** * Returns the current frequency in {@link #FREQUENCY_UNITS}. * @return the frequency. * @see #FREQUENCY_UNITS @@ -864,7 +912,11 @@ public class WifiInfo implements Parcelable { .append(", RSSI: ").append(mRssi) .append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS) .append(", Tx Link speed: ").append(mTxLinkSpeed).append(LINK_SPEED_UNITS) + .append(", Max Supported Tx Link speed: ") + .append(mMaxSupportedTxLinkSpeed).append(LINK_SPEED_UNITS) .append(", Rx Link speed: ").append(mRxLinkSpeed).append(LINK_SPEED_UNITS) + .append(", Max Supported Rx Link speed: ") + .append(mMaxSupportedRxLinkSpeed).append(LINK_SPEED_UNITS) .append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS) .append(", Net ID: ").append(mNetworkId) .append(", Metered hint: ").append(mMeteredHint) @@ -917,6 +969,8 @@ public class WifiInfo implements Parcelable { dest.writeString(mFqdn); dest.writeString(mProviderFriendlyName); dest.writeInt(mWifiStandard); + dest.writeInt(mMaxSupportedTxLinkSpeed); + dest.writeInt(mMaxSupportedRxLinkSpeed); } /** Implement the Parcelable interface {@hide} */ @@ -959,6 +1013,8 @@ public class WifiInfo implements Parcelable { info.mFqdn = in.readString(); info.mProviderFriendlyName = in.readString(); info.mWifiStandard = in.readInt(); + info.mMaxSupportedTxLinkSpeed = in.readInt(); + info.mMaxSupportedRxLinkSpeed = in.readInt(); return info; } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index ec3de43ee85a..64d4eafe71bf 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -5806,4 +5806,186 @@ public class WifiManager { return new SparseArray<>(); } } + + /** + * Callback interface for framework to receive network status changes and trigger of updating + * {@link WifiUsabilityStatsEntry}. + * + * @hide + */ + @SystemApi + public interface ScoreChangeCallback { + /** + * Called by applications to indicate network status. + * + * @param sessionId The ID to indicate current Wi-Fi network connection obtained from + * {@link WifiConnectedNetworkScorer#start(int)}. + * @param isUsable The bit to indicate whether current Wi-Fi network is usable or not. + * Populated by connected network scorer in applications. + */ + void onStatusChange(int sessionId, boolean isUsable); + + /** + * Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}. + * To receive update applications need to add WifiUsabilityStatsEntry listener. See + * {@link addOnWifiUsabilityStatsListener(Executor, OnWifiUsabilityStatsListener)}. + * + * @param sessionId The ID to indicate current Wi-Fi network connection obtained from + * {@link WifiConnectedNetworkScorer#start(int)}. + */ + void onTriggerUpdateOfWifiUsabilityStats(int sessionId); + } + + /** + * Callback proxy for {@link ScoreChangeCallback} objects. + * + * @hide + */ + private class ScoreChangeCallbackProxy implements ScoreChangeCallback { + private final IScoreChangeCallback mScoreChangeCallback; + + private ScoreChangeCallbackProxy(IScoreChangeCallback callback) { + mScoreChangeCallback = callback; + } + + @Override + public void onStatusChange(int sessionId, boolean isUsable) { + try { + mScoreChangeCallback.onStatusChange(sessionId, isUsable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void onTriggerUpdateOfWifiUsabilityStats(int sessionId) { + try { + mScoreChangeCallback.onTriggerUpdateOfWifiUsabilityStats(sessionId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Interface for Wi-Fi connected network scorer. Should be implemented by applications and set + * when calling + * {@link WifiManager#setWifiConnectedNetworkScorer(Executor, WifiConnectedNetworkScorer)}. + * + * @hide + */ + @SystemApi + public interface WifiConnectedNetworkScorer { + /** + * Called by framework to indicate the start of a network connection. + * @param sessionId The ID to indicate current Wi-Fi network connection. + */ + void start(int sessionId); + + /** + * Called by framework to indicate the end of a network connection. + * @param sessionId The ID to indicate current Wi-Fi network connection obtained from + * {@link WifiConnectedNetworkScorer#start(int)}. + */ + void stop(int sessionId); + + /** + * Framework sets callback for score change events after application sets its scorer. + * @param cbImpl The instance for {@link WifiManager#ScoreChangeCallback}. Should be + * implemented and instantiated by framework. + */ + void setScoreChangeCallback(@NonNull ScoreChangeCallback cbImpl); + } + + /** + * Callback proxy for {@link WifiConnectedNetworkScorer} objects. + * + * @hide + */ + private class WifiConnectedNetworkScorerProxy extends IWifiConnectedNetworkScorer.Stub { + private Executor mExecutor; + private WifiConnectedNetworkScorer mScorer; + + WifiConnectedNetworkScorerProxy(Executor executor, WifiConnectedNetworkScorer scorer) { + mExecutor = executor; + mScorer = scorer; + } + + @Override + public void start(int sessionId) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "WifiConnectedNetworkScorer: " + "start: sessionId=" + sessionId); + } + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mScorer.start(sessionId)); + } + + @Override + public void stop(int sessionId) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "WifiConnectedNetworkScorer: " + "stop: sessionId=" + sessionId); + } + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mScorer.stop(sessionId)); + } + + @Override + public void setScoreChangeCallback(IScoreChangeCallback cbImpl) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "WifiConnectedNetworkScorer: " + + "setScoreChangeCallback: cbImpl=" + cbImpl); + } + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mScorer.setScoreChangeCallback( + new ScoreChangeCallbackProxy(cbImpl))); + } + } + + /** + * Set a callback for Wi-Fi connected network scorer. See {@link WifiConnectedNetworkScorer}. + * Only a single scorer can be set. Caller will be invoked periodically by framework to inform + * client about start and stop of Wi-Fi connection. Caller can clear a previously set scorer + * using {@link clearWifiConnectedNetworkScorer()}. + * + * @param executor The executor on which callback will be invoked. + * @param scorer Scorer for Wi-Fi network implemented by application. + * @return true Scorer is set successfully. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) + public boolean setWifiConnectedNetworkScorer(@NonNull @CallbackExecutor Executor executor, + @NonNull WifiConnectedNetworkScorer scorer) { + if (executor == null) throw new IllegalArgumentException("executor cannot be null"); + if (scorer == null) throw new IllegalArgumentException("scorer cannot be null"); + if (mVerboseLoggingEnabled) { + Log.v(TAG, "setWifiConnectedNetworkScorer: scorer=" + scorer); + } + try { + return mService.setWifiConnectedNetworkScorer(new Binder(), + new WifiConnectedNetworkScorerProxy(executor, scorer)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Allow caller to clear a previously set scorer. After calling this method, + * client will no longer receive information about start and stop of Wi-Fi connection. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) + public void clearWifiConnectedNetworkScorer() { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "clearWifiConnectedNetworkScorer"); + } + try { + mService.clearWifiConnectedNetworkScorer(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index 07afd7fb6714..444e1ef041e8 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -20,7 +20,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityThread; +import android.app.Application; import android.net.MacAddress; import android.net.MatchAllNetworkSpecifier; import android.net.NetworkRequest; @@ -30,8 +30,11 @@ import android.os.Parcelable; import android.os.PatternMatcher; import android.os.Process; import android.text.TextUtils; +import android.util.Log; import android.util.Pair; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.charset.CharsetEncoder; import java.nio.charset.StandardCharsets; import java.util.Objects; @@ -41,6 +44,7 @@ import java.util.Objects; * {@link WifiNetworkSpecifier.Builder} class to create an instance. */ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parcelable { + private static final String TAG = "WifiNetworkSpecifier"; /** * Builder used to create {@link WifiNetworkSpecifier} objects. @@ -436,7 +440,22 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc mBssidPatternMatcher, buildWifiConfiguration(), Process.myUid(), - ActivityThread.currentApplication().getApplicationContext().getOpPackageName()); + getCurrentApplicationReflectively().getApplicationContext().getOpPackageName()); + } + + // TODO(b/144102365): Remove once refactor is complete + private static Application getCurrentApplicationReflectively() { + try { + // reflection for static method android.app.ActivityThread#currentApplication() + Class<?> klass = Class.forName("android.app.ActivityThread"); + Method currentApplicationMethod = klass.getDeclaredMethod("currentApplication"); + Object result = currentApplicationMethod.invoke(null); + return (Application) result; + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException + | InvocationTargetException e) { + Log.e(TAG, "Failed to call ActivityThread#currentApplication() reflectively!", e); + throw new RuntimeException(e); + } } } diff --git a/wifi/java/android/net/wifi/wificond/DeviceWiphyCapabilities.java b/wifi/java/android/net/wifi/wificond/DeviceWiphyCapabilities.java new file mode 100644 index 000000000000..de1c7600f8ef --- /dev/null +++ b/wifi/java/android/net/wifi/wificond/DeviceWiphyCapabilities.java @@ -0,0 +1,160 @@ +/* + * 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.wifi.wificond; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.wifi.ScanResult; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.Objects; + +/** + * DeviceWiphyCapabilities for wificond + * + * @hide + */ +@SystemApi +public final class DeviceWiphyCapabilities implements Parcelable { + private static final String TAG = "DeviceWiphyCapabilities"; + + private boolean m80211nSupported; + private boolean m80211acSupported; + private boolean m80211axSupported; + + /** public constructor */ + public DeviceWiphyCapabilities() { + m80211nSupported = false; + m80211acSupported = false; + m80211axSupported = false; + } + + /** + * Get the IEEE 802.11 standard support + * + * @param standard the IEEE 802.11 standard to check on its support. + * valid values from {@link ScanResult}'s {@code WIFI_STANDARD_} + * @return {@code true} if supported, {@code false} otherwise. + */ + public boolean isWifiStandardSupported(int standard) { + switch (standard) { + case ScanResult.WIFI_STANDARD_LEGACY: + return true; + case ScanResult.WIFI_STANDARD_11N: + return m80211nSupported; + case ScanResult.WIFI_STANDARD_11AC: + return m80211acSupported; + case ScanResult.WIFI_STANDARD_11AX: + return m80211axSupported; + default: + Log.e(TAG, "isWifiStandardSupported called with invalid standard: " + standard); + return false; + } + } + + /** + * Set the IEEE 802.11 standard support + * + * @param standard the IEEE 802.11 standard to set its support. + * valid values from {@link ScanResult}'s {@code WIFI_STANDARD_} + * @param support {@code true} if supported, {@code false} otherwise. + */ + public void setWifiStandardSupport(int standard, boolean support) { + switch (standard) { + case ScanResult.WIFI_STANDARD_11N: + m80211nSupported = support; + break; + case ScanResult.WIFI_STANDARD_11AC: + m80211acSupported = support; + break; + case ScanResult.WIFI_STANDARD_11AX: + m80211axSupported = support; + break; + default: + Log.e(TAG, "setWifiStandardSupport called with invalid standard: " + standard); + } + } + + /** override comparator */ + @Override + public boolean equals(Object rhs) { + if (this == rhs) return true; + if (!(rhs instanceof DeviceWiphyCapabilities)) { + return false; + } + DeviceWiphyCapabilities capa = (DeviceWiphyCapabilities) rhs; + + return m80211nSupported == capa.m80211nSupported + && m80211acSupported == capa.m80211acSupported + && m80211axSupported == capa.m80211axSupported; + } + + /** override hash code */ + @Override + public int hashCode() { + return Objects.hash(m80211nSupported, m80211acSupported, m80211axSupported); + } + + /** implement Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** + * implement Parcelable interface + * |flags| is ignored. + */ + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeBoolean(m80211nSupported); + out.writeBoolean(m80211acSupported); + out.writeBoolean(m80211axSupported); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("m80211nSupported:").append(m80211nSupported ? "Yes" : "No"); + sb.append("m80211acSupported:").append(m80211acSupported ? "Yes" : "No"); + sb.append("m80211axSupported:").append(m80211axSupported ? "Yes" : "No"); + return sb.toString(); + } + + /** implement Parcelable interface */ + public static final @NonNull Parcelable.Creator<DeviceWiphyCapabilities> CREATOR = + new Parcelable.Creator<DeviceWiphyCapabilities>() { + /** + * Caller is responsible for providing a valid parcel. + */ + @Override + public DeviceWiphyCapabilities createFromParcel(Parcel in) { + DeviceWiphyCapabilities capabilities = new DeviceWiphyCapabilities(); + capabilities.m80211nSupported = in.readBoolean(); + capabilities.m80211acSupported = in.readBoolean(); + capabilities.m80211axSupported = in.readBoolean(); + return capabilities; + } + + @Override + public DeviceWiphyCapabilities[] newArray(int size) { + return new DeviceWiphyCapabilities[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java index f70bdac25c33..4847640b1418 100644 --- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java +++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java @@ -1052,6 +1052,22 @@ public class WifiCondManager { } /** + * Get the device phy capabilities for a given interface + */ + @Nullable public DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String ifaceName) { + if (mWificond == null) { + Log.e(TAG, "Can not query for device wiphy capabilities at this time"); + return null; + } + + try { + return mWificond.getDeviceWiphyCapabilities(ifaceName); + } catch (RemoteException e) { + return null; + } + } + + /** * Register the provided callback handler for SoftAp events. Note that the Soft AP itself is * configured using {@link #setupInterfaceForSoftApMode(String)}. * diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java index 1cf3825d2397..08822e2762f6 100644 --- a/wifi/java/com/android/server/wifi/BaseWifiService.java +++ b/wifi/java/com/android/server/wifi/BaseWifiService.java @@ -32,6 +32,7 @@ import android.net.wifi.ISoftApCallback; import android.net.wifi.ISuggestionConnectionStatusListener; import android.net.wifi.ITrafficStateCallback; import android.net.wifi.ITxPacketCountListener; +import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.IWifiManager; import android.net.wifi.ScanResult; import android.net.wifi.SoftApConfiguration; @@ -600,4 +601,15 @@ public class BaseWifiService extends IWifiManager.Stub { List<ScanResult> scanResults) { throw new UnsupportedOperationException(); } + + @Override + public boolean setWifiConnectedNetworkScorer(IBinder binder, + IWifiConnectedNetworkScorer scorer) { + throw new UnsupportedOperationException(); + } + + @Override + public void clearWifiConnectedNetworkScorer() { + throw new UnsupportedOperationException(); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java index f7612341d4b3..af85ce05f23b 100644 --- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java +++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java @@ -39,6 +39,8 @@ public class WifiInfoTest { private static final String TEST_FQDN = "test.com"; private static final String TEST_PROVIDER_NAME = "test"; private static final int TEST_WIFI_STANDARD = ScanResult.WIFI_STANDARD_11AC; + private static final int TEST_MAX_SUPPORTED_TX_LINK_SPEED_MBPS = 866; + private static final int TEST_MAX_SUPPORTED_RX_LINK_SPEED_MBPS = 1200; /** * Verify parcel write/read with WifiInfo. @@ -56,6 +58,8 @@ public class WifiInfoTest { writeWifiInfo.setProviderFriendlyName(TEST_PROVIDER_NAME); writeWifiInfo.setAppPackageName(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); Parcel parcel = Parcel.obtain(); writeWifiInfo.writeToParcel(parcel, 0); @@ -75,5 +79,26 @@ public class WifiInfoTest { 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()); + } + + /** + * Verify values after reset() + */ + @Test + public void testWifiInfoResetValue() throws Exception { + WifiInfo wifiInfo = new WifiInfo(); + wifiInfo.reset(); + assertEquals(WifiInfo.LINK_SPEED_UNKNOWN, wifiInfo.getMaxSupportedTxLinkSpeedMbps()); + assertEquals(WifiInfo.LINK_SPEED_UNKNOWN, wifiInfo.getMaxSupportedRxLinkSpeedMbps()); + assertEquals(WifiInfo.LINK_SPEED_UNKNOWN, wifiInfo.getTxLinkSpeedMbps()); + assertEquals(WifiInfo.LINK_SPEED_UNKNOWN, wifiInfo.getRxLinkSpeedMbps()); + assertEquals(WifiInfo.INVALID_RSSI, wifiInfo.getRssi()); + assertEquals(WifiManager.UNKNOWN_SSID, wifiInfo.getSSID()); + assertEquals(null, wifiInfo.getBSSID()); + assertEquals(-1, wifiInfo.getNetworkId()); } } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index 983ac8216124..1ee5374aaa69 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -82,6 +82,7 @@ import android.net.wifi.WifiManager.ScanResultsCallback; import android.net.wifi.WifiManager.SoftApCallback; import android.net.wifi.WifiManager.SuggestionConnectionStatusListener; import android.net.wifi.WifiManager.TrafficStateCallback; +import android.net.wifi.WifiManager.WifiConnectedNetworkScorer; import android.os.Binder; import android.os.Build; import android.os.Handler; @@ -138,6 +139,7 @@ public class WifiManagerTest { @Mock Executor mExecutor; @Mock Executor mAnotherExecutor; @Mock ActivityManager mActivityManager; + @Mock WifiConnectedNetworkScorer mWifiConnectedNetworkScorer; private Handler mHandler; private TestLooper mLooper; @@ -2230,4 +2232,63 @@ public class WifiManagerTest { assertEquals(testResults, mWifiManager .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(new ArrayList<>())); } + + /** + * Verify the call to setWifiConnectedNetworkScorer goes to WifiServiceImpl. + */ + @Test + public void setWifiConnectedNetworkScorerGoesToWifiServiceImpl() throws Exception { + mExecutor = new SynchronousExecutor(); + mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer); + verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class), + any(IWifiConnectedNetworkScorer.Stub.class)); + } + + /** + * Verify the call to clearWifiConnectedNetworkScorer goes to WifiServiceImpl. + */ + @Test + public void clearWifiConnectedNetworkScorerGoesToWifiServiceImpl() throws Exception { + mExecutor = new SynchronousExecutor(); + mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer); + verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class), + any(IWifiConnectedNetworkScorer.Stub.class)); + + mWifiManager.clearWifiConnectedNetworkScorer(); + verify(mWifiService).clearWifiConnectedNetworkScorer(); + } + + /** + * Verify that Wi-Fi connected scorer receives score change callback after registeration. + */ + @Test + public void verifyScorerReceiveScoreChangeCallbackAfterRegistration() throws Exception { + mExecutor = new SynchronousExecutor(); + mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer); + ArgumentCaptor<IWifiConnectedNetworkScorer.Stub> scorerCaptor = + ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class); + verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class), + scorerCaptor.capture()); + scorerCaptor.getValue().setScoreChangeCallback(any()); + mLooper.dispatchAll(); + verify(mWifiConnectedNetworkScorer).setScoreChangeCallback(any()); + } + + /** + * Verify that Wi-Fi connected scorer receives session ID when start/stop methods are called. + */ + @Test + public void verifyScorerReceiveSessionIdWhenStartStopIsCalled() throws Exception { + mExecutor = new SynchronousExecutor(); + mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer); + ArgumentCaptor<IWifiConnectedNetworkScorer.Stub> callbackCaptor = + ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class); + verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class), + callbackCaptor.capture()); + callbackCaptor.getValue().start(0); + callbackCaptor.getValue().stop(10); + mLooper.dispatchAll(); + verify(mWifiConnectedNetworkScorer).start(0); + verify(mWifiConnectedNetworkScorer).stop(10); + } } diff --git a/wifi/tests/src/android/net/wifi/wificond/DeviceWiphyCapabilitiesTest.java b/wifi/tests/src/android/net/wifi/wificond/DeviceWiphyCapabilitiesTest.java new file mode 100644 index 000000000000..1479acfe8e20 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/wificond/DeviceWiphyCapabilitiesTest.java @@ -0,0 +1,59 @@ +/* + * 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.wifi.wificond; + +import static org.junit.Assert.assertEquals; + +import android.net.wifi.ScanResult; +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.wificond.DeviceWiphyCapabilities}. + */ +@SmallTest +public class DeviceWiphyCapabilitiesTest { + @Before + public void setUp() {} + + /** + * DeviceWiphyCapabilities object can be serialized and deserialized, while keeping the + * values unchanged. + */ + @Test + public void canSerializeAndDeserialize() { + DeviceWiphyCapabilities capa = new DeviceWiphyCapabilities(); + + capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11N, true); + capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AC, true); + capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AX, false); + + Parcel parcel = Parcel.obtain(); + capa.writeToParcel(parcel, 0); + // Rewind the pointer to the head of the parcel. + parcel.setDataPosition(0); + DeviceWiphyCapabilities capaDeserialized = + DeviceWiphyCapabilities.CREATOR.createFromParcel(parcel); + + assertEquals(capa, capaDeserialized); + assertEquals(capa.hashCode(), capaDeserialized.hashCode()); + } +} diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java index f3867c1c3fdf..619c95efb173 100644 --- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java +++ b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java @@ -38,6 +38,7 @@ import static org.mockito.Mockito.when; import android.app.AlarmManager; import android.app.test.TestAlarmManager; import android.content.Context; +import android.net.wifi.ScanResult; import android.net.wifi.SoftApInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiScanner; @@ -397,7 +398,6 @@ public class WifiCondManagerTest { verify(mWifiScannerImpl).unsubscribeScanEvents(); } - /** * Verifies that tearDownInterfaces() returns false when wificond is not started. */ @@ -1036,6 +1036,25 @@ public class WifiCondManagerTest { verify(mSendMgmtFrameCallback).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT); } + /** + * Tests getDeviceWiphyCapabililties + */ + @Test + public void testGetDeviceWiphyCapabilities() throws Exception { + DeviceWiphyCapabilities capaExpected = new DeviceWiphyCapabilities(); + + capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11N, true); + capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AC, true); + capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AX, false); + + when(mWificond.getDeviceWiphyCapabilities(TEST_INTERFACE_NAME)) + .thenReturn(capaExpected); + + DeviceWiphyCapabilities capaActual = + mWificondControl.getDeviceWiphyCapabilities(TEST_INTERFACE_NAME); + assertEquals(capaExpected, capaActual); + } + // Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it // matches the provided frequency set and ssid set. private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> { |