Merge "Support accessibility on embedded hierarchies (1/n)"
diff --git a/Android.bp b/Android.bp
index 8c85631..a7ac094 100644
--- a/Android.bp
+++ b/Android.bp
@@ -226,6 +226,7 @@
":framework-wifi-non-updatable-sources",
":PacProcessor-aidl-sources",
":ProxyHandler-aidl-sources",
+ ":net-utils-framework-common-srcs",
// AIDL from frameworks/base/native/
":platform-compat-native-aidl",
@@ -348,7 +349,6 @@
"android.hardware.vibrator-V1.1-java",
"android.hardware.vibrator-V1.2-java",
"android.hardware.vibrator-V1.3-java",
- "android.hardware.wifi-V1.0-java-constants",
"devicepolicyprotosnano",
"com.android.sysprop.apex",
@@ -506,7 +506,11 @@
defaults: ["framework-defaults"],
srcs: [":framework-all-sources"],
installable: false,
- static_libs: ["exoplayer2-core"],
+ static_libs: [
+ "exoplayer2-core",
+ "android.hardware.wifi-V1.0-java-constants",
+ ],
+ libs: ["icing-java-proto-lite"],
apex_available: ["//apex_available:platform"],
}
@@ -604,17 +608,22 @@
filegroup {
name: "framework-annotations",
srcs: [
+ "core/java/android/annotation/CallbackExecutor.java",
+ "core/java/android/annotation/CheckResult.java",
"core/java/android/annotation/IntDef.java",
"core/java/android/annotation/IntRange.java",
"core/java/android/annotation/NonNull.java",
"core/java/android/annotation/Nullable.java",
"core/java/android/annotation/RequiresPermission.java",
"core/java/android/annotation/SdkConstant.java",
+ "core/java/android/annotation/StringDef.java",
"core/java/android/annotation/SystemApi.java",
+ "core/java/android/annotation/SystemService.java",
"core/java/android/annotation/TestApi.java",
"core/java/android/annotation/UnsupportedAppUsage.java",
"core/java/com/android/internal/annotations/GuardedBy.java",
"core/java/com/android/internal/annotations/VisibleForTesting.java",
+ "core/java/com/android/internal/annotations/Immutable.java",
],
}
@@ -629,6 +638,9 @@
visibility: ["//frameworks/opt/net/ike"],
srcs: [
"core/java/android/net/annotations/PolicyDirection.java",
+ "core/java/com/android/internal/util/IState.java",
+ "core/java/com/android/internal/util/State.java",
+ "core/java/com/android/internal/util/StateMachine.java",
"telephony/java/android/telephony/Annotation.java",
],
}
@@ -1148,13 +1160,36 @@
],
}
+// utility classes statically linked into framework-wifi and dynamically linked
+// into wifi-service
+java_library {
+ name: "framework-wifi-util-lib",
+ sdk_version: "core_current",
+ srcs: [
+ "core/java/android/content/pm/BaseParceledListSlice.java",
+ "core/java/android/content/pm/ParceledListSlice.java",
+ "core/java/android/net/shared/Inet4AddressUtils.java",
+ "core/java/android/os/HandlerExecutor.java",
+ "core/java/com/android/internal/util/AsyncChannel.java",
+ "core/java/com/android/internal/util/AsyncService.java",
+ "core/java/com/android/internal/util/Protocol.java",
+ "core/java/com/android/internal/util/Preconditions.java",
+ "telephony/java/android/telephony/Annotation.java",
+ ],
+ libs: [
+ "framework-annotations-lib",
+ "unsupportedappusage",
+ "android_system_stubs_current",
+ ],
+ visibility: ["//frameworks/base/wifi"],
+}
+
+// utility classes statically linked into wifi-service
filegroup {
name: "framework-wifi-service-shared-srcs",
srcs: [
- ":framework-annotations",
"core/java/android/net/InterfaceConfiguration.java",
"core/java/android/os/BasicShellCommandHandler.java",
- "core/java/android/os/HandlerExecutor.java",
"core/java/android/util/BackupUtils.java",
"core/java/android/util/LocalLog.java",
"core/java/android/util/Rational.java",
@@ -1162,11 +1197,9 @@
"core/java/com/android/internal/util/HexDump.java",
"core/java/com/android/internal/util/IState.java",
"core/java/com/android/internal/util/MessageUtils.java",
- "core/java/com/android/internal/util/Preconditions.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
"core/java/com/android/internal/util/WakeupMessage.java",
- "core/java/com/android/internal/util/XmlUtils.java",
],
}
diff --git a/ApiDocs.bp b/ApiDocs.bp
index e373db6..c40004c 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -121,8 +121,10 @@
doc_defaults {
name: "framework-docs-default",
- libs: framework_docs_only_libs +
- ["stub-annotations"],
+ libs: framework_docs_only_libs + [
+ "stub-annotations",
+ "unsupportedappusage",
+ ],
html_dirs: [
"docs/html",
],
diff --git a/StubLibraries.bp b/StubLibraries.bp
index d195047..baa3c61 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -41,10 +41,9 @@
]
stubs_defaults {
- name: "metalava-api-stubs-default",
+ name: "metalava-non-updatable-api-stubs-default",
srcs: [
":framework-non-updatable-sources",
- ":framework-updatable-sources",
"core/java/**/*.logtags",
":opt-telephony-srcs",
":opt-net-voip-srcs",
@@ -64,14 +63,23 @@
"sdk-dir",
"api-versions-jars-dir",
],
- sdk_version: "core_platform",
filter_packages: packages_to_document,
}
+stubs_defaults {
+ name: "metalava-api-stubs-default",
+ defaults: ["metalava-non-updatable-api-stubs-default"],
+ srcs: [":framework-updatable-sources"],
+ sdk_version: "core_platform",
+}
+
/////////////////////////////////////////////////////////////////////
// *-api-stubs-docs modules providing source files for the stub libraries
/////////////////////////////////////////////////////////////////////
+// api-stubs-docs, system-api-stubs-docs, and test-api-stubs-docs have APIs
+// from the non-updatable part of the platform as well as from the updatable
+// modules
droidstubs {
name: "api-stubs-docs",
defaults: ["metalava-api-stubs-default"],
@@ -112,7 +120,10 @@
arg_files: [
"core/res/AndroidManifest.xml",
],
- args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\)",
+ args: metalava_framework_docs_args +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+ "process=android.annotation.SystemApi.Process.ALL\\)",
check_api: {
current: {
api_file: "api/system-current.txt",
@@ -155,6 +166,111 @@
}
/////////////////////////////////////////////////////////////////////
+// Following droidstubs modules are for extra APIs for modules.
+// The framework currently have two more API surfaces for modules:
+// @SystemApi(client=MODULE_APPS) and @SystemApi(client=MODULE_LIBRARIES)
+/////////////////////////////////////////////////////////////////////
+
+// TODO(b/146727827) remove the *-api modules when we can teach metalava
+// about the relationship among the API surfaces. Currently, these modules are only to generate
+// the API signature files and ensure that the APIs evolve in a backwards compatible manner.
+// They however are NOT used for building the API stub.
+droidstubs {
+ name: "module-app-api",
+ defaults: ["metalava-non-updatable-api-stubs-default"],
+ libs: ["framework-all"],
+ arg_files: ["core/res/AndroidManifest.xml"],
+ args: metalava_framework_docs_args +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_APPS," +
+ "process=android.annotation.SystemApi.Process.ALL\\)",
+ check_api: {
+ current: {
+ api_file: "api/module-app-current.txt",
+ removed_api_file: "api/module-app-removed.txt",
+ },
+ // TODO(b/147559833) enable the compatibility check against the last release API
+ // and the API lint
+ //last_released: {
+ // api_file: ":last-released-module-app-api",
+ // removed_api_file: "api/module-app-removed.txt",
+ // baseline_file: ":module-app-api-incompatibilities-with-last-released"
+ //},
+ //api_lint: {
+ // enabled: true,
+ // new_since: ":last-released-module-app-api",
+ // baseline_file: "api/module-app-lint-baseline.txt",
+ //},
+ },
+ //jdiff_enabled: true,
+}
+
+droidstubs {
+ name: "module-lib-api",
+ defaults: ["metalava-non-updatable-api-stubs-default"],
+ libs: ["framework-all"],
+ arg_files: ["core/res/AndroidManifest.xml"],
+ args: metalava_framework_docs_args +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
+ "process=android.annotation.SystemApi.Process.ALL\\)",
+ check_api: {
+ current: {
+ api_file: "api/module-lib-current.txt",
+ removed_api_file: "api/module-lib-removed.txt",
+ },
+ // TODO(b/147559833) enable the compatibility check against the last release API
+ // and the API lint
+ //last_released: {
+ // api_file: ":last-released-module-lib-api",
+ // removed_api_file: "api/module-lib-removed.txt",
+ // baseline_file: ":module-lib-api-incompatibilities-with-last-released"
+ //},
+ //api_lint: {
+ // enabled: true,
+ // new_since: ":last-released-module-lib-api",
+ // baseline_file: "api/module-lib-lint-baseline.txt",
+ //},
+ },
+ //jdiff_enabled: true,
+}
+
+// The following two droidstubs modules generate source files for the API stub libraries for
+// modules. Note that they not only include their own APIs but also other APIs that have
+// narrower scope. For example, module-lib-api-stubs-docs includes all @SystemApis not just
+// the ones with 'client=MODULE_LIBRARIES'.
+droidstubs {
+ name: "module-app-api-stubs-docs",
+ defaults: ["metalava-non-updatable-api-stubs-default"],
+ libs: ["framework-all"],
+ arg_files: ["core/res/AndroidManifest.xml"],
+ args: metalava_framework_docs_args +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+ "process=android.annotation.SystemApi.Process.ALL\\)" +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_APPS," +
+ "process=android.annotation.SystemApi.Process.ALL\\)",
+}
+
+droidstubs {
+ name: "module-lib-api-stubs-docs",
+ defaults: ["metalava-non-updatable-api-stubs-default"],
+ libs: ["framework-all"],
+ arg_files: ["core/res/AndroidManifest.xml"],
+ args: metalava_framework_docs_args +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+ "process=android.annotation.SystemApi.Process.ALL\\)" +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_APPS," +
+ "process=android.annotation.SystemApi.Process.ALL\\)" +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
+ "process=android.annotation.SystemApi.Process.ALL\\)",
+}
+
+/////////////////////////////////////////////////////////////////////
// android_*_stubs_current modules are the stubs libraries compiled
// from *-api-stubs-docs
/////////////////////////////////////////////////////////////////////
@@ -169,7 +285,6 @@
java_resources: [
":notices-for-framework-stubs",
],
- sdk_version: "core_current",
system_modules: "none",
java_version: "1.8",
compile_dex: true,
@@ -187,6 +302,7 @@
"private-stub-annotations-jar",
],
defaults: ["framework-stubs-default"],
+ sdk_version: "core_current",
}
java_library_static {
@@ -201,6 +317,7 @@
"private-stub-annotations-jar",
],
defaults: ["framework-stubs-default"],
+ sdk_version: "core_current",
}
java_library_static {
@@ -215,6 +332,37 @@
"private-stub-annotations-jar",
],
defaults: ["framework-stubs-default"],
+ sdk_version: "core_current",
+}
+
+java_library_static {
+ name: "framework_module_app_stubs_current",
+ srcs: [
+ ":module-app-api-stubs-docs",
+ ],
+ libs: [
+ "stub-annotations",
+ "framework-all",
+ ],
+ static_libs: [
+ "private-stub-annotations-jar",
+ ],
+ defaults: ["framework-stubs-default"],
+}
+
+java_library_static {
+ name: "framework_module_lib_stubs_current",
+ srcs: [
+ ":module-lib-api-stubs-docs",
+ ],
+ libs: [
+ "stub-annotations",
+ "framework-all",
+ ],
+ static_libs: [
+ "private-stub-annotations-jar",
+ ],
+ defaults: ["framework-stubs-default"],
}
/////////////////////////////////////////////////////////////////////
diff --git a/apex/Android.bp b/apex/Android.bp
index 56f7db2..abebfa3 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -37,6 +37,36 @@
stubs_defaults {
name: "framework-module-stubs-defaults-systemapi",
- args: mainline_stubs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) ",
+ args: mainline_stubs_args +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+ "process=android.annotation.SystemApi.Process.ALL\\) ",
+ installable: false,
+}
+
+stubs_defaults {
+ name: "framework-module-stubs-defaults-module_apps_api",
+ args: mainline_stubs_args +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+ "process=android.annotation.SystemApi.Process.ALL\\) " +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_APPS," +
+ "process=android.annotation.SystemApi.Process.ALL\\) ",
+ installable: false,
+}
+
+stubs_defaults {
+ name: "framework-module-stubs-defaults-module_libs_api",
+ args: mainline_stubs_args +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+ "process=android.annotation.SystemApi.Process.ALL\\) " +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_APPS," +
+ "process=android.annotation.SystemApi.Process.ALL\\) " +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
+ "process=android.annotation.SystemApi.Process.ALL\\) ",
installable: false,
}
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index 1f30dda..2e88115 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -13,29 +13,32 @@
// limitations under the License.
filegroup {
- name: "framework-appsearch-sources",
- srcs: [
- "java/**/*.java",
- "java/**/*.aidl",
- ],
- path: "java",
+ name: "framework-appsearch-sources",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
+ path: "java",
}
java_library {
- name: "framework-appsearch",
- installable: true,
- sdk_version: "core_platform", // TODO(b/146218515) should be core_current
- srcs: [":framework-appsearch-sources"],
- hostdex: true, // for hiddenapi check
- libs: [
- "framework-minus-apex", // TODO(b/146218515) should be framework-system-stubs
- ],
- visibility: [
- "//frameworks/base/apex/appsearch:__subpackages__",
- // TODO(b/146218515) remove this when framework is built with the stub of appsearch
- "//frameworks/base",
- ],
- apex_available: ["com.android.appsearch"],
+ name: "framework-appsearch",
+ installable: true,
+ sdk_version: "core_platform", // TODO(b/146218515) should be core_current
+ srcs: [":framework-appsearch-sources"],
+ hostdex: true, // for hiddenapi check
+ libs: [
+ "framework-minus-apex", // TODO(b/146218515) should be framework-system-stubs
+ ],
+ 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",
+ ],
+ apex_available: ["com.android.appsearch"],
}
metalava_appsearch_docs_args =
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index a8ee35c..58bb605 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -15,21 +15,84 @@
*/
package android.app.appsearch;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
+import android.os.RemoteException;
+
+import com.android.internal.infra.AndroidFuture;
+
+import com.google.android.icing.proto.SchemaProto;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
- * TODO(b/142567528): add comments when implement this class
+ * This class provides access to the centralized AppSearch index maintained by the system.
+ *
+ * <p>Apps can index structured text documents with AppSearch, which can then be retrieved through
+ * the query API.
+ *
* @hide
*/
@SystemService(Context.APP_SEARCH_SERVICE)
public class AppSearchManager {
private final IAppSearchManager mService;
+
+ /** @hide */
+ public AppSearchManager(@NonNull IAppSearchManager service) {
+ mService = service;
+ }
+
/**
- * TODO(b/142567528): add comments when implement this class
+ * Sets the schema being used by documents provided to the #put method.
+ *
+ * <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.
+ *
+ * <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 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}.
+ *
* @hide
*/
- public AppSearchManager(IAppSearchManager service) {
- mService = service;
+ // 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,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<? super Throwable> callback) {
+ SchemaProto schemaProto = schema.getProto();
+ byte[] schemaBytes = schemaProto.toByteArray();
+ AndroidFuture<Void> future = new AndroidFuture<>();
+ try {
+ mService.setSchema(schemaBytes, future);
+ } catch (RemoteException e) {
+ future.completeExceptionally(e);
+ }
+ future.whenCompleteAsync((noop, err) -> callback.accept(err), executor);
}
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java
new file mode 100644
index 0000000..7e5f187
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java
@@ -0,0 +1,426 @@
+/*
+ * 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.app.appsearch;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+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 java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Representation of the AppSearch Schema.
+ *
+ * <p>The schema is the set of document types, properties, and config (like tokenization type)
+ * understood by AppSearch for this app.
+ *
+ * @hide
+ */
+public final class AppSearchSchema {
+ private final SchemaProto mProto;
+
+ private AppSearchSchema(SchemaProto 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);
+ }
+
+ /** Creates a new {@link PropertyConfig.Builder}. */
+ @NonNull
+ public static PropertyConfig.Builder newPropertyBuilder(@NonNull String propertyName) {
+ 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.
+ * @hide
+ */
+ @NonNull
+ @VisibleForTesting
+ public SchemaProto getProto() {
+ return mProto;
+ }
+
+ /** Builder for {@link AppSearchSchema objects}. */
+ public static final class Builder {
+ private final SchemaProto.Builder mProtoBuilder = SchemaProto.newBuilder();
+
+ private Builder() {}
+
+ /** Adds a supported type to this app's AppSearch schema. */
+ @NonNull
+ public AppSearchSchema.Builder addType(@NonNull SchemaType schemaType) {
+ mProtoBuilder.addTypes(schemaType.mProto);
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link AppSearchSchema} from the contents of this builder.
+ *
+ * <p>After calling this method, the builder must no longer be used.
+ */
+ @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());
+ }
+ }
+ }
+
+ /**
+ * Configuration for a single property (field) of a document type.
+ *
+ * <p>For example, an {@code EmailMessage} would be a type and the {@code subject} would be
+ * a property.
+ */
+ public static final class PropertyConfig {
+ /** Physical data-types of the contents of the property. */
+ // NOTE: The integer values of these constants must match the proto enum constants in
+ // com.google.android.icing.proto.PropertyConfigProto.DataType.Code.
+ @IntDef(prefix = {"DATA_TYPE_"}, value = {
+ DATA_TYPE_STRING,
+ DATA_TYPE_INT64,
+ DATA_TYPE_DOUBLE,
+ DATA_TYPE_BOOLEAN,
+ DATA_TYPE_BYTES,
+ DATA_TYPE_DOCUMENT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DataType {}
+
+ public static final int DATA_TYPE_STRING = 1;
+ public static final int DATA_TYPE_INT64 = 2;
+ public static final int DATA_TYPE_DOUBLE = 3;
+ public static final int DATA_TYPE_BOOLEAN = 4;
+
+ /** Unstructured BLOB. */
+ public static final int DATA_TYPE_BYTES = 5;
+
+ /**
+ * Indicates that the property itself is an Document, making it part a hierarchical
+ * Document schema. Any property using this DataType MUST have a valid
+ * {@code schemaType}.
+ */
+ public static final int DATA_TYPE_DOCUMENT = 6;
+
+ /** The cardinality of the property (whether it is required, optional or repeated). */
+ // NOTE: The integer values of these constants must match the proto enum constants in
+ // com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code.
+ @IntDef(prefix = {"CARDINALITY_"}, value = {
+ CARDINALITY_REPEATED,
+ CARDINALITY_OPTIONAL,
+ CARDINALITY_REQUIRED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Cardinality {}
+
+ /** Any number of items (including zero) [0...*]. */
+ public static final int CARDINALITY_REPEATED = 1;
+
+ /** Zero or one value [0,1]. */
+ public static final int CARDINALITY_OPTIONAL = 2;
+
+ /** Exactly one value [1]. */
+ public static final int CARDINALITY_REQUIRED = 3;
+
+ private final PropertyConfigProto mProto;
+
+ private PropertyConfig(PropertyConfigProto proto) {
+ mProto = proto;
+ }
+
+ /**
+ * Builder for {@link PropertyConfig}.
+ *
+ * <p>The following properties must be set, or {@link PropertyConfig} construction will
+ * fail:
+ * <ul>
+ * <li>dataType
+ * <li>cardinality
+ * </ul>
+ *
+ * <p>In addition, if {@code schemaType} is {@link #DATA_TYPE_DOCUMENT}, {@code schemaType}
+ * is also required.
+ */
+ public static final class Builder {
+ private final PropertyConfigProto.Builder mProtoBuilder =
+ PropertyConfigProto.newBuilder();
+
+ private Builder(String propertyName) {
+ mProtoBuilder.setPropertyName(propertyName);
+ }
+
+ /**
+ * Type of data the property contains (e.g. string, int, bytes, etc).
+ *
+ * <p>This property must be set.
+ */
+ @NonNull
+ public PropertyConfig.Builder setDataType(@DataType int dataType) {
+ PropertyConfigProto.DataType.Code dataTypeProto =
+ PropertyConfigProto.DataType.Code.forNumber(dataType);
+ if (dataTypeProto == null) {
+ throw new IllegalArgumentException("Invalid dataType: " + dataType);
+ }
+ mProtoBuilder.setDataType(dataTypeProto);
+ return this;
+ }
+
+ /**
+ * The logical schema-type of the contents of this property.
+ *
+ * <p>Only required when {@link #setDataType(int)} is set to
+ * {@link #DATA_TYPE_DOCUMENT}. Otherwise, it is ignored.
+ */
+ @NonNull
+ public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
+ mProtoBuilder.setSchemaType(schemaType);
+ return this;
+ }
+
+ /**
+ * The cardinality of the property (whether it is optional, required or repeated).
+ *
+ * <p>This property must be set.
+ */
+ @NonNull
+ public PropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+ PropertyConfigProto.Cardinality.Code cardinalityProto =
+ PropertyConfigProto.Cardinality.Code.forNumber(cardinality);
+ if (cardinalityProto == null) {
+ throw new IllegalArgumentException("Invalid cardinality: " + cardinality);
+ }
+ mProtoBuilder.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.
+ */
+ @NonNull
+ public PropertyConfig.Builder setIndexingConfig(
+ @NonNull IndexingConfig indexingConfig) {
+ mProtoBuilder.setIndexingConfig(indexingConfig.mProto);
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link PropertyConfig} from the contents of this builder.
+ *
+ * <p>After calling this method, the builder must no longer be used.
+ *
+ * @throws IllegalSchemaException If the property is not correctly populated (e.g.
+ * missing {@code dataType}).
+ */
+ @NonNull
+ public PropertyConfig build() {
+ if (mProtoBuilder.getDataType() == PropertyConfigProto.DataType.Code.UNKNOWN) {
+ throw new IllegalSchemaException("Missing field: dataType");
+ }
+ if (mProtoBuilder.getSchemaType().isEmpty()
+ && mProtoBuilder.getDataType()
+ == PropertyConfigProto.DataType.Code.DOCUMENT) {
+ throw new IllegalSchemaException(
+ "Missing field: schemaType (required for configs with "
+ + "dataType = DOCUMENT)");
+ }
+ if (mProtoBuilder.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());
+ }
+ }
+ }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index f0f4f51..8085aa8 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -14,6 +14,18 @@
* limitations under the License.
*/
package android.app.appsearch;
+
+import com.android.internal.infra.AndroidFuture;
+
/** {@hide} */
interface IAppSearchManager {
+ /**
+ * Sets the schema.
+ *
+ * @param schemaProto serialized SchemaProto
+ * @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);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IllegalSchemaException.java b/apex/appsearch/framework/java/android/app/appsearch/IllegalSchemaException.java
new file mode 100644
index 0000000..f9e528c
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/IllegalSchemaException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.app.appsearch;
+
+import android.annotation.NonNull;
+
+/**
+ * Indicates that a {@link android.app.appsearch.AppSearchSchema} has logical inconsistencies such
+ * as unpopulated mandatory fields or illegal combinations of parameters.
+ *
+ * @hide
+ */
+public class IllegalSchemaException extends IllegalArgumentException {
+ /**
+ * Constructs a new {@link IllegalSchemaException}.
+ *
+ * @param message A developer-readable description of the issue with the bundle.
+ */
+ public IllegalSchemaException(@NonNull String message) {
+ super(message);
+ }
+}
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index 4ebafce8..7327231 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -12,16 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
java_library {
- name: "service-appsearch",
- installable: true,
- srcs: [
- "java/**/*.java",
- ],
- libs: [
- "framework",
- "services.core",
- ],
- static_libs: [
- "icing-java-proto-lite",
- ]
+ name: "service-appsearch",
+ installable: true,
+ srcs: ["java/**/*.java"],
+ libs: [
+ "framework",
+ "framework-appsearch",
+ "services.core",
+ ],
+ static_libs: ["icing-java-proto-lite"],
+ apex_available: ["com.android.appsearch"],
}
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 4d44d9d..96316b3 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -18,8 +18,11 @@
import android.app.appsearch.IAppSearchManager;
import android.content.Context;
+import com.android.internal.infra.AndroidFuture;
import com.android.server.SystemService;
+import com.google.android.icing.proto.SchemaProto;
+
/**
* TODO(b/142567528): add comments when implement this class
*/
@@ -35,5 +38,14 @@
}
private class Stub extends IAppSearchManager.Stub {
+ @Override
+ public void setSchema(byte[] schemaBytes, AndroidFuture callback) {
+ try {
+ SchemaProto schema = SchemaProto.parseFrom(schemaBytes);
+ throw new UnsupportedOperationException("setSchema not yet implemented: " + schema);
+ } catch (Throwable t) {
+ callback.completeExceptionally(t);
+ }
+ }
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
index 08811f8..ca5b884 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
+++ b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
@@ -10,6 +10,14 @@
"include-filter": "com.android.server.appsearch"
}
]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.app.appsearch"
+ }
+ ]
}
]
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
index 3dbb5cf..02a79a1 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
@@ -36,8 +36,6 @@
* <p>
* Currently, only queries by single exact term are supported. There is no support for persistence,
* namespaces, i18n tokenization, or schema.
- *
- * @hide
*/
public class FakeIcing {
private final AtomicInteger mNextDocId = new AtomicInteger();
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index c9d9d6c..69a9fd8 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -9,6 +9,7 @@
],
libs: [
+ "app-compat-annotations",
"framework",
"services.core",
],
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 3f58c72..f8b2f32 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -761,6 +761,7 @@
@Override
public void onTrigger(TriggerEvent event) {
synchronized (DeviceIdleController.this) {
+ // One_shot sensors (which call onTrigger) are unregistered when onTrigger is called
active = false;
motionLocked();
}
@@ -769,6 +770,9 @@
@Override
public void onSensorChanged(SensorEvent event) {
synchronized (DeviceIdleController.this) {
+ // Since one_shot sensors are unregistered when onTrigger is called, unregister
+ // listeners here so that the MotionListener is in a consistent state when it calls
+ // out to motionLocked.
mSensorManager.unregisterListener(this, mMotionSensor);
active = false;
motionLocked();
diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
new file mode 100644
index 0000000..8fbfb1d
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
@@ -0,0 +1,22 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "file_patterns": [
+ "DeviceIdleController\\.java"
+ ],
+ "options": [
+ {"include-filter": "com.android.server.DeviceIdleControllerTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server"}
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
new file mode 100644
index 0000000..bc7a7d3
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.DeviceIdleControllerTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server"}
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 9310762..f6512a6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -37,12 +37,15 @@
import android.app.job.JobWorkItem;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -54,6 +57,7 @@
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -67,6 +71,7 @@
import android.os.WorkSource;
import android.provider.Settings;
import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
@@ -85,6 +90,7 @@
import com.android.server.DeviceIdleInternal;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.compat.PlatformCompat;
import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
import com.android.server.job.controllers.BackgroundJobsController;
@@ -102,6 +108,9 @@
import com.android.server.job.restrictions.ThermalStatusRestriction;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
+import com.android.server.utils.quota.Categorizer;
+import com.android.server.utils.quota.Category;
+import com.android.server.utils.quota.CountQuotaTracker;
import libcore.util.EmptyArray;
@@ -145,6 +154,16 @@
/** The maximum number of jobs that we allow an unprivileged app to schedule */
private static final int MAX_JOBS_PER_APP = 100;
+ /**
+ * {@link #schedule(JobInfo)}, {@link #scheduleAsPackage(JobInfo, String, int, String)}, and
+ * {@link #enqueue(JobInfo, JobWorkItem)} will throw a {@link IllegalStateException} if the app
+ * calls the APIs too frequently.
+ */
+ @ChangeId
+ // This means the change will be enabled for target SDK larger than 29 (Q), meaning R and up.
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ protected static final long CRASH_ON_EXCEEDED_LIMIT = 144363383L;
+
@VisibleForTesting
public static Clock sSystemClock = Clock.systemUTC();
@@ -237,6 +256,10 @@
*/
private final List<JobRestriction> mJobRestrictions;
+ private final CountQuotaTracker mQuotaTracker;
+ private static final String QUOTA_TRACKER_SCHEDULE_TAG = ".schedule()";
+ private final PlatformCompat mPlatformCompat;
+
/**
* Queue of pending jobs. The JobServiceContext class will receive jobs from this list
* when ready to execute them.
@@ -276,6 +299,11 @@
final SparseIntArray mBackingUpUids = new SparseIntArray();
/**
+ * Cache of debuggable app status.
+ */
+ final ArrayMap<String, Boolean> mDebuggableApps = new ArrayMap<>();
+
+ /**
* Named indices into standby bucket arrays, for clarity in referring to
* specific buckets' bookkeeping.
*/
@@ -315,6 +343,10 @@
final StateController sc = mControllers.get(controller);
sc.onConstantsUpdatedLocked();
}
+ mQuotaTracker.setEnabled(mConstants.ENABLE_API_QUOTAS);
+ mQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
+ mConstants.API_QUOTA_SCHEDULE_COUNT,
+ mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
@@ -466,6 +498,11 @@
private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
private static final String DEPRECATED_KEY_USE_HEARTBEATS = "use_heartbeats";
+ private static final String KEY_ENABLE_API_QUOTAS = "enable_api_quotas";
+ private static final String KEY_API_QUOTA_SCHEDULE_COUNT = "aq_schedule_count";
+ private static final String KEY_API_QUOTA_SCHEDULE_WINDOW_MS = "aq_schedule_window_ms";
+ private static final String KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION =
+ "aq_schedule_throw_exception";
private static final int DEFAULT_MIN_IDLE_COUNT = 1;
private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
@@ -484,6 +521,10 @@
private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
+ private static final boolean DEFAULT_ENABLE_API_QUOTAS = true;
+ private static final int DEFAULT_API_QUOTA_SCHEDULE_COUNT = 500;
+ private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS;
+ private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true;
/**
* Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
@@ -618,6 +659,24 @@
*/
public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
+ /**
+ * Whether to enable quota limits on APIs.
+ */
+ public boolean ENABLE_API_QUOTAS = DEFAULT_ENABLE_API_QUOTAS;
+ /**
+ * The maximum number of schedule() calls an app can make in a set amount of time.
+ */
+ public int API_QUOTA_SCHEDULE_COUNT = DEFAULT_API_QUOTA_SCHEDULE_COUNT;
+ /**
+ * The time window that {@link #API_QUOTA_SCHEDULE_COUNT} should be evaluated over.
+ */
+ public long API_QUOTA_SCHEDULE_WINDOW_MS = DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS;
+ /**
+ * Whether to throw an exception when an app hits its schedule quota limit.
+ */
+ public boolean API_QUOTA_SCHEDULE_THROW_EXCEPTION =
+ DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION;
+
private final KeyValueListParser mParser = new KeyValueListParser(',');
void updateConstantsLocked(String value) {
@@ -678,6 +737,16 @@
DEFAULT_CONN_CONGESTION_DELAY_FRAC);
CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
DEFAULT_CONN_PREFETCH_RELAX_FRAC);
+
+ ENABLE_API_QUOTAS = mParser.getBoolean(KEY_ENABLE_API_QUOTAS,
+ DEFAULT_ENABLE_API_QUOTAS);
+ API_QUOTA_SCHEDULE_COUNT = Math.max(250,
+ mParser.getInt(KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT));
+ API_QUOTA_SCHEDULE_WINDOW_MS = mParser.getDurationMillis(
+ KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
+ API_QUOTA_SCHEDULE_THROW_EXCEPTION = mParser.getBoolean(
+ KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
+ DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION);
}
void dump(IndentingPrintWriter pw) {
@@ -716,6 +785,12 @@
pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
+ pw.printPair(KEY_ENABLE_API_QUOTAS, ENABLE_API_QUOTAS).println();
+ pw.printPair(KEY_API_QUOTA_SCHEDULE_COUNT, API_QUOTA_SCHEDULE_COUNT).println();
+ pw.printPair(KEY_API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS).println();
+ pw.printPair(KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
+ API_QUOTA_SCHEDULE_THROW_EXCEPTION).println();
+
pw.decreaseIndent();
}
@@ -746,6 +821,12 @@
proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
+
+ proto.write(ConstantsProto.ENABLE_API_QUOTAS, ENABLE_API_QUOTAS);
+ proto.write(ConstantsProto.API_QUOTA_SCHEDULE_COUNT, API_QUOTA_SCHEDULE_COUNT);
+ proto.write(ConstantsProto.API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS);
+ proto.write(ConstantsProto.API_QUOTA_SCHEDULE_THROW_EXCEPTION,
+ API_QUOTA_SCHEDULE_THROW_EXCEPTION);
}
}
@@ -847,6 +928,7 @@
for (int c = 0; c < mControllers.size(); ++c) {
mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
}
+ mDebuggableApps.remove(pkgName);
}
}
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
@@ -972,6 +1054,45 @@
public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
int userId, String tag) {
+ final String pkg = packageName == null ? job.getService().getPackageName() : packageName;
+ if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_TAG)) {
+ Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
+ // TODO(b/145551233): attempt to restrict app
+ if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION
+ && mPlatformCompat.isChangeEnabledByPackageName(
+ CRASH_ON_EXCEEDED_LIMIT, pkg, userId)) {
+ final boolean isDebuggable;
+ synchronized (mLock) {
+ if (!mDebuggableApps.containsKey(packageName)) {
+ try {
+ final ApplicationInfo appInfo = AppGlobals.getPackageManager()
+ .getApplicationInfo(pkg, 0, userId);
+ if (appInfo != null) {
+ mDebuggableApps.put(packageName,
+ (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
+ } else {
+ return JobScheduler.RESULT_FAILURE;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ isDebuggable = mDebuggableApps.get(packageName);
+ }
+ if (isDebuggable) {
+ // Only throw the exception for debuggable apps.
+ throw new IllegalStateException(
+ "schedule()/enqueue() called more than "
+ + mQuotaTracker.getLimit(Category.SINGLE_CATEGORY)
+ + " times in the past "
+ + mQuotaTracker.getWindowSizeMs(Category.SINGLE_CATEGORY)
+ + "ms");
+ }
+ }
+ return JobScheduler.RESULT_FAILURE;
+ }
+ mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_TAG);
+
try {
if (ActivityManager.getService().isAppStartModeDisabled(uId,
job.getService().getPackageName())) {
@@ -1296,6 +1417,12 @@
// Set up the app standby bucketing tracker
mStandbyTracker = new StandbyTracker();
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+ mPlatformCompat =
+ (PlatformCompat) ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
+ mQuotaTracker = new CountQuotaTracker(context, Categorizer.SINGLE_CATEGORIZER);
+ mQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
+ mConstants.API_QUOTA_SCHEDULE_COUNT,
+ mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class);
appStandby.addListener(mStandbyTracker);
@@ -2745,7 +2872,7 @@
return new ParceledListSlice<>(snapshots);
}
}
- };
+ }
// Shell command infrastructure: run the given job immediately
int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
@@ -2968,6 +3095,10 @@
return 0;
}
+ void resetScheduleQuota() {
+ mQuotaTracker.clear();
+ }
+
void triggerDockState(boolean idleState) {
final Intent dockIntent;
if (idleState) {
@@ -3030,6 +3161,9 @@
}
pw.println();
+ mQuotaTracker.dump(pw);
+ pw.println();
+
pw.println("Started users: " + Arrays.toString(mStartedUsers));
pw.print("Registered ");
pw.print(mJobs.size());
@@ -3217,6 +3351,9 @@
for (int u : mStartedUsers) {
proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
}
+
+ mQuotaTracker.dump(proto, JobSchedulerServiceDumpProto.QUOTA_TRACKER);
+
if (mJobs.size() > 0) {
final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
sortJobs(jobs);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index a5c6c01..6becf04 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -66,6 +66,8 @@
return getJobState(pw);
case "heartbeat":
return doHeartbeat(pw);
+ case "reset-schedule-quota":
+ return resetScheduleQuota(pw);
case "trigger-dock-state":
return triggerDockState(pw);
default:
@@ -344,6 +346,18 @@
return -1;
}
+ private int resetScheduleQuota(PrintWriter pw) throws Exception {
+ checkPermission("reset schedule quota");
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mInternal.resetScheduleQuota();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return 0;
+ }
+
private int triggerDockState(PrintWriter pw) throws Exception {
checkPermission("trigger wireless charging dock state");
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 82292cf..b9df30a 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -17,7 +17,7 @@
package com.android.server.usage;
import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
@@ -441,7 +441,7 @@
elapsedRealtime, true);
if (idle) {
appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
- appUsageHistory.bucketingReason = REASON_MAIN_FORCED;
+ appUsageHistory.bucketingReason = REASON_MAIN_FORCED_BY_USER;
} else {
appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
// This is to pretend that the app was just used, don't freeze the state anymore.
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 58eb589..eb0b54b 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -17,7 +17,8 @@
package com.android.server.usage;
import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
@@ -565,7 +566,7 @@
// If the bucket was forced by the user/developer, leave it alone.
// A usage event will be the only way to bring it out of this forced state
- if (oldMainReason == REASON_MAIN_FORCED) {
+ if (oldMainReason == REASON_MAIN_FORCED_BY_USER) {
return;
}
final int oldBucket = app.currentBucket;
@@ -783,7 +784,7 @@
// Inform listeners if necessary
if (previouslyIdle != stillIdle) {
maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket,
- REASON_MAIN_FORCED, false);
+ REASON_MAIN_FORCED_BY_USER, false);
if (!stillIdle) {
notifyBatteryStats(packageName, userId, idle);
}
@@ -1030,8 +1031,17 @@
callingPid, callingUid, userId, false, true, "setAppStandbyBucket", null);
final boolean shellCaller = callingUid == Process.ROOT_UID
|| callingUid == Process.SHELL_UID;
- final boolean systemCaller = UserHandle.isCore(callingUid);
- final int reason = systemCaller ? REASON_MAIN_FORCED : REASON_MAIN_PREDICTED;
+ final int reason;
+ // The Settings app runs in the system UID but in a separate process. Assume
+ // things coming from other processes are due to the user.
+ if ((UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) && callingPid != Process.myPid())
+ || shellCaller) {
+ reason = REASON_MAIN_FORCED_BY_USER;
+ } else if (UserHandle.isCore(callingUid)) {
+ reason = REASON_MAIN_FORCED_BY_SYSTEM;
+ } else {
+ reason = REASON_MAIN_PREDICTED;
+ }
final int packageFlags = PackageManager.MATCH_ANY_USER
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -1087,7 +1097,11 @@
}
// If the bucket was forced, don't allow prediction to override
- if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED && predicted) return;
+ if (predicted
+ && ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER
+ || (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM)) {
+ return;
+ }
// If the bucket is required to stay in a higher state for a specified duration, don't
// override unless the duration has passed
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
new file mode 100644
index 0000000..cf70878
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.usage"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.usage"}
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/apex/statsd/service/Android.bp b/apex/statsd/service/Android.bp
index f3a8989..9103848 100644
--- a/apex/statsd/service/Android.bp
+++ b/apex/statsd/service/Android.bp
@@ -13,4 +13,8 @@
"framework-minus-apex",
"services.core",
],
+ apex_available: [
+ "com.android.os.statsd",
+ "test_com.android.os.statsd",
+ ],
}
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 0f981e2..41347cc 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -714,60 +714,6 @@
}
}
- private void addNetworkStats(
- int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
- int size = stats.size();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
- long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
- NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
- for (int j = 0; j < size; j++) {
- stats.getValues(j, entry);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tag, elapsedNanos, wallClockNanos);
- 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);
- }
- }
-
- /**
- * 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;
- }
-
/**
* Helper method to extract the Parcelable controller info from a
* SynchronousResultReceiver.
@@ -815,168 +761,6 @@
}
}
- private void pullWifiBytesTransferByFgBg(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getWifiIfaces();
- if (ifaces.length == 0) {
- return;
- }
- if (mNetworkStatsService == null) {
- Slog.e(TAG, "NetworkStats Service is not available!");
- return;
- }
- NetworkStats stats = rollupNetworkStatsByFGBG(
- mNetworkStatsService.getDetailedUidStats(ifaces));
- addNetworkStats(tagId, pulledData, stats, true);
- } catch (RemoteException e) {
- Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void pullMobileBytesTransfer(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getMobileIfaces();
- if (ifaces.length == 0) {
- return;
- }
- if (mNetworkStatsService == null) {
- Slog.e(TAG, "NetworkStats Service is not available!");
- return;
- }
- // Combine all the metrics per Uid into one record.
- NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid();
- addNetworkStats(tagId, pulledData, stats, false);
- } catch (RemoteException e) {
- Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void pullBluetoothBytesTransfer(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- BluetoothActivityEnergyInfo info = fetchBluetoothData();
- if (info.getUidTraffic() != null) {
- for (UidTraffic traffic : info.getUidTraffic()) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeInt(traffic.getUid());
- e.writeLong(traffic.getRxBytes());
- e.writeLong(traffic.getTxBytes());
- pulledData.add(e);
- }
- }
- }
-
- private void pullMobileBytesTransferByFgBg(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getMobileIfaces();
- if (ifaces.length == 0) {
- return;
- }
- if (mNetworkStatsService == null) {
- Slog.e(TAG, "NetworkStats Service is not available!");
- return;
- }
- NetworkStats stats = rollupNetworkStatsByFGBG(
- mNetworkStatsService.getDetailedUidStats(ifaces));
- addNetworkStats(tagId, pulledData, stats, true);
- } catch (RemoteException e) {
- Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void pullCpuTimePerFreq(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> 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) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeInt(cluster);
- e.writeInt(speed);
- e.writeLong(clusterTimeMs[speed]);
- pulledData.add(e);
- }
- }
- }
- }
-
- private void pullKernelUidCpuTime(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- mCpuUidUserSysTimeReader.readAbsolute((uid, timesUs) -> {
- long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(uid);
- e.writeLong(userTimeUs);
- e.writeLong(systemTimeUs);
- pulledData.add(e);
- });
- }
-
- private void pullKernelUidCpuFreqTime(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
- for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
- if (cpuFreqTimeMs[freqIndex] != 0) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeInt(uid);
- e.writeInt(freqIndex);
- e.writeLong(cpuFreqTimeMs[freqIndex]);
- pulledData.add(e);
- }
- }
- });
- }
-
- private void pullKernelUidCpuClusterTime(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- mCpuUidClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
- for (int i = 0; i < cpuClusterTimesMs.length; i++) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeInt(uid);
- e.writeInt(i);
- e.writeLong(cpuClusterTimesMs[i]);
- pulledData.add(e);
- }
- });
- }
-
- private void pullKernelUidCpuActiveTime(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(uid);
- e.writeLong((long) cpuActiveTimesMs);
- pulledData.add(e);
- });
- }
-
private void pullWifiActivityInfo(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
@@ -1052,33 +836,6 @@
}
}
- private void pullBluetoothActivityInfo(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- BluetoothActivityEnergyInfo info = fetchBluetoothData();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(info.getTimeStamp());
- e.writeInt(info.getBluetoothStackState());
- e.writeLong(info.getControllerTxTimeMillis());
- e.writeLong(info.getControllerRxTimeMillis());
- e.writeLong(info.getControllerIdleTimeMillis());
- e.writeLong(info.getControllerEnergyUsed());
- pulledData.add(e);
- }
-
- private synchronized BluetoothActivityEnergyInfo fetchBluetoothData() {
- 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 void pullSystemElapsedRealtime(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
@@ -1671,21 +1428,6 @@
}
}
- private void pullPowerProfile(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- PowerProfile powerProfile = new PowerProfile(mContext);
- Objects.requireNonNull(powerProfile);
-
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- ProtoOutputStream proto = new ProtoOutputStream();
- powerProfile.dumpDebug(proto);
- proto.flush();
- e.writeStorage(proto.getBytes());
- pulledData.add(e);
- }
-
private void pullBuildInformation(int tagId,
long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
@@ -2291,57 +2033,12 @@
long elapsedNanos = SystemClock.elapsedRealtimeNanos();
long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
switch (tagId) {
-
- case StatsLog.MOBILE_BYTES_TRANSFER: {
- pullMobileBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
- pullWifiBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
- pullMobileBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.BLUETOOTH_BYTES_TRANSFER: {
- pullBluetoothBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
case StatsLog.KERNEL_WAKELOCK: {
pullKernelWakelock(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
- case StatsLog.CPU_TIME_PER_FREQ: {
- pullCpuTimePerFreq(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.CPU_TIME_PER_UID: {
- pullKernelUidCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.CPU_TIME_PER_UID_FREQ: {
- pullKernelUidCpuFreqTime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.CPU_CLUSTER_TIME: {
- pullKernelUidCpuClusterTime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.CPU_ACTIVE_TIME: {
- pullKernelUidCpuActiveTime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
case StatsLog.WIFI_ACTIVITY_INFO: {
pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
@@ -2352,11 +2049,6 @@
break;
}
- case StatsLog.BLUETOOTH_ACTIVITY_INFO: {
- pullBluetoothActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
case StatsLog.SYSTEM_UPTIME: {
pullSystemUpTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
@@ -2455,11 +2147,6 @@
break;
}
- case StatsLog.POWER_PROFILE: {
- pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
case StatsLog.BUILD_INFORMATION: {
pullBuildInformation(tagId, elapsedNanos, wallClockNanos, ret);
break;
diff --git a/api/current.txt b/api/current.txt
index d1e1108..e166c14 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2925,6 +2925,7 @@
method public int getShowMode();
method public boolean removeOnShowModeChangedListener(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener);
method public boolean setShowMode(int);
+ method public boolean switchToInputMethod(@NonNull String);
}
public static interface AccessibilityService.SoftKeyboardController.OnShowModeChangedListener {
@@ -6763,6 +6764,7 @@
method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String);
method public CharSequence getDeviceOwnerLockScreenInfo();
method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName);
+ method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName);
method @Nullable public String getGlobalPrivateDnsHost(@NonNull android.content.ComponentName);
method public int getGlobalPrivateDnsMode(@NonNull android.content.ComponentName);
method @NonNull public java.util.List<byte[]> getInstalledCaCerts(@Nullable android.content.ComponentName);
@@ -6881,6 +6883,7 @@
method public void setDelegatedScopes(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.lang.String>);
method public void setDeviceOwnerLockScreenInfo(@NonNull android.content.ComponentName, CharSequence);
method public void setEndUserSessionMessage(@NonNull android.content.ComponentName, @Nullable CharSequence);
+ method public void setFactoryResetProtectionPolicy(@NonNull android.content.ComponentName, @Nullable android.app.admin.FactoryResetProtectionPolicy);
method public int setGlobalPrivateDnsModeOpportunistic(@NonNull android.content.ComponentName);
method @WorkerThread public int setGlobalPrivateDnsModeSpecifiedHost(@NonNull android.content.ComponentName, @NonNull String);
method public void setGlobalSetting(@NonNull android.content.ComponentName, String, String);
@@ -7115,6 +7118,21 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DnsEvent> CREATOR;
}
+ public final class FactoryResetProtectionPolicy implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getFactoryResetProtectionAccounts();
+ method public boolean isFactoryResetProtectionDisabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FactoryResetProtectionPolicy> CREATOR;
+ }
+
+ public static class FactoryResetProtectionPolicy.Builder {
+ ctor public FactoryResetProtectionPolicy.Builder();
+ method @NonNull public android.app.admin.FactoryResetProtectionPolicy build();
+ method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionAccounts(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionDisabled(boolean);
+ }
+
public class FreezePeriod {
ctor public FreezePeriod(java.time.MonthDay, java.time.MonthDay);
method public java.time.MonthDay getEnd();
@@ -10008,6 +10026,7 @@
field public static final String MEDIA_ROUTER_SERVICE = "media_router";
field public static final String MEDIA_SESSION_SERVICE = "media_session";
field public static final String MIDI_SERVICE = "midi";
+ field public static final String MMS_SERVICE = "mms";
field public static final int MODE_APPEND = 32768; // 0x8000
field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
field @Deprecated public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -16833,6 +16852,8 @@
method @Nullable public CharSequence getSubtitle();
method @NonNull public CharSequence getTitle();
method public boolean isConfirmationRequired();
+ field public static final int AUTHENTICATION_RESULT_TYPE_BIOMETRIC = 2; // 0x2
+ field public static final int AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL = 1; // 0x1
field public static final int BIOMETRIC_ACQUIRED_GOOD = 0; // 0x0
field public static final int BIOMETRIC_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
field public static final int BIOMETRIC_ACQUIRED_INSUFFICIENT = 2; // 0x2
@@ -16862,6 +16883,7 @@
}
public static class BiometricPrompt.AuthenticationResult {
+ method public int getAuthenticationType();
method public android.hardware.biometrics.BiometricPrompt.CryptoObject getCryptoObject();
}
@@ -17061,13 +17083,13 @@
method public abstract void close();
method @NonNull public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
method @NonNull public android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int, java.util.Set<java.lang.String>) throws android.hardware.camera2.CameraAccessException;
- method public abstract void createCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method @Deprecated public abstract void createCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public void createCaptureSession(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
- method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
- method public abstract void createConstrainedHighSpeedCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method @Deprecated public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method @Deprecated public abstract void createConstrainedHighSpeedCaptureSession(@NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method @NonNull public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(@NonNull android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
- method public abstract void createReprocessableCaptureSession(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
- method public abstract void createReprocessableCaptureSessionByConfigurations(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method @Deprecated public abstract void createReprocessableCaptureSession(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.view.Surface>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method @Deprecated public abstract void createReprocessableCaptureSessionByConfigurations(@NonNull android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public int getCameraAudioRestriction() throws android.hardware.camera2.CameraAccessException;
method @NonNull public abstract String getId();
method public boolean isSessionConfigurationSupported(@NonNull android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
@@ -27119,9 +27141,11 @@
public abstract class VolumeProvider {
ctor public VolumeProvider(int, int, int);
+ ctor public VolumeProvider(int, int, int, @Nullable String);
method public final int getCurrentVolume();
method public final int getMaxVolume();
method public final int getVolumeControl();
+ method @Nullable public final String getVolumeControlId();
method public void onAdjustVolume(int);
method public void onSetVolumeTo(int);
method public final void setCurrentVolume(int);
@@ -27981,6 +28005,7 @@
method public int getMaxVolume();
method public int getPlaybackType();
method public int getVolumeControl();
+ method @Nullable public String getVolumeControlId();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.session.MediaController.PlaybackInfo> CREATOR;
field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
@@ -28682,7 +28707,9 @@
ctor public TvInputService();
method public final android.os.IBinder onBind(android.content.Intent);
method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(String);
+ method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String, @NonNull String);
method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(String);
+ method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String);
field public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService";
field public static final String SERVICE_META_DATA = "android.media.tv.input";
}
@@ -30486,6 +30513,7 @@
method @Nullable public java.security.cert.X509Certificate[] getCaCertificates();
method public java.security.cert.X509Certificate getClientCertificate();
method @Nullable public java.security.cert.X509Certificate[] getClientCertificateChain();
+ method @Nullable public java.security.PrivateKey getClientPrivateKey();
method public String getDomainSuffixMatch();
method public int getEapMethod();
method public String getIdentity();
@@ -30625,6 +30653,7 @@
field public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
field public static final String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
field public static final String ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION = "android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION";
+ field public static final String ACTION_WIFI_SCAN_AVAILABLE = "android.net.wifi.action.WIFI_SCAN_AVAILABLE";
field @Deprecated public static final int ERROR_AUTHENTICATING = 1; // 0x1
field @Deprecated public static final String EXTRA_BSSID = "bssid";
field public static final String EXTRA_NETWORK_INFO = "networkInfo";
@@ -30633,6 +30662,7 @@
field @Deprecated public static final String EXTRA_NEW_STATE = "newState";
field public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
field public static final String EXTRA_RESULTS_UPDATED = "resultsUpdated";
+ field public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE";
field @Deprecated public static final String EXTRA_SUPPLICANT_CONNECTED = "connected";
field @Deprecated public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
field @Deprecated public static final String EXTRA_WIFI_INFO = "wifiInfo";
@@ -39182,6 +39212,7 @@
method @NonNull public static android.app.PendingIntent createWriteRequest(@NonNull android.content.ContentResolver, @NonNull java.util.Collection<android.net.Uri>);
method @Nullable public static android.net.Uri getDocumentUri(@NonNull android.content.Context, @NonNull android.net.Uri);
method @NonNull public static java.util.Set<java.lang.String> getExternalVolumeNames(@NonNull android.content.Context);
+ method public static long getGeneration(@NonNull android.content.Context, @NonNull String);
method public static android.net.Uri getMediaScannerUri();
method @Nullable public static android.net.Uri getMediaUri(@NonNull android.content.Context, @NonNull android.net.Uri);
method @NonNull public static java.util.Set<java.lang.String> getRecentExternalVolumeNames(@NonNull android.content.Context);
@@ -39492,6 +39523,8 @@
field public static final String DISPLAY_NAME = "_display_name";
field public static final String DOCUMENT_ID = "document_id";
field public static final String DURATION = "duration";
+ field public static final String GENERATION_ADDED = "generation_added";
+ field public static final String GENERATION_MODIFIED = "generation_modified";
field public static final String GENRE = "genre";
field public static final String HEIGHT = "height";
field public static final String INSTANCE_ID = "instance_id";
@@ -45128,6 +45161,9 @@
field public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool";
field public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
+ field public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int";
+ field public static final String KEY_5G_ICON_CONFIGURATION_STRING = "5g_icon_configuration_string";
+ field public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT = "5g_icon_display_grace_period_sec_int";
field public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrp_thresholds_int_array";
field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array";
field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array";
@@ -45141,18 +45177,32 @@
field public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
field public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool";
field public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
- field public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
+ field public static final String KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL = "allow_video_calling_fallback_bool";
+ field public static final String KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL = "always_show_data_rat_icon_bool";
+ field @Deprecated public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
+ field public static final String KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN = "always_show_primary_signal_bar_in_opportunistic_network_boolean";
field public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
+ field public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY = "apn_settings_default_apn_types_string_array";
field public static final String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool";
field public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL = "call_barring_supports_password_change_bool";
field public static final String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool";
field public static final String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array";
+ field public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING = "call_redirection_service_component_name_string";
+ field public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL = "carrier_allow_deflect_ims_call_bool";
field public static final String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
field public static final String KEY_CARRIER_APP_REQUIRED_DURING_SIM_SETUP_BOOL = "carrier_app_required_during_setup_bool";
field public static final String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app";
+ field public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY = "carrier_certificate_string_array";
+ field public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool";
field public static final String KEY_CARRIER_CONFIG_VERSION_STRING = "carrier_config_version_string";
field public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS = "carrier_data_call_permanent_failure_strings";
+ field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY = "carrier_default_actions_on_dcfailure_string_array";
+ field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE = "carrier_default_actions_on_default_network_available_string_array";
+ field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY = "carrier_default_actions_on_redirection_string_array";
+ field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET = "carrier_default_actions_on_reset_string_array";
+ field public static final String KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY = "carrier_default_redirection_url_string_array";
+ field public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL = "carrier_default_wfc_ims_enabled_bool";
field public static final String KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = "carrier_default_wfc_ims_mode_int";
field public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = "carrier_default_wfc_ims_roaming_mode_int";
field @Deprecated public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL = "carrier_force_disable_etws_cmas_test_bool";
@@ -45164,11 +45214,14 @@
field public static final String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT = "carrier_instant_lettering_length_limit_int";
field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
+ field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
+ field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
+ field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
field public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
field public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
@@ -45182,6 +45235,7 @@
field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
+ field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
@@ -45191,6 +45245,7 @@
field public static final String KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING = "config_ims_rcs_package_override_string";
field public static final String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
field public static final String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL = "config_telephony_use_own_number_for_voicemail_bool";
+ field public static final String KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm";
field public static final String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL = "data_limit_notification_bool";
field public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
@@ -45202,6 +45257,7 @@
field public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+ field public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL = "disable_charge_indication_bool";
field public static final String KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL = "disable_supplementary_services_in_airplane_mode_bool";
field public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array";
field public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL = "display_hd_audio_property_bool";
@@ -45211,9 +45267,13 @@
field public static final String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
field public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
field public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
+ field public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
+ field public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL = "editable_wfc_roaming_mode_bool";
+ field public static final String KEY_EMERGENCY_NOTIFICATION_DELAY_INT = "emergency_notification_delay_int";
field public static final String KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY = "emergency_number_prefix_string_array";
field public static final String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool";
+ field public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT = "enhanced_4g_lte_title_variant_int";
field public static final String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
field public static final String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
@@ -45222,13 +45282,17 @@
field public static final String KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
field public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
field public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
+ field public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL = "hide_lte_plus_data_icon_bool";
field public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
field public static final String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
field public static final String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
+ field public static final String KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS = "ignore_data_enabled_changed_for_video_calls";
+ field public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL = "ignore_rtt_mode_setting_bool";
field public static final String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
field public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
field public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
field public static final String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL = "is_ims_conference_size_enforced_bool";
+ field public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool";
field public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "lte_rsrq_thresholds_int_array";
field public static final String KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY = "lte_rssnr_thresholds_int_array";
field public static final String KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL = "mdn_is_additional_voicemail_number_bool";
@@ -45264,6 +45328,7 @@
field public static final String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
field public static final String KEY_MMS_USER_AGENT_STRING = "userAgent";
field public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
+ field public static final String KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network";
field public static final String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
field public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG = "opportunistic_network_data_switch_hysteresis_time_long";
@@ -45277,15 +45342,24 @@
field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool";
field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array";
field public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
+ field public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY = "read_only_apn_fields_string_array";
+ field public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY = "read_only_apn_types_string_array";
field public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
field @Deprecated public static final String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
field public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
+ field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
+ field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
+ field public static final String KEY_SHOW_BLOCKING_PAY_PHONE_OPTION_BOOL = "show_blocking_pay_phone_option_bool";
field public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool";
+ field public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING = "show_carrier_data_icon_pattern_string";
field public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
+ field public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL = "show_ims_registration_status_bool";
field public static final String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
field public static final String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL = "show_signal_strength_in_sim_status_bool";
+ field public static final String KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL = "show_video_call_charges_alert_dialog_bool";
+ field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool";
field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
@@ -45293,14 +45367,20 @@
field public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool";
field public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
+ field public static final String KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL = "support_enhanced_call_blocking_bool";
+ field public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL = "support_ims_conference_event_package_bool";
field public static final String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
field public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
+ field public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool";
+ field public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY = "support_tdscdma_roaming_networks_string_array";
field public static final String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool";
field public static final String KEY_TTY_SUPPORTED_BOOL = "tty_supported_bool";
+ field public static final String KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY = "unloggable_numbers_string_array";
field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
field public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
field public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
+ field public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = "use_wfc_home_network_mode_in_roaming_network_bool";
field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
field public static final String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
field public static final String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
@@ -45313,6 +45393,8 @@
field public static final String KEY_VVM_PREFETCH_BOOL = "vvm_prefetch_bool";
field public static final String KEY_VVM_SSL_ENABLED_BOOL = "vvm_ssl_enabled_bool";
field public static final String KEY_VVM_TYPE_STRING = "vvm_type_string";
+ field public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string";
+ field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
}
@@ -45632,6 +45714,11 @@
method @Nullable public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, @NonNull java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
}
+ public class MmsManager {
+ method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent);
+ method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent);
+ }
+
@Deprecated public class NeighboringCellInfo implements android.os.Parcelable {
ctor @Deprecated public NeighboringCellInfo();
ctor @Deprecated public NeighboringCellInfo(int, int);
@@ -45869,8 +45956,8 @@
method public String createAppSpecificSmsToken(android.app.PendingIntent);
method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent);
method public java.util.ArrayList<java.lang.String> divideMessage(String);
- method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
- method @Nullable public android.os.Bundle getCarrierConfigValues();
+ method @Deprecated public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
+ method @NonNull public android.os.Bundle getCarrierConfigValues();
method public static android.telephony.SmsManager getDefault();
method public static int getDefaultSmsSubscriptionId();
method public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int);
@@ -45879,7 +45966,7 @@
method public int getSubscriptionId();
method public void injectSmsPdu(byte[], String, android.app.PendingIntent);
method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
- method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, 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 sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
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);
@@ -54793,6 +54880,7 @@
public final class InlineSuggestionsRequest implements android.os.Parcelable {
method public int describeContents();
+ method @NonNull public String getHostPackageName();
method public int getMaxSuggestionCount();
method @NonNull public java.util.List<android.view.inline.InlinePresentationSpec> getPresentationSpecs();
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/api/module-app-current.txt b/api/module-app-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/api/module-app-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/module-app-removed.txt b/api/module-app-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/api/module-app-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
new file mode 100644
index 0000000..0a67065
--- /dev/null
+++ b/api/module-lib-current.txt
@@ -0,0 +1,81 @@
+// Signature format: 2.0
+package android.app.timedetector {
+
+ public final class PhoneTimeSuggestion implements android.os.Parcelable {
+ method public void addDebugInfo(@NonNull String);
+ method public void addDebugInfo(@NonNull java.util.List<java.lang.String>);
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getDebugInfo();
+ method public int getPhoneId();
+ method @Nullable public android.os.TimestampedValue<java.lang.Long> getUtcTime();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.timedetector.PhoneTimeSuggestion> CREATOR;
+ }
+
+ public static final class PhoneTimeSuggestion.Builder {
+ ctor public PhoneTimeSuggestion.Builder(int);
+ method @NonNull public android.app.timedetector.PhoneTimeSuggestion.Builder addDebugInfo(@NonNull String);
+ method @NonNull public android.app.timedetector.PhoneTimeSuggestion build();
+ method @NonNull public android.app.timedetector.PhoneTimeSuggestion.Builder setUtcTime(@Nullable android.os.TimestampedValue<java.lang.Long>);
+ }
+
+ public class TimeDetector {
+ method @RequiresPermission("android.permission.SUGGEST_PHONE_TIME_AND_ZONE") public void suggestPhoneTime(@NonNull android.app.timedetector.PhoneTimeSuggestion);
+ }
+
+}
+
+package android.app.timezonedetector {
+
+ public final class PhoneTimeZoneSuggestion implements android.os.Parcelable {
+ method public void addDebugInfo(@NonNull String);
+ method public void addDebugInfo(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public static android.app.timezonedetector.PhoneTimeZoneSuggestion createEmptySuggestion(int, @NonNull String);
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getDebugInfo();
+ method public int getMatchType();
+ method public int getPhoneId();
+ method public int getQuality();
+ method @Nullable public String getZoneId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.timezonedetector.PhoneTimeZoneSuggestion> CREATOR;
+ field public static final int MATCH_TYPE_EMULATOR_ZONE_ID = 4; // 0x4
+ field public static final int MATCH_TYPE_NA = 0; // 0x0
+ field public static final int MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET = 3; // 0x3
+ field public static final int MATCH_TYPE_NETWORK_COUNTRY_ONLY = 2; // 0x2
+ field public static final int MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY = 5; // 0x5
+ field public static final int QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS = 3; // 0x3
+ field public static final int QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET = 2; // 0x2
+ field public static final int QUALITY_NA = 0; // 0x0
+ field public static final int QUALITY_SINGLE_ZONE = 1; // 0x1
+ }
+
+ public static final class PhoneTimeZoneSuggestion.Builder {
+ ctor public PhoneTimeZoneSuggestion.Builder(int);
+ method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder addDebugInfo(@NonNull String);
+ method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion build();
+ method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setMatchType(int);
+ method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setQuality(int);
+ method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setZoneId(@Nullable String);
+ }
+
+ public class TimeZoneDetector {
+ method @RequiresPermission("android.permission.SUGGEST_PHONE_TIME_AND_ZONE") public void suggestPhoneTimeZone(@NonNull android.app.timezonedetector.PhoneTimeZoneSuggestion);
+ }
+
+}
+
+package android.os {
+
+ public final class TimestampedValue<T> implements android.os.Parcelable {
+ ctor public TimestampedValue(long, @Nullable T);
+ method public int describeContents();
+ method public long getReferenceTimeMillis();
+ method @Nullable public T getValue();
+ method public static long referenceTimeDifference(@NonNull android.os.TimestampedValue<?>, @NonNull android.os.TimestampedValue<?>);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.os.TimestampedValue<?>> CREATOR;
+ }
+
+}
+
diff --git a/api/module-lib-removed.txt b/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/system-current.txt b/api/system-current.txt
index 309b989..3f34096 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -190,6 +190,7 @@
field public static final String REVIEW_ACCESSIBILITY_SERVICES = "android.permission.REVIEW_ACCESSIBILITY_SERVICES";
field public static final String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
field public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
+ field public static final String SECURE_ELEMENT_PRIVILEGED = "android.permission.SECURE_ELEMENT_PRIVILEGED";
field public static final String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
@@ -207,9 +208,11 @@
field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
+ field public static final String SUGGEST_PHONE_TIME_AND_ZONE = "android.permission.SUGGEST_PHONE_TIME_AND_ZONE";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
+ field public static final String TUNER_RESOURCE_ACCESS = "android.permission.TUNER_RESOURCE_ACCESS";
field public static final String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
field public static final String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
@@ -282,6 +285,7 @@
field public static final int config_helpIntentNameKey = 17039390; // 0x104001e
field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
+ field public static final int config_systemGallery = 17039402; // 0x104002a
}
public static final class R.style {
@@ -324,6 +328,7 @@
method public void setDeviceLocales(@NonNull android.os.LocaleList);
method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
}
public static interface ActivityManager.OnUidImportanceListener {
@@ -814,6 +819,7 @@
field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
field public static final String ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE";
field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
+ field public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
field public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
@@ -1330,6 +1336,10 @@
field public static final String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService";
}
+ public class NetworkStatsManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.net.netstats.provider.NetworkStatsProviderCallback registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.AbstractNetworkStatsProvider);
+ }
+
public static final class UsageEvents.Event {
method public int getInstanceId();
method @Nullable public String getNotificationChannelId();
@@ -1706,6 +1716,7 @@
field public static final String NETD_SERVICE = "netd";
field public static final String NETWORK_POLICY_SERVICE = "netpolicy";
field public static final String NETWORK_SCORE_SERVICE = "network_score";
+ field public static final String NETWORK_STACK_SERVICE = "network_stack";
field public static final String OEM_LOCK_SERVICE = "oem_lock";
field public static final String PERMISSION_SERVICE = "permission";
field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
@@ -2408,7 +2419,7 @@
package android.hardware.camera2 {
public abstract class CameraDevice implements java.lang.AutoCloseable {
- method public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method @Deprecated public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1
field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0
field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000
@@ -4397,10 +4408,12 @@
}
public static class SoundTriggerManager.Model {
- method public static android.media.soundtrigger.SoundTriggerManager.Model create(java.util.UUID, java.util.UUID, byte[]);
- method public byte[] getModelData();
- method public java.util.UUID getModelUuid();
- method public java.util.UUID getVendorUuid();
+ method @NonNull public static android.media.soundtrigger.SoundTriggerManager.Model create(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], int);
+ method @NonNull public static android.media.soundtrigger.SoundTriggerManager.Model create(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[]);
+ method @Nullable public byte[] getModelData();
+ method @NonNull public java.util.UUID getModelUuid();
+ method @NonNull public java.util.UUID getVendorUuid();
+ method public int getVersion();
}
}
@@ -4530,6 +4543,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
+ method @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS) public int getClientPid(@NonNull String);
method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList();
method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList();
@@ -4541,6 +4555,7 @@
method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public void releaseTvInputHardware(int, android.media.tv.TvInputManager.Hardware);
method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void removeBlockedRating(@NonNull android.media.tv.TvContentRating);
method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void setParentalControlsEnabled(boolean);
+ field public static final int UNKNOWN_CLIENT_PID = -1; // 0xffffffff
}
public static final class TvInputManager.Hardware {
@@ -4617,6 +4632,28 @@
method public abstract int getType();
}
+ public class Lnb implements java.lang.AutoCloseable {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int sendDiseqcMessage(@NonNull byte[]);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setSatellitePosition(int);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setTone(int);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setVoltage(int);
+ field public static final int POSITION_A = 1; // 0x1
+ field public static final int POSITION_B = 2; // 0x2
+ field public static final int POSITION_UNDEFINED = 0; // 0x0
+ field public static final int TONE_CONTINUOUS = 1; // 0x1
+ field public static final int TONE_NONE = 0; // 0x0
+ field public static final int VOLTAGE_11V = 2; // 0x2
+ field public static final int VOLTAGE_12V = 3; // 0x3
+ field public static final int VOLTAGE_13V = 4; // 0x4
+ field public static final int VOLTAGE_14V = 5; // 0x5
+ field public static final int VOLTAGE_15V = 6; // 0x6
+ field public static final int VOLTAGE_18V = 7; // 0x7
+ field public static final int VOLTAGE_19V = 8; // 0x8
+ field public static final int VOLTAGE_5V = 1; // 0x1
+ field public static final int VOLTAGE_NONE = 0; // 0x0
+ }
+
public final class Tuner implements java.lang.AutoCloseable {
ctor public Tuner(@NonNull android.content.Context);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Tuner.Descrambler openDescrambler();
@@ -4639,16 +4676,43 @@
field public static final int FILTER_STATUS_HIGH_WATER = 4; // 0x4
field public static final int FILTER_STATUS_LOW_WATER = 2; // 0x2
field public static final int FILTER_STATUS_OVERFLOW = 8; // 0x8
+ field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
+ field public static final int RESULT_INVALID_STATE = 3; // 0x3
+ field public static final int RESULT_NOT_INITIALIZED = 2; // 0x2
+ field public static final int RESULT_OUT_OF_MEMORY = 5; // 0x5
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ field public static final int RESULT_UNAVAILABLE = 1; // 0x1
+ field public static final int RESULT_UNKNOWN_ERROR = 6; // 0x6
}
}
package android.media.tv.tuner.filter {
+ public abstract class FilterConfiguration {
+ field public static final int FILTER_TYPE_ALP = 16; // 0x10
+ field public static final int FILTER_TYPE_IP = 4; // 0x4
+ field public static final int FILTER_TYPE_MMTP = 2; // 0x2
+ field public static final int FILTER_TYPE_TLV = 8; // 0x8
+ field public static final int FILTER_TYPE_TS = 1; // 0x1
+ }
+
public abstract class FilterEvent {
ctor public FilterEvent();
}
+ public class PesSettings extends android.media.tv.tuner.filter.Settings {
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.PesSettings.Builder builder(@NonNull android.content.Context, int);
+ method public int getStreamId();
+ method public boolean isRaw();
+ }
+
+ public static class PesSettings.Builder {
+ method @NonNull public android.media.tv.tuner.filter.PesSettings build();
+ method @NonNull public android.media.tv.tuner.filter.PesSettings.Builder setRaw(boolean);
+ method @NonNull public android.media.tv.tuner.filter.PesSettings.Builder setStreamId(int);
+ }
+
public class SectionEvent extends android.media.tv.tuner.filter.FilterEvent {
method public int getDataLength();
method public int getSectionNumber();
@@ -4656,6 +4720,22 @@
method public int getVersion();
}
+ public abstract class Settings {
+ }
+
+ public class TsFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.filter.TsFilterConfiguration.Builder builder(@NonNull android.content.Context);
+ method @Nullable public android.media.tv.tuner.filter.Settings getSettings();
+ method public int getTpid();
+ method public int getType();
+ }
+
+ public static class TsFilterConfiguration.Builder {
+ method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration build();
+ method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setSettings(@NonNull android.media.tv.tuner.filter.Settings);
+ method @NonNull public android.media.tv.tuner.filter.TsFilterConfiguration.Builder setTpid(int);
+ }
+
}
package android.metrics {
@@ -4706,7 +4786,9 @@
public class CaptivePortal implements android.os.Parcelable {
method public void logEvent(int, @NonNull String);
+ method public void reevaluateNetwork();
method public void useNetwork();
+ field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
field public static final int APP_RETURN_DISMISSED = 0; // 0x0
field public static final int APP_RETURN_UNWANTED = 1; // 0x1
field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
@@ -4720,6 +4802,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
+ method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
@@ -4736,6 +4819,7 @@
field public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd
field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+ field public static final int TYPE_NONE = -1; // 0xffffffff
}
public abstract static class ConnectivityManager.OnStartTetheringCallback {
@@ -4871,12 +4955,36 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.MatchAllNetworkSpecifier> CREATOR;
}
+ public final class NattKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
+ ctor public NattKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException;
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NattKeepalivePacketData> CREATOR;
+ }
+
public class Network implements android.os.Parcelable {
ctor public Network(@NonNull android.net.Network);
method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
field public final int netId;
}
+ public final class NetworkAgentConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getSubscriberId();
+ method public boolean isNat64DetectionEnabled();
+ method public boolean isProvisioningNotificationEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
+ }
+
+ public static class NetworkAgentConfig.Builder {
+ ctor public NetworkAgentConfig.Builder();
+ 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 setSubscriberId(@Nullable String);
+ }
+
public final class NetworkCapabilities implements android.os.Parcelable {
method public boolean deduceRestrictedCapability();
method @NonNull public int[] getTransportTypes();
@@ -4915,6 +5023,10 @@
method public abstract void onRequestScores(android.net.NetworkKey[]);
}
+ public class NetworkRequest implements android.os.Parcelable {
+ method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities);
+ }
+
public static class NetworkRequest.Builder {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
}
@@ -4935,6 +5047,9 @@
field @Deprecated public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore";
field public static final String EXTRA_NEW_SCORER = "newScorer";
field @Deprecated public static final String EXTRA_PACKAGE_NAME = "packageName";
+ field public static final int SCORE_FILTER_CURRENT_NETWORK = 1; // 0x1
+ field public static final int SCORE_FILTER_NONE = 0; // 0x0
+ field public static final int SCORE_FILTER_SCAN_RESULTS = 2; // 0x2
}
public static interface NetworkScoreManager.NetworkScoreCallback {
@@ -5018,6 +5133,10 @@
field public final android.net.RssiCurve rssiCurve;
}
+ public abstract class SocketKeepalive implements java.lang.AutoCloseable {
+ field public static final int SUCCESS = 0; // 0x0
+ }
+
public final class StaticIpConfiguration implements android.os.Parcelable {
ctor public StaticIpConfiguration();
ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
@@ -5812,6 +5931,7 @@
}
public class ScanResult implements android.os.Parcelable {
+ ctor public ScanResult();
field public static final int CIPHER_CCMP = 3; // 0x3
field public static final int CIPHER_GCMP_256 = 4; // 0x4
field public static final int CIPHER_NONE = 0; // 0x0
@@ -5928,6 +6048,7 @@
method @Deprecated public static boolean isMetered(@Nullable android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiInfo);
method @Deprecated public boolean isNoInternetAccessExpected();
method @Deprecated public void setIpConfiguration(@Nullable android.net.IpConfiguration);
+ method @Deprecated public void setNetworkSelectionStatus(@NonNull android.net.wifi.WifiConfiguration.NetworkSelectionStatus);
method @Deprecated public void setProxy(@NonNull android.net.IpConfiguration.ProxySettings, @NonNull android.net.ProxyInfo);
field @Deprecated public static final int AP_BAND_2GHZ = 0; // 0x0
field @Deprecated public static final int AP_BAND_5GHZ = 1; // 0x1
@@ -5972,6 +6093,7 @@
method @Deprecated public boolean getHasEverConnected();
method @Deprecated @Nullable public static String getNetworkDisableReasonString(int);
method @Deprecated public int getNetworkSelectionDisableReason();
+ method @Deprecated public int getNetworkSelectionStatus();
method @Deprecated @NonNull public String getNetworkStatusString();
method @Deprecated public boolean isNetworkEnabled();
method @Deprecated public boolean isNetworkPermanentlyDisabled();
@@ -5986,6 +6108,16 @@
field @Deprecated public static final int DISABLED_NO_INTERNET_TEMPORARY = 4; // 0x4
field @Deprecated public static final int NETWORK_SELECTION_DISABLED_MAX = 10; // 0xa
field @Deprecated public static final int NETWORK_SELECTION_ENABLE = 0; // 0x0
+ field @Deprecated public static final int NETWORK_SELECTION_ENABLED = 0; // 0x0
+ field @Deprecated public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED = 2; // 0x2
+ field @Deprecated public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1; // 0x1
+ }
+
+ @Deprecated public static final class WifiConfiguration.NetworkSelectionStatus.Builder {
+ ctor @Deprecated public WifiConfiguration.NetworkSelectionStatus.Builder();
+ method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus build();
+ method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus.Builder setNetworkSelectionDisableReason(int);
+ method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus.Builder setNetworkSelectionStatus(int);
}
@Deprecated public static class WifiConfiguration.RecentFailure {
@@ -6030,6 +6162,15 @@
field public static final int INVALID_RSSI = -127; // 0xffffff81
}
+ public static final class WifiInfo.Builder {
+ ctor public WifiInfo.Builder();
+ method @NonNull public android.net.wifi.WifiInfo build();
+ method @NonNull public android.net.wifi.WifiInfo.Builder setBssid(@NonNull String);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setNetworkId(int);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setRssi(int);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setSsid(@NonNull byte[]);
+ }
+
public class WifiManager {
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);
@@ -8075,7 +8216,6 @@
field @NonNull public static final String ENABLE_CMAS_PRESIDENTIAL_PREF = "enable_cmas_presidential_alerts";
field @NonNull public static final String ENABLE_CMAS_SEVERE_THREAT_PREF = "enable_cmas_severe_threat_alerts";
field @NonNull public static final String ENABLE_EMERGENCY_PERF = "enable_emergency_alerts";
- field @NonNull public static final String ENABLE_FULL_VOLUME_PREF = "use_full_volume";
field @NonNull public static final String ENABLE_PUBLIC_SAFETY_PREF = "enable_public_safety_messages";
field @NonNull public static final String ENABLE_STATE_LOCAL_TEST_PREF = "enable_state_local_test_alerts";
field @NonNull public static final String ENABLE_TEST_ALERT_PREF = "enable_test_alerts";
@@ -9311,6 +9451,7 @@
public final class CallQuality implements android.os.Parcelable {
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
+ ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
method public int describeContents();
method public int getAverageRelativeJitter();
method public int getAverageRoundTripTime();
@@ -9323,6 +9464,9 @@
method public int getNumRtpPacketsTransmitted();
method public int getNumRtpPacketsTransmittedLost();
method public int getUplinkCallQualityLevel();
+ method public boolean isIncomingSilenceDetected();
+ method public boolean isOutgoingSilenceDetected();
+ method public boolean isRtpInactivityDetected();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CALL_QUALITY_BAD = 4; // 0x4
field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0
@@ -10322,9 +10466,19 @@
method public boolean disableCellBroadcastRange(int, int, int);
method public boolean enableCellBroadcastRange(int, int, int);
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 @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
+ field public static final int PREMIUM_SMS_CONSENT_ASK_USER = 1; // 0x1
+ field public static final int PREMIUM_SMS_CONSENT_NEVER_ALLOW = 2; // 0x2
+ field public static final int PREMIUM_SMS_CONSENT_UNKNOWN = 0; // 0x0
+ }
+
+ public class SmsMessage {
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static byte[] getSubmitPduEncodedMessage(boolean, @NonNull String, @NonNull String, int, int, int, int, int, int);
}
public class SubscriptionInfo implements android.os.Parcelable {
@@ -10422,6 +10576,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
method public String getCdmaPrlVersion();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
method public int getCurrentPhoneType();
method public int getCurrentPhoneType(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState();
@@ -10495,6 +10650,8 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
+ 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 @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);
@@ -10519,12 +10676,16 @@
method public void updateServiceLocation();
method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String);
field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED";
+ field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
+ field public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE";
+ field public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED";
+ field public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED";
+ field public static final String ACTION_CARRIER_SIGNAL_RESET = "com.android.internal.telephony.CARRIER_SIGNAL_RESET";
field public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
field public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
field public static final String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE";
field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
- field public static final String ACTION_NETWORK_SET_TIME = "android.telephony.action.NETWORK_SET_TIME";
field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
@@ -10534,8 +10695,22 @@
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+ field public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
+ field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
+ field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
+ field @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto";
+ field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
+ field @Deprecated public static final String EXTRA_APN_TYPE = "apnType";
+ field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
+ field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
+ field public static final String EXTRA_ERROR_CODE = "errorCode";
+ field public static final String EXTRA_PCO_ID = "pcoId";
+ field public static final String EXTRA_PCO_VALUE = "pcoValue";
+ field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
+ field public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
+ field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
@@ -11435,10 +11610,13 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
@@ -11698,6 +11876,7 @@
method public String getConfigString(int);
method public final void notifyProvisionedValueChanged(int, int);
method public final void notifyProvisionedValueChanged(int, String);
+ method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
method public int setConfig(int, int);
method public int setConfig(int, String);
field public static final int CONFIG_RESULT_FAILED = 1; // 0x1
diff --git a/api/test-current.txt b/api/test-current.txt
index a8e097e..10028f2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34,6 +34,7 @@
public static final class R.string {
field public static final int config_defaultAssistant = 17039393; // 0x1040021
field public static final int config_defaultDialer = 17039395; // 0x1040023
+ field public static final int config_systemGallery = 17039402; // 0x104002a
}
}
@@ -470,7 +471,10 @@
}
public class WallpaperManager {
+ method @Nullable public android.graphics.Bitmap getBitmap();
method @RequiresPermission("android.permission.SET_WALLPAPER_COMPONENT") public boolean setWallpaperComponent(android.content.ComponentName);
+ method public boolean shouldEnableWideColorGamut();
+ method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public boolean wallpaperSupportsWcg(int);
}
public class WindowConfiguration implements java.lang.Comparable<android.app.WindowConfiguration> android.os.Parcelable {
@@ -756,6 +760,7 @@
field public static final String BUGREPORT_SERVICE = "bugreport";
field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
+ field public static final String NETWORK_STACK_SERVICE = "network_stack";
field public static final String PERMISSION_SERVICE = "permission";
field public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
field public static final String ROLLBACK_SERVICE = "rollback";
@@ -1020,7 +1025,7 @@
package android.hardware.camera2 {
public abstract class CameraDevice implements java.lang.AutoCloseable {
- method public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method @Deprecated public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1
field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0
field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000
@@ -1496,7 +1501,9 @@
public class CaptivePortal implements android.os.Parcelable {
method public void logEvent(int, @NonNull String);
+ method public void reevaluateNetwork();
method public void useNetwork();
+ field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
field public static final int APP_RETURN_DISMISSED = 0; // 0x0
field public static final int APP_RETURN_UNWANTED = 1; // 0x1
field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
@@ -2733,6 +2740,11 @@
method @Nullable public android.util.SparseArray<android.service.autofill.InternalOnClickAction> getActions();
}
+ public static final class Dataset.Builder {
+ ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
+ method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
+ }
+
public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception;
}
@@ -3127,6 +3139,7 @@
public final class CallQuality implements android.os.Parcelable {
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
+ ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
method public int describeContents();
method public int getAverageRelativeJitter();
method public int getAverageRoundTripTime();
@@ -3139,6 +3152,9 @@
method public int getNumRtpPacketsTransmitted();
method public int getNumRtpPacketsTransmittedLost();
method public int getUplinkCallQualityLevel();
+ method public boolean isIncomingSilenceDetected();
+ method public boolean isOutgoingSilenceDetected();
+ method public boolean isRtpInactivityDetected();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CALL_QUALITY_BAD = 4; // 0x4
field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0
@@ -3813,10 +3829,13 @@
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public int getProvisioningIntValue(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public String getProvisioningStringValue(int);
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
@@ -4035,6 +4054,7 @@
method public String getConfigString(int);
method public final void notifyProvisionedValueChanged(int, int);
method public final void notifyProvisionedValueChanged(int, String);
+ method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
method public int setConfig(int, int);
method public int setConfig(int, String);
field public static final int CONFIG_RESULT_FAILED = 1; // 0x1
@@ -4264,6 +4284,7 @@
field public static final String FFLAG_OVERRIDE_PREFIX = "sys.fflag.override.";
field public static final String FFLAG_PREFIX = "sys.fflag.";
field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
+ field public static final String NOTIF_CONVO_BYPASS_SHORTCUT_REQ = "settings_notif_convo_bypass_shortcut_req";
field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
@@ -4363,6 +4384,7 @@
}
public final class Display {
+ method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
method public boolean hasAccess(int);
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 459520a..8fac31a 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -1113,7 +1113,7 @@
SurfaceComposerClient::Transaction t;
t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset)
.setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight));
- t.setDisplayProjection(mDisplayToken, 0 /* orientation */, layerStackRect, displayRect);
+ t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, layerStackRect, displayRect);
t.apply();
mTargetInset = mCurrentInset = 0;
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 229628c..4074789 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -33,6 +33,8 @@
namespace {
+#define REWRITE_PACKAGE(resid, package_id) \
+ (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
std::string ConcatPolicies(const std::vector<std::string>& policies) {
@@ -154,6 +156,7 @@
return Error("root element is not <overlay> tag");
}
+ const uint8_t target_package_id = target_package->GetPackageId();
const uint8_t overlay_package_id = overlay_package->GetPackageId();
auto overlay_it_end = root_it.end();
for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
@@ -187,6 +190,9 @@
continue;
}
+ // Retrieve the compile-time resource id of the target resource.
+ target_id = REWRITE_PACKAGE(target_id, target_package_id);
+
if (overlay_resource->dataType == Res_value::TYPE_STRING) {
overlay_resource->data += string_pool_offset;
}
@@ -214,6 +220,7 @@
const AssetManager2* target_am, const AssetManager2* overlay_am,
const LoadedPackage* target_package, const LoadedPackage* overlay_package) {
ResourceMapping resource_mapping;
+ const uint8_t target_package_id = target_package->GetPackageId();
const auto end = overlay_package->end();
for (auto iter = overlay_package->begin(); iter != end; ++iter) {
const ResourceId overlay_resid = *iter;
@@ -225,11 +232,14 @@
// Find the resource with the same type and entry name within the target package.
const std::string full_name =
base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
- const ResourceId target_resource = target_am->GetResourceId(full_name);
+ ResourceId target_resource = target_am->GetResourceId(full_name);
if (target_resource == 0U) {
continue;
}
+ // Retrieve the compile-time resource id of the target resource.
+ target_resource = REWRITE_PACKAGE(target_resource, target_package_id);
+
resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
/* rewrite_overlay_reference */ false);
}
diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS
index 04464ce..a61babf 100644
--- a/cmds/statsd/OWNERS
+++ b/cmds/statsd/OWNERS
@@ -1,7 +1,8 @@
-jianjin@google.com
+jeffreyhuang@google.com
joeo@google.com
jtnguyen@google.com
muhammadq@google.com
+ruchirr@google.com
singhtejinder@google.com
tsaichristine@google.com
yaochen@google.com
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 6fc1e23..967fd32 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -261,6 +261,11 @@
return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000);
}
+inline Matcher getFirstUidMatcher(int32_t atomId) {
+ int32_t pos[] = {1, 1, 1};
+ return Matcher(Field(atomId, pos, 2), 0xff7f7f7f);
+}
+
/**
* A wrapper for a union type to contain multiple types of values.
*
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 6d2bd04..946c550 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -30,6 +30,8 @@
PRIMARY = 1;
// The field that represents the state. It's an exclusive state.
EXCLUSIVE = 2;
+
+ PRIMARY_FIELD_FIRST_UID = 3;
}
// Used to annotate an atom that reprsents a state change. A state change atom must have exactly ONE
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2bacfbc..cbece78 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -126,10 +126,10 @@
AppStartOccurred app_start_occurred = 48;
AppStartCanceled app_start_canceled = 49;
AppStartFullyDrawn app_start_fully_drawn = 50;
- LmkKillOccurred lmk_kill_occurred = 51;
+ LmkKillOccurred lmk_kill_occurred = 51 [(module) = "lmkd"];
PictureInPictureStateChanged picture_in_picture_state_changed = 52;
WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53 [(module) = "wifi"];
- LmkStateChanged lmk_state_changed = 54;
+ LmkStateChanged lmk_state_changed = 54 [(module) = "lmkd"];
AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
ShutdownSequenceReported shutdown_sequence_reported = 56;
BootSequenceReported boot_sequence_reported = 57;
@@ -334,10 +334,15 @@
MediaProviderIdleMaintenance media_provider_idle_maintenance =
237 [(module) = "mediaprovider"];
RebootEscrowRecoveryReported reboot_escrow_recovery_reported = 238;
+ BootTimeEventDuration boot_time_event_duration_reported = 239;
+ BootTimeEventElapsedTime boot_time_event_elapsed_time_reported = 240;
+ BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
+ BootTimeEventErrorCode boot_time_event_error_code_reported = 242;
+ UserspaceRebootReported userspace_reboot_reported = 243;
}
// Pulled events will start at field 10000.
- // Next: 10069
+ // Next: 10070
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -407,6 +412,7 @@
NotificationRemoteViews notification_remote_views = 10066;
DangerousPermissionStateSampled dangerous_permission_state_sampled = 10067;
GraphicsStats graphics_stats = 10068;
+ RuntimeAppOpsAccess runtime_app_ops_access = 10069;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -908,14 +914,16 @@
* TODO
*/
message WakelockStateChanged {
- repeated AttributionNode attribution_node = 1;
+ repeated AttributionNode attribution_node = 1
+ [(state_field_option).option = PRIMARY_FIELD_FIRST_UID];
// The type (level) of the wakelock; e.g. a partial wakelock or a full wakelock.
// From frameworks/base/core/proto/android/os/enums.proto.
- optional android.os.WakeLockLevelEnum type = 2;
+ optional android.os.WakeLockLevelEnum type = 2 [(state_field_option).option = PRIMARY];
+ ;
// The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
- optional string tag = 3;
+ optional string tag = 3 [(state_field_option).option = PRIMARY];
enum State {
RELEASE = 0;
@@ -923,7 +931,7 @@
CHANGE_RELEASE = 2;
CHANGE_ACQUIRE = 3;
}
- optional State state = 4;
+ optional State state = 4 [(state_field_option).option = EXCLUSIVE];
}
/**
@@ -3925,6 +3933,207 @@
optional float normalized_expired_media = 5;
}
+/**
+ * Represents boot time event with duration in ms.
+ *
+ * Logged from: bootstat and various system server components. Check each enums for details.
+ */
+message BootTimeEventDuration {
+ enum DurationEvent {
+ UNKNOWN = 0;
+ // Bootloader time excluding BOOTLOADER_UI_WAIT + boot complete time. Logged from bootstat.
+ ABSOLUTE_BOOT_TIME = 1;
+ // Bootloader's 1st stage execution time.
+ // Logged from bootstat.
+ BOOTLOADER_FIRST_STAGE_EXEC = 2;
+ // Bootloader's 1st stage loading time.
+ // Logged from bootstat.
+ BOOTLOADER_FIRST_STAGE_LOAD = 3;
+ // Bootloader's kernel loading time.
+ // Logged from bootstat.
+ BOOTLOADER_KERNEL_LOAD = 4;
+ // Bootloader's 2nd stage execution time.
+ // Logged from bootstat.
+ BOOTLOADER_SECOND_STAGE_EXEC = 5;
+ // Bootloader's 2nd stage loading time.
+ // Logged from bootstat.
+ BOOTLOADER_SECOND_STAGE_LOAD = 6;
+ // Duration for Bootloader to show unlocked device's warning UI. This should not happen
+ // for locked device.
+ // Logged from bootstat.
+ BOOTLOADER_UI_WAIT = 7;
+ // Total time spend in bootloader. This is the sum of all BOOTLOADER_* listed above.
+ // Logged from bootstat.
+ BOOTLOADER_TOTAL = 8;
+ // Shutdown duration inside init for the reboot before the current boot up.
+ // Logged from f/b/services/.../BootReceiver.java.
+ SHUTDOWN_DURATION = 9;
+ // Total time for mounting of disk devices during bootup.
+ // Logged from f/b/services/.../BootReceiver.java.
+ MOUNT_DEFAULT_DURATION = 10;
+ // Total time for early stage mounting of disk devices during bootup.
+ // Logged from f/b/services/.../BootReceiver.java.
+ MOUNT_EARLY_DURATION = 11;
+ // Total time for late stage mounting of disk devices during bootup.
+ // Logged from f/b/services/.../BootReceiver.java.
+ MOUNT_LATE_DURATION = 12;
+ // Average time to scan non-system app after OTA
+ // Logged from f/b/services/.../PackageManagerService.java
+ OTA_PACKAGE_MANAGER_INIT_TIME = 13;
+ // Time to initialize Package manager after OTA
+ // Logged from f/b/services/.../PackageManagerService.java
+ OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME = 14;
+ // Time to scan all system app from Package manager after OTA
+ // Logged from f/b/services/.../PackageManagerService.java
+ OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME = 15;
+ // Init's total time for cold boot stage.
+ // Logged from bootstat.
+ COLDBOOT_WAIT = 16;
+ // Init's total time for initializing selinux.
+ // Logged from bootstat.
+ SELINUX_INIT = 17;
+ // Time since last factory reset.
+ // Logged from bootstat.
+ FACTORY_RESET_TIME_SINCE_RESET = 18;
+ }
+
+ // Type of the event.
+ optional DurationEvent event = 1;
+ // Duration of the event in ms.
+ optional int64 duration_millis = 2;
+}
+
+/**
+ * Represents the start of specific boot time event during bootup in ms. This is usually a time
+ * since boot-up.
+ *
+ * Logged from: bootstat and various system server components. Check each enums for details.
+ */
+message BootTimeEventElapsedTime {
+ enum ElapsedTimeEvent {
+ UNKNOWN = 0;
+ // Time when init starts 1st stage. Logged from bootstat.
+ ANDROID_INIT_STAGE_1 = 1;
+ // Time when sys.boot_completed prop is set.
+ // Logged from bootstat.
+ BOOT_COMPLETE = 2;
+ // BOOT_COMPLETE for encrypted device.
+ BOOT_COMPLETE_ENCRYPTION = 3;
+ // BOOT_COMPLETE for device with no encryption.
+ BOOT_COMPLETE_NO_ENCRYPTION = 4;
+ // Adjusted BOOT_COMPLETE for encrypted device extracting decryption time.
+ BOOT_COMPLETE_POST_DESCRYPT = 5;
+ // BOOT_COMPLETE after factory reset.
+ FACTORY_RESET_BOOT_COMPLETE = 6;
+ // BOOT_COMPLETE_NO_ENCRYPTION after factory reset.
+ FACTORY_RESET_BOOT_COMPLETE_NO_ENCRYPTION = 7;
+ // BOOT_COMPLETE_POST_DESCRYPT after factory reset.
+ FACTORY_RESET_BOOT_COMPLETE_POST_DESCRYPT = 8;
+ // BOOT_COMPLETE after OTA.
+ OTA_BOOT_COMPLETE = 9;
+ // BOOT_COMPLETE_NO_ENCRYPTION after OTA.
+ OTA_BOOT_COMPLETE_NO_ENCRYPTION = 10;
+ // BOOT_COMPLETE_POST_DESCRYPT after OTA.
+ OTA_BOOT_COMPLETE_POST_DESCRYPT = 11;
+ // Time when the system starts sending LOCKED_BOOT_COMPLETED broadcast.
+ // Logged from f/b/services/.../UserController.java
+ FRAMEWORK_LOCKED_BOOT_COMPLETED = 12;
+ // Time when the system starts sending BOOT_COMPLETED broadcast.
+ // Logged from f/b/services/.../UserController.java
+ FRAMEWORK_BOOT_COMPLETED = 13;
+ // Time when the package manager starts init.
+ // Logged from f/b/services/.../SystemServer.java
+ PACKAGE_MANAGER_INIT_START = 14;
+ // Time when package manager is ready
+ // Logged from f/b/services/.../SystemServer.java
+ PACKAGE_MANAGER_INIT_READY = 15;
+ // Represents the time when user has entered unlock credential for system with user pin.
+ // Logged from bootstat.
+ POST_DECRYPT = 16;
+ // Represents the start of zygote's init.
+ // Logged from zygote itself.
+ ZYGOTE_INIT_START = 17;
+ // Represents the start of secondary zygote's init.
+ // TODO: add logging to zygote
+ SECONDARY_ZYGOTE_INIT_START = 18;
+ // Represents the start of system server's init.
+ // Logged from f/b/services/.../SystemServer.java
+ SYSTEM_SERVER_INIT_START = 19;
+ // Represents the completion of system server's init.
+ // Logged from f/b/services/.../SystemServer.java
+ SYSTEM_SERVER_READY = 20;
+ // Represents the start of launcher during boot-up.
+ // TODO: add logging
+ LAUNCHER_START = 21;
+ // Represents the completion of launcher's initial rendering. User can use other apps from
+ // launcher from this point.
+ // TODO: add logging
+ LAUNCHER_SHOWN = 22;
+ }
+
+ // Type of the event.
+ optional ElapsedTimeEvent event = 1;
+ // Time since bootup for the event.
+ // It should be acquired from SystemClock elapsedRealtime() call or equivalent.
+ optional int64 time_millis = 2;
+}
+
+/**
+ * Boot time events with UTC time.
+ *
+ * Logged from: bootstat and various system server components. Check each enums for details.
+ */
+message BootTimeEventUtcTime {
+ enum UtcTimeEvent {
+ UNKNOWN = 0;
+ // Time of the bootstat's marking of 1st boot after the last factory reset.
+ // Logged from bootstat.
+ FACTORY_RESET_RESET_TIME = 1;
+ // The time when bootstat records FACTORY_RESET_* events. This is close to
+ // BOOT_COMPLETE time for the current bootup.
+ // Logged from bootstat.
+ FACTORY_RESET_CURRENT_TIME = 2;
+ // DUplicate of FACTORY_RESET_RESET_TIME added for debugging purpose.
+ // Logged from bootstat.
+ FACTORY_RESET_RECORD_VALUE = 3;
+ }
+
+ // Type of the event.
+ optional UtcTimeEvent event = 1;
+ // UTC time for the event.
+ optional int64 utc_time_secs = 2;
+}
+
+/**
+ * Boot time events representing specific error code during bootup.
+ * Meaning of error code can be different per each event type.
+ *
+ * Logged from: bootstat and various system server components. Check each enums for details.
+ */
+message BootTimeEventErrorCode {
+ enum ErrorCodeEvent {
+ UNKNOWN = 0;
+ // Linux error code for time() call to get the current UTC time.
+ // Logged from bootstat.
+ FACTORY_RESET_CURRENT_TIME_FAILURE = 1;
+ // Represents UmountStat before the reboot for the current boot up. Error codes defined
+ // as UMOUNT_STAT_* from init/reboot.cpp.
+ // Logged from f/b/services/.../BootReceiver.java.
+ SHUTDOWN_UMOUNT_STAT = 2;
+ // Reprepsents fie system mounting error code of /data partition for the current boot.
+ // Error codes defined as combination of FsStatFlags from system/core/fs_mgr/fs_mgr.cpp.
+ // Logged from f/b/services/.../BootReceiver.java.
+ FS_MGR_FS_STAT_DATA_PARTITION = 3;
+ }
+
+ // Type of the event.
+ optional ErrorCodeEvent event = 1;
+ // error code defined per each event type.
+ // For example, this can have a value of FsStatFlags.FS_STAT_FULL_MOUNT_FAILED for the event of
+ // FS_MGR_FS_STAT.
+ optional int32 error_code = 2;
+}
+
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
@@ -4725,36 +4934,69 @@
}
message AggStats {
- optional int64 min = 1;
+ // These are all in byte resolution.
+ optional int64 min = 1 [deprecated = true];
+ optional int64 average = 2 [deprecated = true];
+ optional int64 max = 3 [deprecated = true];
- optional int64 average = 2;
-
- optional int64 max = 3;
+ // These are all in kilobyte resolution. Can fit in int32, so smaller on the wire than the above
+ // int64 fields.
+ optional int32 mean_kb = 4;
+ optional int32 max_kb = 5;
}
+// A reduced subset of process states; reducing the number of possible states allows more
+// aggressive device-side aggregation of statistics and hence reduces metric upload size.
+enum ProcessStateAggregated {
+ PROCESS_STATE_UNKNOWN = 0;
+ // Persistent system process.
+ PROCESS_STATE_PERSISTENT = 1;
+ // Top activity; actually any visible activity.
+ PROCESS_STATE_TOP = 2;
+ // Process binding to top or a foreground service.
+ PROCESS_STATE_BOUND_TOP_OR_FGS = 3;
+ // Processing running a foreground service.
+ PROCESS_STATE_FGS = 4;
+ // Important foreground process (ime, wallpaper, etc).
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 5;
+ // Important background process.
+ PROCESS_STATE_BACKGROUND = 6;
+ // Process running a receiver.
+ PROCESS_STATE_RECEIVER = 7;
+ // All kinds of cached processes.
+ PROCESS_STATE_CACHED = 8;
+}
+
+// Next tag: 13
message ProcessStatsStateProto {
optional android.service.procstats.ScreenState screen_state = 1;
- optional android.service.procstats.MemoryState memory_state = 2;
+ optional android.service.procstats.MemoryState memory_state = 2 [deprecated = true];
// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
// and not frameworks/base/core/java/android/app/ActivityManager.java
- optional android.service.procstats.ProcessState process_state = 3;
+ optional android.service.procstats.ProcessState process_state = 3 [deprecated = true];
+
+ optional ProcessStateAggregated process_state_aggregated = 10;
// Millisecond uptime duration spent in this state
- optional int64 duration_millis = 4;
+ optional int64 duration_millis = 4 [deprecated = true];
+ // Same as above, but with minute resolution so it fits into an int32.
+ optional int32 duration_minutes = 11;
// Millisecond elapsed realtime duration spent in this state
- optional int64 realtime_duration_millis = 9;
+ optional int64 realtime_duration_millis = 9 [deprecated = true];
+ // Same as above, but with minute resolution so it fits into an int32.
+ optional int32 realtime_duration_minutes = 12;
// # of samples taken
optional int32 sample_size = 5;
// PSS is memory reserved for this process
- optional AggStats pss = 6;
+ optional AggStats pss = 6 [deprecated = true];
// USS is memory shared between processes, divided evenly for accounting
- optional AggStats uss = 7;
+ optional AggStats uss = 7 [deprecated = true];
// RSS is memory resident for this process
optional AggStats rss = 8;
@@ -4779,7 +5021,7 @@
// PSS stats during cached kill
optional AggStats cached_pss = 3;
}
- optional Kill kill = 3;
+ optional Kill kill = 3 [deprecated = true];
// Time and memory spent in various states.
repeated ProcessStatsStateProto states = 5;
@@ -6846,7 +7088,7 @@
// Uid of the package requesting the op
optional int32 uid = 1 [(is_uid) = true];
- // Nmae of the package performing the op
+ // Name of the package performing the op
optional string package_name = 2;
// operation id; maps to the OP_* constants in AppOpsManager.java
@@ -7632,3 +7874,73 @@
// more apps are running / rendering.
optional bool is_today = 16;
}
+
+/**
+ * Message related to dangerous (runtime) app ops access
+ */
+message RuntimeAppOpsAccess {
+ // Uid of the package accessing app op
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Name of the package accessing app op
+ optional string package_name = 2;
+
+ // operation id; maps to the OP_* constants in AppOpsManager.java
+ optional int32 op_id = 3;
+
+ // feature id; provided by developer when accessing related API, limited at 50 chars by API.
+ // Features must be provided through manifest using <feature> tag available in R and above.
+ optional string feature_id = 4;
+
+ // message related to app op access, limited to 600 chars by API
+ optional string message = 5;
+
+ enum SamplingStrategy {
+ DEFAULT = 0;
+ UNIFORM = 1;
+ RARELY_USED = 2;
+ }
+
+ // sampling strategy used to collect this message
+ optional SamplingStrategy sampling_strategy = 6;
+}
+
+/*
+ * Logs userspace reboot outcome and duration.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/server/BootReceiver.java
+ */
+message UserspaceRebootReported {
+ // Possible outcomes of userspace reboot.
+ enum Outcome {
+ // Default value in case platform failed to determine the outcome.
+ OUTCOME_UNKNOWN = 0;
+ // Userspace reboot succeeded (i.e. boot completed without a fall back to hard reboot).
+ SUCCESS = 1;
+ // Userspace reboot shutdown sequence was aborted.
+ FAILED_SHUTDOWN_SEQUENCE_ABORTED = 2;
+ // Remounting userdata into checkpointing mode failed.
+ FAILED_USERDATA_REMOUNT = 3;
+ // Device didn't finish booting before timeout and userspace reboot watchdog issued a hard
+ // reboot.
+ FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED = 4;
+ }
+ // Outcome of userspace reboot. Always set.
+ optional Outcome outcome = 1;
+ // Duration of userspace reboot in case it has a successful outcome.
+ // Duration is measured as time between userspace reboot was initiated and until boot completed
+ // (e.g. sys.boot_completed=1).
+ optional int64 duration_millis = 2;
+ // State of primary user's (user0) credential encryption storage.
+ enum UserEncryptionState {
+ // Default value.
+ USER_ENCRYPTION_STATE_UNKNOWN = 0;
+ // Credential encrypted storage is unlocked.
+ UNLOCKED = 1;
+ // Credential encrypted storage is locked.
+ LOCKED = 2;
+ }
+ // State of primary user's encryption storage at the moment boot completed. Always set.
+ optional UserEncryptionState user_encryption_state = 3;
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index b568033..e37a549 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -60,27 +60,6 @@
std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
- // wifi_bytes_transfer_by_fg_bg
- {{.atomTag = android::util::WIFI_BYTES_TRANSFER_BY_FG_BG},
- {.additiveFields = {3, 4, 5, 6},
- .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
-
- // mobile_bytes_transfer
- {{.atomTag = android::util::MOBILE_BYTES_TRANSFER},
- {.additiveFields = {2, 3, 4, 5},
- .puller = new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
-
- // mobile_bytes_transfer_by_fg_bg
- {{.atomTag = android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG},
- {.additiveFields = {3, 4, 5, 6},
- .puller =
- new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
-
- // bluetooth_bytes_transfer
- {{.atomTag = android::util::BLUETOOTH_BYTES_TRANSFER},
- {.additiveFields = {2, 3},
- .puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
-
// kernel_wakelock
{{.atomTag = android::util::KERNEL_WAKELOCK},
{.puller = new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
@@ -93,37 +72,6 @@
{{.atomTag = android::util::ON_DEVICE_POWER_MEASUREMENT},
{.puller = new PowerStatsPuller()}},
- // cpu_time_per_freq
- {{.atomTag = android::util::CPU_TIME_PER_FREQ},
- {.additiveFields = {3},
- .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
-
- // cpu_time_per_uid
- {{.atomTag = android::util::CPU_TIME_PER_UID},
- {.additiveFields = {2, 3},
- .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
-
- // cpu_time_per_uid_freq
- // the throttling is 3sec, handled in
- // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
- {{.atomTag = android::util::CPU_TIME_PER_UID_FREQ},
- {.additiveFields = {4},
- .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
-
- // cpu_active_time
- // the throttling is 3sec, handled in
- // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
- {{.atomTag = android::util::CPU_ACTIVE_TIME},
- {.additiveFields = {2},
- .puller = new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
-
- // cpu_cluster_time
- // the throttling is 3sec, handled in
- // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
- {{.atomTag = android::util::CPU_CLUSTER_TIME},
- {.additiveFields = {3},
- .puller = new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
-
// wifi_activity_energy_info
{{.atomTag = android::util::WIFI_ACTIVITY_INFO},
{.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
@@ -132,10 +80,6 @@
{{.atomTag = android::util::MODEM_ACTIVITY_INFO},
{.puller = new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
- // bluetooth_activity_info
- {{.atomTag = android::util::BLUETOOTH_ACTIVITY_INFO},
- {.puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
-
// system_elapsed_realtime
{{.atomTag = android::util::SYSTEM_ELAPSED_REALTIME},
{.coolDownNs = NS_PER_SEC,
@@ -249,10 +193,6 @@
.coolDownNs = 3 * NS_PER_SEC,
.puller = new StatsCompanionServicePuller(android::util::DISK_IO)}},
- // PowerProfile constants for power model calculations.
- {{.atomTag = android::util::POWER_PROFILE},
- {.puller = new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
-
// 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*/,
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index ef59c92..3ad21e0 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -28,10 +28,14 @@
StateTracker::StateTracker(const int32_t atomId, const util::StateAtomFieldOptions& stateAtomInfo)
: mAtomId(atomId), mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) {
// create matcher for each primary field
- // TODO(tsaichristine): b/142108433 handle when primary field is first uid in chain
- for (const auto& primary : stateAtomInfo.primaryFields) {
- Matcher matcher = getSimpleMatcher(atomId, primary);
- mPrimaryFields.push_back(matcher);
+ for (const auto& primaryField : stateAtomInfo.primaryFields) {
+ if (primaryField == util::FIRST_UID_IN_CHAIN) {
+ Matcher matcher = getFirstUidMatcher(atomId);
+ mPrimaryFields.push_back(matcher);
+ } else {
+ Matcher matcher = getSimpleMatcher(atomId, primaryField);
+ mPrimaryFields.push_back(matcher);
+ }
}
// TODO(tsaichristine): b/142108433 set default state, reset state, and nesting
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
index 7453370..70f1627 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -72,7 +72,7 @@
int32_t mDefaultState = kStateUnknown;
- int32_t mResetState;
+ int32_t mResetState = kStateUnknown;
// Maps primary key to state value info
std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index 26a3733..84aaa54 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -76,6 +76,23 @@
return event;
}
+// State with first uid in attribution chain as primary field - WakelockStateChanged
+std::shared_ptr<LogEvent> buildPartialWakelockEvent(int uid, const std::string& tag, bool acquire) {
+ std::vector<AttributionNodeInternal> chain;
+ chain.push_back(AttributionNodeInternal());
+ AttributionNodeInternal& attr = chain.back();
+ attr.set_uid(uid);
+
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, 1000 /* timestamp */);
+ event->write(chain);
+ event->write((int32_t)1); // PARTIAL_WAKE_LOCK
+ event->write(tag);
+ event->write(acquire ? 1 : 0);
+ event->init();
+ return event;
+}
+
// State with multiple primary fields - OverlayStateChanged
std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) {
std::shared_ptr<LogEvent> event =
@@ -134,6 +151,39 @@
key->addValue(FieldValue(field1, value1));
key->addValue(FieldValue(field2, value2));
}
+
+void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key) {
+ int pos1[] = {1, 1, 1};
+ int pos3[] = {2, 0, 0};
+ int pos4[] = {3, 0, 0};
+
+ Field field1(10 /* atom id */, pos1, 2 /* depth */);
+
+ Field field3(10 /* atom id */, pos3, 0 /* depth */);
+ Field field4(10 /* atom id */, pos4, 0 /* depth */);
+
+ Value value1((int32_t)uid);
+ Value value3((int32_t)1 /*partial*/);
+ Value value4(tag);
+
+ key->addValue(FieldValue(field1, value1));
+ key->addValue(FieldValue(field3, value3));
+ key->addValue(FieldValue(field4, value4));
+}
+
+void getPartialWakelockKey(int uid, HashableDimensionKey* key) {
+ int pos1[] = {1, 1, 1};
+ int pos3[] = {2, 0, 0};
+
+ Field field1(10 /* atom id */, pos1, 2 /* depth */);
+ Field field3(10 /* atom id */, pos3, 0 /* depth */);
+
+ Value value1((int32_t)uid);
+ Value value3((int32_t)1 /*partial*/);
+
+ key->addValue(FieldValue(field1, value1));
+ key->addValue(FieldValue(field3, value3));
+}
// END: get primary key functions
TEST(StateListenerTest, TestStateListenerWeakPointer) {
@@ -247,7 +297,8 @@
// check StateTracker was updated by querying for state
HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
- EXPECT_EQ(2, getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, queryKey));
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, queryKey));
}
/**
@@ -272,7 +323,46 @@
// check StateTracker was updated by querying for state
HashableDimensionKey queryKey;
getUidProcessKey(1000 /* uid */, &queryKey);
- EXPECT_EQ(1002, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey));
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
+ getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey));
+}
+
+TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::WAKELOCK_STATE_CHANGED, listener1);
+
+ // Log event.
+ std::shared_ptr<LogEvent> event =
+ buildPartialWakelockEvent(1001 /* uid */, "tag1", false /* acquire */);
+ mgr.onLogEvent(*event);
+
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::WAKELOCK_STATE_CHANGED));
+
+ // Check listener was updated.
+ EXPECT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(3, listener1->updates[0].mKey.getValues().size());
+ EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value);
+ EXPECT_EQ("tag1", listener1->updates[0].mKey.getValues()[2].mValue.str_value);
+ EXPECT_EQ(WakelockStateChanged::RELEASE, listener1->updates[0].mState);
+
+ // Check StateTracker was updated by querying for state.
+ HashableDimensionKey queryKey;
+ getPartialWakelockKey(1001 /* uid */, "tag1", &queryKey);
+ EXPECT_EQ(WakelockStateChanged::RELEASE,
+ getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey));
+
+ // No state stored for this query key.
+ HashableDimensionKey queryKey2;
+ getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
+ EXPECT_EQ(-1, getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey2));
+
+ // Partial query fails.
+ HashableDimensionKey queryKey3;
+ getPartialWakelockKey(1001 /* uid */, &queryKey3);
+ EXPECT_EQ(-1, getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey3));
}
/**
@@ -297,7 +387,8 @@
// check StateTracker was updated by querying for state
HashableDimensionKey queryKey;
getOverlayKey(1000 /* uid */, "package1", &queryKey);
- EXPECT_EQ(1, getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey));
+ EXPECT_EQ(OverlayStateChanged::ENTERED,
+ getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey));
}
/**
@@ -326,10 +417,12 @@
sp<TestStateListener> listener1 = new TestStateListener();
sp<TestStateListener> listener2 = new TestStateListener();
sp<TestStateListener> listener3 = new TestStateListener();
+ sp<TestStateListener> listener4 = new TestStateListener();
StateManager mgr;
mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener2);
mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener3);
+ mgr.registerListener(android::util::WAKELOCK_STATE_CHANGED, listener4);
std::shared_ptr<LogEvent> event1 = buildUidProcessEvent(
1000,
@@ -346,8 +439,12 @@
android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
std::shared_ptr<LogEvent> event5 =
buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1);
- std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2);
+ std::shared_ptr<LogEvent> event6 =
+ buildOverlayEvent(1000, "package1", OverlayStateChanged::ENTERED);
+ std::shared_ptr<LogEvent> event7 =
+ buildOverlayEvent(1000, "package2", OverlayStateChanged::EXITED);
+ std::shared_ptr<LogEvent> event8 = buildPartialWakelockEvent(1005, "tag1", true);
+ std::shared_ptr<LogEvent> event9 = buildPartialWakelockEvent(1005, "tag2", false);
mgr.onLogEvent(*event1);
mgr.onLogEvent(*event2);
@@ -356,11 +453,14 @@
mgr.onLogEvent(*event5);
mgr.onLogEvent(*event6);
mgr.onLogEvent(*event7);
+ mgr.onLogEvent(*event8);
+ mgr.onLogEvent(*event9);
// Query for UidProcessState of uid 1001
HashableDimensionKey queryKey1;
getUidProcessKey(1001, &queryKey1);
- EXPECT_EQ(1003, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
+ getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
// Query for UidProcessState of uid 1004 - not in state map
HashableDimensionKey queryKey2;
@@ -370,15 +470,30 @@
// Query for UidProcessState of uid 1001 - after change in state
mgr.onLogEvent(*event4);
- EXPECT_EQ(1002, getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
+ getStateInt(mgr, android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
// Query for ScreenState
- EXPECT_EQ(2, getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ getStateInt(mgr, android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
// Query for OverlayState of uid 1000, package name "package2"
HashableDimensionKey queryKey3;
getOverlayKey(1000, "package2", &queryKey3);
- EXPECT_EQ(2, getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey3));
+ EXPECT_EQ(OverlayStateChanged::EXITED,
+ getStateInt(mgr, android::util::OVERLAY_STATE_CHANGED, queryKey3));
+
+ // Query for WakelockState of uid 1005, tag 2
+ HashableDimensionKey queryKey4;
+ getPartialWakelockKey(1005, "tag2", &queryKey4);
+ EXPECT_EQ(WakelockStateChanged::RELEASE,
+ getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey4));
+
+ // Query for WakelockState of uid 1005, tag 1
+ HashableDimensionKey queryKey5;
+ getPartialWakelockKey(1005, "tag1", &queryKey5);
+ EXPECT_EQ(WakelockStateChanged::ACQUIRE,
+ getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey5));
}
} // namespace statsd
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 7b651df..e0aecce 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -617,19 +617,6 @@
void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) {
EXPECT_EQ(value.field(), atomId);
- // Attribution field.
- EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
- // Uid only.
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).value_int(), uid);
-}
-
-void ValidateUidDimension(const DimensionsValue& value, int atomId, int uid) {
- EXPECT_EQ(value.field(), atomId);
EXPECT_EQ(value.value_tuple().dimensions_value_size(), 1);
// Attribution field.
EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index c0fee6e..e46840c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -1547,6 +1547,34 @@
void onShowModeChanged(@NonNull SoftKeyboardController controller,
@SoftKeyboardShowMode int showMode);
}
+
+ /**
+ * Switches the current IME for the user for whom the service is enabled. The change will
+ * persist until the current IME is explicitly changed again, and may persist beyond the
+ * life cycle of the requesting service.
+ *
+ * @param imeId The ID of the input method to make current. This IME must be installed and
+ * enabled.
+ * @return {@code true} if the current input method was successfully switched to the input
+ * method by {@code imeId},
+ * {@code false} if the input method specified is not installed, not enabled, or
+ * otherwise not available to become the current IME
+ *
+ * @see android.view.inputmethod.InputMethodInfo#getId()
+ */
+ public boolean switchToInputMethod(@NonNull String imeId) {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.switchToInputMethod(imeId);
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ }
+ return false;
+ }
}
/**
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 92889ce..932d0a4 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -91,6 +91,8 @@
void setSoftKeyboardCallbackEnabled(boolean enabled);
+ boolean switchToInputMethod(String imeId);
+
boolean isAccessibilityButtonAvailable();
void sendGesture(int sequence, in ParceledListSlice gestureSteps);
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 8fe2f12..f2702a8 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -25,6 +25,7 @@
import android.annotation.Size;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
import android.app.Activity;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
@@ -40,6 +41,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Parcelable;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -528,12 +530,9 @@
* authenticator known to the AccountManager service. Empty (never
* null) if no authenticators are known.
*/
+ @UserHandleAware
public AuthenticatorDescription[] getAuthenticatorTypes() {
- try {
- return mService.getAuthenticatorTypes(UserHandle.getCallingUserId());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getAuthenticatorTypesAsUser(mContext.getUserId());
}
/**
@@ -584,13 +583,10 @@
* @return An array of {@link Account}, one for each account. Empty (never null) if no accounts
* have been added.
*/
+ @UserHandleAware
@NonNull
public Account[] getAccounts() {
- try {
- return mService.getAccounts(null, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return getAccountsAsUser(mContext.getUserId());
}
/**
@@ -708,6 +704,7 @@
* @return An array of {@link Account}, one per matching account. Empty (never null) if no
* accounts of the specified type have been added.
*/
+ @UserHandleAware
@NonNull
public Account[] getAccountsByType(String type) {
return getAccountsByTypeAsUser(type, mContext.getUser());
@@ -1183,23 +1180,11 @@
* {@link #removeAccount(Account, Activity, AccountManagerCallback, Handler)}
* instead
*/
+ @UserHandleAware
@Deprecated
public AccountManagerFuture<Boolean> removeAccount(final Account account,
AccountManagerCallback<Boolean> callback, Handler handler) {
- if (account == null) throw new IllegalArgumentException("account is null");
- return new Future2Task<Boolean>(handler, callback) {
- @Override
- public void doWork() throws RemoteException {
- mService.removeAccount(mResponse, account, false);
- }
- @Override
- public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
- if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
- throw new AuthenticatorException("no result in response");
- }
- return bundle.getBoolean(KEY_BOOLEAN_RESULT);
- }
- }.start();
+ return removeAccountAsUser(account, callback, handler, mContext.getUser());
}
/**
@@ -1243,15 +1228,10 @@
* adding accounts (of this type) has been disabled by policy
* </ul>
*/
+ @UserHandleAware
public AccountManagerFuture<Bundle> removeAccount(final Account account,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
- if (account == null) throw new IllegalArgumentException("account is null");
- return new AmsTask(activity, handler, callback) {
- @Override
- public void doWork() throws RemoteException {
- mService.removeAccount(mResponse, account, activity != null);
- }
- }.start();
+ return removeAccountAsUser(account, activity, callback, handler, mContext.getUser());
}
/**
@@ -1841,24 +1821,30 @@
* creating a new account, usually because of network trouble
* </ul>
*/
+ @UserHandleAware
public AccountManagerFuture<Bundle> addAccount(final String accountType,
final String authTokenType, final String[] requiredFeatures,
final Bundle addAccountOptions,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
- if (accountType == null) throw new IllegalArgumentException("accountType is null");
- final Bundle optionsIn = new Bundle();
- if (addAccountOptions != null) {
- optionsIn.putAll(addAccountOptions);
- }
- optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
-
- return new AmsTask(activity, handler, callback) {
- @Override
- public void doWork() throws RemoteException {
- mService.addAccount(mResponse, accountType, authTokenType,
- requiredFeatures, activity != null, optionsIn);
+ if (Process.myUserHandle().equals(mContext.getUser())) {
+ if (accountType == null) throw new IllegalArgumentException("accountType is null");
+ final Bundle optionsIn = new Bundle();
+ if (addAccountOptions != null) {
+ optionsIn.putAll(addAccountOptions);
}
- }.start();
+ optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
+
+ return new AmsTask(activity, handler, callback) {
+ @Override
+ public void doWork() throws RemoteException {
+ mService.addAccount(mResponse, accountType, authTokenType,
+ requiredFeatures, activity != null, optionsIn);
+ }
+ }.start();
+ } else {
+ return addAccountAsUser(accountType, authTokenType, requiredFeatures, addAccountOptions,
+ activity, callback, handler, mContext.getUser());
+ }
}
/**
@@ -2002,6 +1988,7 @@
* verifying the password, usually because of network trouble
* </ul>
*/
+ @UserHandleAware
public AccountManagerFuture<Bundle> confirmCredentials(final Account account,
final Bundle options,
final Activity activity,
@@ -3209,6 +3196,7 @@
* </ul>
* @see #startAddAccountSession and #startUpdateCredentialsSession
*/
+ @UserHandleAware
public AccountManagerFuture<Bundle> finishSession(
final Bundle sessionBundle,
final Activity activity,
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 0127138..ce68e08 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -34,7 +34,6 @@
String getPassword(in Account account);
String getUserData(in Account account, String key);
AuthenticatorDescription[] getAuthenticatorTypes(int userId);
- Account[] getAccounts(String accountType, String opPackageName);
Account[] getAccountsForPackage(String packageName, int uid, String opPackageName);
Account[] getAccountsByTypeForPackage(String type, String packageName, String opPackageName);
Account[] getAccountsAsUser(String accountType, int userId, String opPackageName);
@@ -45,8 +44,6 @@
void getAccountsByFeatures(in IAccountManagerResponse response, String accountType,
in String[] features, String opPackageName);
boolean addAccountExplicitly(in Account account, String password, in Bundle extras);
- void removeAccount(in IAccountManagerResponse response, in Account account,
- boolean expectActivityLaunch);
void removeAccountAsUser(in IAccountManagerResponse response, in Account account,
boolean expectActivityLaunch, int userId);
boolean removeAccountExplicitly(in Account account);
diff --git a/core/java/android/annotation/SystemService.java b/core/java/android/annotation/SystemService.java
index 0c5d15e..c05c1ba 100644
--- a/core/java/android/annotation/SystemService.java
+++ b/core/java/android/annotation/SystemService.java
@@ -19,14 +19,12 @@
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.content.Context;
-
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Description of a system service available through
- * {@link Context#getSystemService(Class)}. This is used to auto-generate
+ * {@link android.content.Context#getSystemService(Class)}. This is used to auto-generate
* documentation explaining how to obtain a reference to the service.
*
* @hide
@@ -36,9 +34,9 @@
public @interface SystemService {
/**
* The string name of the system service that can be passed to
- * {@link Context#getSystemService(String)}.
+ * {@link android.content.Context#getSystemService(String)}.
*
- * @see Context#getSystemServiceName(Class)
+ * @see android.content.Context#getSystemServiceName(Class)
*/
String value();
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 9aef20b..c3b07c8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4073,6 +4073,29 @@
}
/**
+ * Updates mcc mnc configuration and applies changes to the entire system.
+ *
+ * @param mcc mcc configuration to update.
+ * @param mnc mnc configuration to update.
+ * @throws RemoteException; IllegalArgumentException if mcc or mnc is null;
+ * @return Returns {@code true} if the configuration was updated successfully;
+ * {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION)
+ public boolean updateMccMncConfiguration(@NonNull String mcc, @NonNull String mnc) {
+ if (mcc == null || mnc == null) {
+ throw new IllegalArgumentException("mcc or mnc cannot be null.");
+ }
+ try {
+ return getService().updateMccMncConfiguration(mcc, mnc);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Logs out current current foreground user by switching to the system user and stopping the
* user being switched from.
* @hide
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index e8494c4..580382e 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -188,6 +188,16 @@
*/
@UnsupportedAppUsage
boolean updateConfiguration(in Configuration values);
+ /**
+ * Updates mcc mnc configuration and applies changes to the entire system.
+ *
+ * @param mcc mcc configuration to update.
+ * @param mnc mnc configuration to update.
+ * @throws RemoteException; IllegalArgumentException if mcc or mnc is null.
+ * @return Returns {@code true} if the configuration was updated;
+ * {@code false} otherwise.
+ */
+ boolean updateMccMncConfiguration(in String mcc, in String mnc);
boolean stopServiceToken(in ComponentName className, in IBinder token, int startId);
@UnsupportedAppUsage
void setProcessLimit(int max);
@@ -345,6 +355,12 @@
in Bundle options, int userId);
@UnsupportedAppUsage
int stopUser(int userid, boolean force, in IStopUserCallback callback);
+ /**
+ * Check {@link com.android.server.am.ActivityManagerService#stopUserWithDelayedLocking(int, boolean, IStopUserCallback)}
+ * for details.
+ */
+ int stopUserWithDelayedLocking(int userid, boolean force, in IStopUserCallback callback);
+
@UnsupportedAppUsage
void registerUserSwitchObserver(in IUserSwitchObserver observer, in String name);
void unregisterUserSwitchObserver(in IUserSwitchObserver observer);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 775b1d1..70262b0 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -251,7 +251,7 @@
}
private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {
- if (appInfo.appComponentFactory != null && cl != null) {
+ if (mIncludeCode && appInfo.appComponentFactory != null && cl != null) {
try {
return (AppComponentFactory)
cl.loadClass(appInfo.appComponentFactory).newInstance();
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index aa11598..d23754e 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -482,19 +482,6 @@
}
}
- if (key.mOverlayDirs != null) {
- for (final String idmapPath : key.mOverlayDirs) {
- try {
- builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
- true /*overlay*/));
- } catch (IOException e) {
- Log.w(TAG, "failed to add overlay path " + idmapPath);
-
- // continue.
- }
- }
- }
-
if (key.mLibDirs != null) {
for (final String libDir : key.mLibDirs) {
if (libDir.endsWith(".apk")) {
@@ -513,6 +500,19 @@
}
}
+ if (key.mOverlayDirs != null) {
+ for (final String idmapPath : key.mOverlayDirs) {
+ try {
+ builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
+ true /*overlay*/));
+ } catch (IOException e) {
+ Log.w(TAG, "failed to add overlay path " + idmapPath);
+
+ // continue.
+ }
+ }
+ }
+
return builder.build();
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index ca3d0d7..a9be9ea 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -170,6 +170,7 @@
import android.service.persistentdata.PersistentDataBlockManager;
import android.service.vr.IVrManager;
import android.telecom.TelecomManager;
+import android.telephony.MmsManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyRegistryManager;
import android.util.ArrayMap;
@@ -345,6 +346,14 @@
}
});
+ registerService(Context.NETWORK_STACK_SERVICE, IBinder.class,
+ new StaticServiceFetcher<IBinder>() {
+ @Override
+ public IBinder createService() {
+ return ServiceManager.getService(Context.NETWORK_STACK_SERVICE);
+ }
+ });
+
registerService(Context.TETHERING_SERVICE, TetheringManager.class,
new CachedServiceFetcher<TetheringManager>() {
@Override
@@ -631,6 +640,13 @@
return new TelecomManager(ctx.getOuterContext());
}});
+ registerService(Context.MMS_SERVICE, MmsManager.class,
+ new CachedServiceFetcher<MmsManager>() {
+ @Override
+ public MmsManager createService(ContextImpl ctx) {
+ return new MmsManager(ctx.getOuterContext());
+ }});
+
registerService(Context.UI_MODE_SERVICE, UiModeManager.class,
new CachedServiceFetcher<UiModeManager>() {
@Override
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 2507991..8eee73c 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -568,8 +568,10 @@
*
* @see Configuration#isScreenWideColorGamut()
* @return True if wcg should be enabled for this device.
+ * @hide
*/
- private boolean shouldEnableWideColorGamut() {
+ @TestApi
+ public boolean shouldEnableWideColorGamut() {
return mWcgEnabled;
}
@@ -877,6 +879,7 @@
* @see #FLAG_SYSTEM
* @hide
*/
+ @TestApi
@RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
public boolean wallpaperSupportsWcg(int which) {
if (!shouldEnableWideColorGamut()) {
@@ -893,6 +896,8 @@
*
* @hide
*/
+ @TestApi
+ @Nullable
@UnsupportedAppUsage
public Bitmap getBitmap() {
return getBitmap(false);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 62b499c..caaa686 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1411,6 +1411,16 @@
= "android.app.action.DEVICE_OWNER_CHANGED";
/**
+ * Broadcast action: sent when the factory reset protection (FRP) policy is changed.
+ *
+ * @see #setFactoryResetProtectionPolicy
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED =
+ "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
+
+ /**
* The ComponentName of the administrator component.
*
* @see #ACTION_ADD_DEVICE_ADMIN
@@ -1982,16 +1992,6 @@
public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14;
/**
- * Result code for {@link #checkProvisioningPreCondition}.
- *
- * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when adding a managed profile is
- * disallowed by {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}.
- *
- * @hide
- */
- public static final int CODE_ADD_MANAGED_PROFILE_DISALLOWED = 15;
-
- /**
* Result codes for {@link #checkProvisioningPreCondition} indicating all the provisioning pre
* conditions.
*
@@ -2003,7 +2003,7 @@
CODE_USER_SETUP_COMPLETED, CODE_NOT_SYSTEM_USER, CODE_HAS_PAIRED,
CODE_MANAGED_USERS_NOT_SUPPORTED, CODE_SYSTEM_USER, CODE_CANNOT_ADD_MANAGED_PROFILE,
CODE_NOT_SYSTEM_USER_SPLIT, CODE_DEVICE_ADMIN_NOT_SUPPORTED,
- CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER, CODE_ADD_MANAGED_PROFILE_DISALLOWED
+ CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER
})
public @interface ProvisioningPreCondition {}
@@ -4329,6 +4329,60 @@
}
/**
+ * Callable by device owner or profile owner of an organization-owned device, to set a
+ * factory reset protection (FRP) policy. When a new policy is set, the system
+ * notifies the FRP management agent of a policy change by broadcasting
+ * {@code ACTION_RESET_PROTECTION_POLICY_CHANGED}.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param policy the new FRP policy, or {@code null} to clear the current policy.
+ * @throws SecurityException if {@code admin} is not a device owner or a profile owner of
+ * an organization-owned device.
+ * @throws UnsupportedOperationException if factory reset protection is not
+ * supported on the device.
+ */
+ public void setFactoryResetProtectionPolicy(@NonNull ComponentName admin,
+ @Nullable FactoryResetProtectionPolicy policy) {
+ throwIfParentInstance("setFactoryResetProtectionPolicy");
+ if (mService != null) {
+ try {
+ mService.setFactoryResetProtectionPolicy(admin, policy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Callable by device owner or profile owner of an organization-owned device, to retrieve
+ * the current factory reset protection (FRP) policy set previously by
+ * {@link #setFactoryResetProtectionPolicy}.
+ * <p>
+ * This method can also be called by the FRP management agent on device, in which case,
+ * it can pass {@code null} as the ComponentName.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with or
+ * {@code null} if called by the FRP management agent on device.
+ * @return The current FRP policy object or {@code null} if no policy is set.
+ * @throws SecurityException if {@code admin} is not a device owner, a profile owner of
+ * an organization-owned device or the FRP management agent.
+ * @throws UnsupportedOperationException if factory reset protection is not
+ * supported on the device.
+ */
+ public @Nullable FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(
+ @Nullable ComponentName admin) {
+ throwIfParentInstance("getFactoryResetProtectionPolicy");
+ if (mService != null) {
+ try {
+ return mService.getFactoryResetProtectionPolicy(admin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
* Called by an application that is administering the device to set the
* global proxy and exclusion list.
* <p>
diff --git a/core/java/android/net/NetworkMisc.aidl b/core/java/android/app/admin/FactoryResetProtectionPolicy.aidl
similarity index 81%
copy from core/java/android/net/NetworkMisc.aidl
copy to core/java/android/app/admin/FactoryResetProtectionPolicy.aidl
index c65583f..72e639a 100644
--- a/core/java/android/net/NetworkMisc.aidl
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net;
+package android.app.admin;
-parcelable NetworkMisc;
+parcelable FactoryResetProtectionPolicy;
diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
new file mode 100644
index 0000000..ed74779
--- /dev/null
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
@@ -0,0 +1,237 @@
+/*
+ * 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.app.admin;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The factory reset protection policy determines which accounts can unlock a device that
+ * has gone through untrusted factory reset.
+ * <p>
+ * Only a device owner or profile owner of an organization-owned device can set a factory
+ * reset protection policy for the device by calling the {@code DevicePolicyManager} method
+ * {@link DevicePolicyManager#setFactoryResetProtectionPolicy(ComponentName,
+ * FactoryResetProtectionPolicy)}}.
+ *
+ * @see DevicePolicyManager#setFactoryResetProtectionPolicy
+ * @see DevicePolicyManager#getFactoryResetProtectionPolicy
+ */
+public final class FactoryResetProtectionPolicy implements Parcelable {
+
+ private static final String LOG_TAG = "FactoryResetProtectionPolicy";
+
+ private static final String KEY_FACTORY_RESET_PROTECTION_ACCOUNT =
+ "factory_reset_protection_account";
+ private static final String KEY_FACTORY_RESET_PROTECTION_DISABLED =
+ "factory_reset_protection_disabled";
+ private static final String ATTR_VALUE = "value";
+
+ private final List<String> mFactoryResetProtectionAccounts;
+ private final boolean mFactoryResetProtectionDisabled;
+
+ private FactoryResetProtectionPolicy(List<String> factoryResetProtectionAccounts,
+ boolean factoryResetProtectionDisabled) {
+ mFactoryResetProtectionAccounts = factoryResetProtectionAccounts;
+ mFactoryResetProtectionDisabled = factoryResetProtectionDisabled;
+ }
+
+ /**
+ * Get the list of accounts that can provision a device which has been factory reset.
+ */
+ public @NonNull List<String> getFactoryResetProtectionAccounts() {
+ return mFactoryResetProtectionAccounts;
+ }
+
+ /**
+ * Return whether factory reset protection for the device is disabled or not.
+ */
+ public boolean isFactoryResetProtectionDisabled() {
+ return mFactoryResetProtectionDisabled;
+ }
+
+ /**
+ * Builder class for {@link FactoryResetProtectionPolicy} objects.
+ */
+ public static class Builder {
+ private List<String> mFactoryResetProtectionAccounts;
+ private boolean mFactoryResetProtectionDisabled;
+
+ /**
+ * Initialize a new Builder to construct a {@link FactoryResetProtectionPolicy}.
+ */
+ public Builder() {
+ };
+
+ /**
+ * Sets which accounts can unlock a device that has been factory reset.
+ * <p>
+ * Once set, the consumer unlock flow will be disabled and only accounts in this list
+ * can unlock factory reset protection after untrusted factory reset.
+ * <p>
+ * It's up to the FRP management agent to interpret the {@code String} as account it
+ * supports. Please consult their relevant documentation for details.
+ *
+ * @param factoryResetProtectionAccounts list of accounts.
+ * @return the same Builder instance.
+ */
+ @NonNull
+ public Builder setFactoryResetProtectionAccounts(
+ @NonNull List<String> factoryResetProtectionAccounts) {
+ mFactoryResetProtectionAccounts = new ArrayList<>(factoryResetProtectionAccounts);
+ return this;
+ }
+
+ /**
+ * Sets whether factory reset protection is disabled or not.
+ * <p>
+ * Once disabled, factory reset protection will not kick in all together when the device
+ * goes through untrusted factory reset. This applies to both the consumer unlock flow and
+ * the admin account overrides via {@link #setFactoryResetProtectionAccounts}
+ *
+ * @param factoryResetProtectionDisabled Whether the policy is disabled or not.
+ * @return the same Builder instance.
+ */
+ @NonNull
+ public Builder setFactoryResetProtectionDisabled(boolean factoryResetProtectionDisabled) {
+ mFactoryResetProtectionDisabled = factoryResetProtectionDisabled;
+ return this;
+ }
+
+ /**
+ * Combines all of the attributes that have been set on this {@code Builder}
+ *
+ * @return a new {@link FactoryResetProtectionPolicy} object.
+ */
+ @NonNull
+ public FactoryResetProtectionPolicy build() {
+ return new FactoryResetProtectionPolicy(mFactoryResetProtectionAccounts,
+ mFactoryResetProtectionDisabled);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "FactoryResetProtectionPolicy{"
+ + "mFactoryResetProtectionAccounts=" + mFactoryResetProtectionAccounts
+ + ", mFactoryResetProtectionDisabled=" + mFactoryResetProtectionDisabled
+ + '}';
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, @Nullable int flags) {
+ int accountsCount = mFactoryResetProtectionAccounts.size();
+ dest.writeInt(accountsCount);
+ for (String account: mFactoryResetProtectionAccounts) {
+ dest.writeString(account);
+ }
+ dest.writeBoolean(mFactoryResetProtectionDisabled);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<FactoryResetProtectionPolicy> CREATOR =
+ new Creator<FactoryResetProtectionPolicy>() {
+
+ @Override
+ public FactoryResetProtectionPolicy createFromParcel(Parcel in) {
+ List<String> factoryResetProtectionAccounts = new ArrayList<>();
+ int accountsCount = in.readInt();
+ for (int i = 0; i < accountsCount; i++) {
+ factoryResetProtectionAccounts.add(in.readString());
+ }
+ boolean factoryResetProtectionDisabled = in.readBoolean();
+
+ return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
+ factoryResetProtectionDisabled);
+ }
+
+ @Override
+ public FactoryResetProtectionPolicy[] newArray(int size) {
+ return new FactoryResetProtectionPolicy[size];
+ }
+ };
+
+ /**
+ * Restore a previously saved FactoryResetProtectionPolicy from XML.
+ * <p>
+ * No validation is required on the reconstructed policy since the XML was previously
+ * created by the system server from a validated policy.
+ * @hide
+ */
+ @Nullable
+ public static FactoryResetProtectionPolicy readFromXml(@NonNull XmlPullParser parser) {
+ try {
+ boolean factoryResetProtectionDisabled = Boolean.parseBoolean(
+ parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_DISABLED));
+
+ List<String> factoryResetProtectionAccounts = new ArrayList<>();
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT
+ && (type != END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == END_TAG || type == TEXT) {
+ continue;
+ }
+ if (!parser.getName().equals(KEY_FACTORY_RESET_PROTECTION_ACCOUNT)) {
+ continue;
+ }
+ factoryResetProtectionAccounts.add(
+ parser.getAttributeValue(null, ATTR_VALUE));
+ }
+
+ return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
+ factoryResetProtectionDisabled);
+ } catch (XmlPullParserException | IOException e) {
+ Log.w(LOG_TAG, "Reading from xml failed", e);
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public void writeToXml(@NonNull XmlSerializer out) throws IOException {
+ out.attribute(null, KEY_FACTORY_RESET_PROTECTION_DISABLED,
+ Boolean.toString(mFactoryResetProtectionDisabled));
+ for (String account : mFactoryResetProtectionAccounts) {
+ out.startTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT);
+ out.attribute(null, ATTR_VALUE, account);
+ out.endTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT);
+ }
+ }
+
+}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 3eec46b..21c9eb5 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -24,6 +24,7 @@
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.PasswordMetrics;
+import android.app.admin.FactoryResetProtectionPolicy;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
@@ -104,6 +105,9 @@
void wipeDataWithReason(int flags, String wipeReasonForUser, boolean parent);
+ void setFactoryResetProtectionPolicy(in ComponentName who, in FactoryResetProtectionPolicy policy);
+ FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(in ComponentName who);
+
ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
ComponentName getGlobalProxyAdmin(int userHandle);
void setRecommendedGlobalProxy(in ComponentName admin, in ProxyInfo proxyInfo);
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index 479e4b4..bd649f8 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.TimestampedValue;
@@ -28,17 +29,23 @@
import java.util.Objects;
/**
- * A time signal from a telephony source. The value can be {@code null} to indicate that the
- * telephony source has entered an "un-opinionated" state and any previously sent suggestions are
- * being withdrawn. When not {@code null}, the value consists of the number of milliseconds elapsed
- * since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime clock when that number
- * was established. The elapsed realtime clock is considered accurate but volatile, so time signals
- * must not be persisted across device resets.
+ * A time suggestion from an identified telephony source. e.g. from NITZ information from a specific
+ * radio.
+ *
+ * <p>The time value can be {@code null} to indicate that the telephony source has entered an
+ * "un-opinionated" state and any previous suggestions from the source are being withdrawn. When not
+ * {@code null}, the value consists of the number of milliseconds elapsed since 1/1/1970 00:00:00
+ * UTC and the time according to the elapsed realtime clock when that number was established. The
+ * elapsed realtime clock is considered accurate but volatile, so time suggestions must not be
+ * persisted across device resets.
*
* @hide
*/
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class PhoneTimeSuggestion implements Parcelable {
+ /** @hide */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final @NonNull Parcelable.Creator<PhoneTimeSuggestion> CREATOR =
new Parcelable.Creator<PhoneTimeSuggestion>() {
public PhoneTimeSuggestion createFromParcel(Parcel in) {
@@ -85,15 +92,27 @@
dest.writeList(mDebugInfo);
}
+ /**
+ * Returns an identifier for the source of this suggestion. When a device has several "phones",
+ * i.e. sim slots or equivalent, it is used to identify which one.
+ */
public int getPhoneId() {
return mPhoneId;
}
+ /**
+ * Returns the suggestion. {@code null} means that the caller is no longer sure what time it
+ * is.
+ */
@Nullable
public TimestampedValue<Long> getUtcTime() {
return mUtcTime;
}
+ /**
+ * Returns debug metadata for the suggestion. The information is present in {@link #toString()}
+ * but is not considered for {@link #equals(Object)} and {@link #hashCode()}.
+ */
@NonNull
public List<String> getDebugInfo() {
return mDebugInfo == null
@@ -105,7 +124,7 @@
* information is present in {@link #toString()} but is not considered for
* {@link #equals(Object)} and {@link #hashCode()}.
*/
- public void addDebugInfo(String debugInfo) {
+ public void addDebugInfo(@NonNull String debugInfo) {
if (mDebugInfo == null) {
mDebugInfo = new ArrayList<>();
}
@@ -156,16 +175,19 @@
*
* @hide
*/
- public static class Builder {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final class Builder {
private final int mPhoneId;
- private TimestampedValue<Long> mUtcTime;
- private List<String> mDebugInfo;
+ @Nullable private TimestampedValue<Long> mUtcTime;
+ @Nullable private List<String> mDebugInfo;
+ /** Creates a builder with the specified {@code phoneId}. */
public Builder(int phoneId) {
mPhoneId = phoneId;
}
/** Returns the builder for call chaining. */
+ @NonNull
public Builder setUtcTime(@Nullable TimestampedValue<Long> utcTime) {
if (utcTime != null) {
// utcTime can be null, but the value it holds cannot.
@@ -177,6 +199,7 @@
}
/** Returns the builder for call chaining. */
+ @NonNull
public Builder addDebugInfo(@NonNull String debugInfo) {
if (mDebugInfo == null) {
mDebugInfo = new ArrayList<>();
@@ -186,6 +209,7 @@
}
/** Returns the {@link PhoneTimeSuggestion}. */
+ @NonNull
public PhoneTimeSuggestion build() {
return new PhoneTimeSuggestion(this);
}
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 54dd1be..7c29f01 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
@@ -29,8 +30,11 @@
/**
* The interface through which system components can send signals to the TimeDetectorService.
+ *
+ * <p>This class is marked non-final for mockito.
* @hide
*/
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@SystemService(Context.TIME_DETECTOR_SERVICE)
public class TimeDetector {
private static final String TAG = "timedetector.TimeDetector";
@@ -38,6 +42,7 @@
private final ITimeDetectorService mITimeDetectorService;
+ /** @hide */
public TimeDetector() throws ServiceNotFoundException {
mITimeDetectorService = ITimeDetectorService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TIME_DETECTOR_SERVICE));
@@ -62,6 +67,8 @@
/**
* Suggests the user's manually entered current time to the detector.
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE)
public void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion) {
@@ -77,6 +84,8 @@
/**
* A shared utility method to create a {@link ManualTimeSuggestion}.
+ *
+ * @hide
*/
public static ManualTimeSuggestion createManualTimeSuggestion(long when, String why) {
TimestampedValue<Long> utcTime =
@@ -88,6 +97,8 @@
/**
* Suggests the time according to a network time source like NTP.
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.SET_TIME)
public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
index e8162488..d71ffcb 100644
--- a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,12 +31,27 @@
import java.util.Objects;
/**
- * A suggested time zone from a Phone-based signal, e.g. from MCC and NITZ information.
+ * A time zone suggestion from an identified telephony source, e.g. from MCC and NITZ information
+ * associated with a specific radio.
+ *
+ * <p>The time zone ID can be {@code null} to indicate that the telephony source has entered an
+ * "un-opinionated" state and any previous suggestions from that source are being withdrawn.
+ * When not {@code null}, the value consists of a suggested time zone ID and metadata that can be
+ * used to judge quality / certainty of the suggestion.
+ *
+ * <p>{@code matchType} must be set to {@link #MATCH_TYPE_NA} when {@code zoneId} is {@code null},
+ * and one of the other {@code MATCH_TYPE_} values when it is not {@code null}.
+ *
+ * <p>{@code quality} must be set to {@link #QUALITY_NA} when {@code zoneId} is {@code null},
+ * and one of the other {@code QUALITY_} values when it is not {@code null}.
*
* @hide
*/
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class PhoneTimeZoneSuggestion implements Parcelable {
+ /** @hide */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@NonNull
public static final Creator<PhoneTimeZoneSuggestion> CREATOR =
new Creator<PhoneTimeZoneSuggestion>() {
@@ -58,6 +74,7 @@
return new Builder(phoneId).addDebugInfo(debugInfo).build();
}
+ /** @hide */
@IntDef({ MATCH_TYPE_NA, MATCH_TYPE_NETWORK_COUNTRY_ONLY, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET,
MATCH_TYPE_EMULATOR_ZONE_ID, MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY })
@Retention(RetentionPolicy.SOURCE)
@@ -90,6 +107,7 @@
*/
public static final int MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY = 5;
+ /** @hide */
@IntDef({ QUALITY_NA, QUALITY_SINGLE_ZONE, QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET,
QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS })
@Retention(RetentionPolicy.SOURCE)
@@ -115,7 +133,7 @@
/**
* The ID of the phone this suggestion is associated with. For multiple-sim devices this
- * helps to establish origin so filtering / stickiness can be implemented.
+ * helps to establish source so filtering / stickiness can be implemented.
*/
private final int mPhoneId;
@@ -123,6 +141,7 @@
* The suggestion. {@code null} means there is no current suggestion and any previous suggestion
* should be forgotten.
*/
+ @Nullable
private final String mZoneId;
/**
@@ -139,9 +158,10 @@
private final int mQuality;
/**
- * Free-form debug information about how the signal was derived. Used for debug only,
+ * Free-form debug information about how the suggestion was derived. Used for debug only,
* intentionally not used in equals(), etc.
*/
+ @Nullable
private List<String> mDebugInfo;
private PhoneTimeZoneSuggestion(Builder builder) {
@@ -182,25 +202,47 @@
return 0;
}
+ /**
+ * Returns an identifier for the source of this suggestion. When a device has several "phones",
+ * i.e. sim slots or equivalent, it is used to identify which one.
+ */
public int getPhoneId() {
return mPhoneId;
}
+ /**
+ * Returns the suggested time zone Olson ID, e.g. "America/Los_Angeles". {@code null} means that
+ * the caller is no longer sure what the current time zone is. See
+ * {@link PhoneTimeZoneSuggestion} for the associated {@code matchType} / {@code quality} rules.
+ */
@Nullable
public String getZoneId() {
return mZoneId;
}
+ /**
+ * Returns information about how the suggestion was determined which could be used to rank
+ * suggestions when several are available from different sources. See
+ * {@link PhoneTimeZoneSuggestion} for the associated rules.
+ */
@MatchType
public int getMatchType() {
return mMatchType;
}
+ /**
+ * Returns information about the likelihood of the suggested zone being correct. See
+ * {@link PhoneTimeZoneSuggestion} for the associated rules.
+ */
@Quality
public int getQuality() {
return mQuality;
}
+ /**
+ * Returns debug metadata for the suggestion. The information is present in {@link #toString()}
+ * but is not considered for {@link #equals(Object)} and {@link #hashCode()}.
+ */
@NonNull
public List<String> getDebugInfo() {
return mDebugInfo == null
@@ -267,36 +309,43 @@
*
* @hide
*/
- public static class Builder {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final class Builder {
private final int mPhoneId;
- private String mZoneId;
+ @Nullable private String mZoneId;
@MatchType private int mMatchType;
@Quality private int mQuality;
- private List<String> mDebugInfo;
+ @Nullable private List<String> mDebugInfo;
public Builder(int phoneId) {
mPhoneId = phoneId;
}
- /** Returns the builder for call chaining. */
- public Builder setZoneId(String zoneId) {
+ /**
+ * Returns the builder for call chaining.
+ */
+ @NonNull
+ public Builder setZoneId(@Nullable String zoneId) {
mZoneId = zoneId;
return this;
}
/** Returns the builder for call chaining. */
+ @NonNull
public Builder setMatchType(@MatchType int matchType) {
mMatchType = matchType;
return this;
}
/** Returns the builder for call chaining. */
+ @NonNull
public Builder setQuality(@Quality int quality) {
mQuality = quality;
return this;
}
/** Returns the builder for call chaining. */
+ @NonNull
public Builder addDebugInfo(@NonNull String debugInfo) {
if (mDebugInfo == null) {
mDebugInfo = new ArrayList<>();
@@ -333,6 +382,7 @@
}
/** Returns the {@link PhoneTimeZoneSuggestion}. */
+ @NonNull
public PhoneTimeZoneSuggestion build() {
validate();
return new PhoneTimeZoneSuggestion(this);
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index e165d8a..5b5f311 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
@@ -28,8 +29,10 @@
/**
* The interface through which system components can send signals to the TimeZoneDetectorService.
*
+ * <p>This class is non-final for mockito.
* @hide
*/
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
public class TimeZoneDetector {
private static final String TAG = "timezonedetector.TimeZoneDetector";
@@ -37,6 +40,7 @@
private final ITimeZoneDetectorService mITimeZoneDetectorService;
+ /** @hide */
public TimeZoneDetector() throws ServiceNotFoundException {
mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
@@ -46,7 +50,10 @@
* Suggests the current time zone, determined using telephony signals, to the detector. The
* detector may ignore the signal based on system settings, whether better information is
* available, and so on.
+ *
+ * @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE)
public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) {
if (DEBUG) {
@@ -62,6 +69,8 @@
/**
* Suggests the current time zone, determined for the user's manually information, to the
* detector. The detector may ignore the signal based on system settings.
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE)
public void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
@@ -77,6 +86,8 @@
/**
* A shared utility method to create a {@link ManualTimeZoneSuggestion}.
+ *
+ * @hide
*/
public static ManualTimeZoneSuggestion createManualTimeZoneSuggestion(String tzId, String why) {
ManualTimeZoneSuggestion suggestion = new ManualTimeZoneSuggestion(tzId);
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 4346d97..9c4a8f4 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -16,7 +16,10 @@
package android.app.usage;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.usage.NetworkStats.Bucket;
@@ -27,6 +30,9 @@
import android.net.INetworkStatsService;
import android.net.NetworkIdentity;
import android.net.NetworkTemplate;
+import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProviderWrapper;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
@@ -519,6 +525,34 @@
private DataUsageRequest request;
}
+ /**
+ * Registers a custom provider of {@link android.net.NetworkStats} to combine the network
+ * statistics that cannot be seen by the kernel to system. To unregister, invoke
+ * {@link NetworkStatsProviderCallback#unregister()}.
+ *
+ * @param tag a human readable identifier of the custom network stats provider.
+ * @param provider a custom implementation of {@link AbstractNetworkStatsProvider} that needs to
+ * be registered to the system.
+ * @return a {@link NetworkStatsProviderCallback}, which can be used to report events to the
+ * system.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ @NonNull public NetworkStatsProviderCallback registerNetworkStatsProvider(
+ @NonNull String tag,
+ @NonNull AbstractNetworkStatsProvider provider) {
+ try {
+ final NetworkStatsProviderWrapper wrapper = new NetworkStatsProviderWrapper(provider);
+ return new NetworkStatsProviderCallback(
+ mService.registerNetworkStatsProvider(tag, wrapper));
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ // Unreachable code, but compiler doesn't know about it.
+ return null;
+ }
+
private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
final NetworkTemplate template;
switch (networkType) {
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 176a181..a60e591 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -163,7 +163,6 @@
/**
* The app spent sufficient time in the old bucket without any substantial event so it reached
* the timeout threshold to have its bucket lowered.
- *
* @hide
*/
public static final int REASON_MAIN_TIMEOUT = 0x0200;
@@ -173,15 +172,25 @@
*/
public static final int REASON_MAIN_USAGE = 0x0300;
/**
- * Forced by a core UID.
+ * Forced by the user/developer, either explicitly or implicitly through some action. If user
+ * action was not involved and this is purely due to the system,
+ * {@link #REASON_MAIN_FORCED_BY_SYSTEM} should be used instead.
* @hide
*/
- public static final int REASON_MAIN_FORCED = 0x0400;
+ public static final int REASON_MAIN_FORCED_BY_USER = 0x0400;
/**
- * Set by a privileged system app.
+ * Set by a privileged system app. This may be overridden by
+ * {@link #REASON_MAIN_FORCED_BY_SYSTEM} or user action.
* @hide
*/
public static final int REASON_MAIN_PREDICTED = 0x0500;
+ /**
+ * Forced by the system, independent of user action. If user action is involved,
+ * {@link #REASON_MAIN_FORCED_BY_USER} should be used instead. When this is used, only
+ * {@link #REASON_MAIN_FORCED_BY_SYSTEM} or user action can change the bucket.
+ * @hide
+ */
+ public static final int REASON_MAIN_FORCED_BY_SYSTEM = 0x0600;
/** @hide */
public static final int REASON_SUB_MASK = 0x00FF;
@@ -1016,7 +1025,10 @@
case REASON_MAIN_DEFAULT:
sb.append("d");
break;
- case REASON_MAIN_FORCED:
+ case REASON_MAIN_FORCED_BY_SYSTEM:
+ sb.append("s");
+ break;
+ case REASON_MAIN_FORCED_BY_USER:
sb.append("f");
break;
case REASON_MAIN_PREDICTED:
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 44b2df6..9ef9574 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3416,6 +3416,7 @@
TELEPHONY_SUBSCRIPTION_SERVICE,
CARRIER_CONFIG_SERVICE,
EUICC_SERVICE,
+ MMS_SERVICE,
TELECOM_SERVICE,
CLIPBOARD_SERVICE,
INPUT_METHOD_SERVICE,
@@ -3612,6 +3613,8 @@
* @see android.telephony.CarrierConfigManager
* @see #EUICC_SERVICE
* @see android.telephony.euicc.EuiccManager
+ * @see #MMS_SERVICE
+ * @see android.telephony.MmsManager
* @see #INPUT_METHOD_SERVICE
* @see android.view.inputmethod.InputMethodManager
* @see #UI_MODE_SERVICE
@@ -3950,10 +3953,12 @@
/**
* Use with {@link android.os.ServiceManager.getService()} to retrieve a
- * {@link NetworkStackClient} IBinder for communicating with the network stack
+ * {@link INetworkStackConnector} IBinder for communicating with the network stack
* @hide
* @see NetworkStackClient
*/
+ @SystemApi
+ @TestApi
public static final String NETWORK_STACK_SERVICE = "network_stack";
/**
@@ -4288,6 +4293,15 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.telephony.MmsManager} to send/receive MMS messages.
+ *
+ * @see #getSystemService(String)
+ * @see android.telephony.MmsManager
+ */
+ public static final String MMS_SERVICE = "mms";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.ClipboardManager} for accessing and modifying
* the contents of the global clipboard.
*
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 32803ab..87acbc1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -7803,7 +7803,7 @@
ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
}
ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
- ai.resourceDirs = state.overlayPaths;
+ ai.resourceDirs = state.getAllOverlayPaths();
ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes;
}
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index f0f6753..da7623a 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -46,6 +46,8 @@
import java.io.IOException;
import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Map;
import java.util.Objects;
/**
@@ -77,7 +79,9 @@
public ArraySet<String> disabledComponents;
public ArraySet<String> enabledComponents;
- public String[] overlayPaths;
+ private String[] overlayPaths;
+ private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths
+ private String[] cachedOverlayPaths;
@UnsupportedAppUsage
public PackageUserState() {
@@ -112,9 +116,33 @@
enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
overlayPaths =
o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length);
+ if (o.sharedLibraryOverlayPaths != null) {
+ sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths);
+ }
harmfulAppWarning = o.harmfulAppWarning;
}
+ public String[] getOverlayPaths() {
+ return overlayPaths;
+ }
+
+ public void setOverlayPaths(String[] paths) {
+ overlayPaths = paths;
+ cachedOverlayPaths = null;
+ }
+
+ public Map<String, String[]> getSharedLibraryOverlayPaths() {
+ return sharedLibraryOverlayPaths;
+ }
+
+ public void setSharedLibraryOverlayPaths(String library, String[] paths) {
+ if (sharedLibraryOverlayPaths == null) {
+ sharedLibraryOverlayPaths = new ArrayMap<>();
+ }
+ sharedLibraryOverlayPaths.put(library, paths);
+ cachedOverlayPaths = null;
+ }
+
/**
* Test if this package is installed.
*/
@@ -235,6 +263,38 @@
return isComponentEnabled;
}
+ public String[] getAllOverlayPaths() {
+ if (overlayPaths == null && sharedLibraryOverlayPaths == null) {
+ return null;
+ }
+
+ if (cachedOverlayPaths != null) {
+ return cachedOverlayPaths;
+ }
+
+ final LinkedHashSet<String> paths = new LinkedHashSet<>();
+ if (overlayPaths != null) {
+ final int N = overlayPaths.length;
+ for (int i = 0; i < N; i++) {
+ paths.add(overlayPaths[i]);
+ }
+ }
+
+ if (sharedLibraryOverlayPaths != null) {
+ for (String[] libOverlayPaths : sharedLibraryOverlayPaths.values()) {
+ if (libOverlayPaths != null) {
+ final int N = libOverlayPaths.length;
+ for (int i = 0; i < N; i++) {
+ paths.add(libOverlayPaths[i]);
+ }
+ }
+ }
+ }
+
+ cachedOverlayPaths = paths.toArray(new String[0]);
+ return cachedOverlayPaths;
+ }
+
@Override
final public boolean equals(Object obj) {
if (!(obj instanceof PackageUserState)) {
diff --git a/core/java/android/content/pm/parsing/PackageInfoUtils.java b/core/java/android/content/pm/parsing/PackageInfoUtils.java
index f2cf9a4..73a8d2a 100644
--- a/core/java/android/content/pm/parsing/PackageInfoUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoUtils.java
@@ -41,9 +41,11 @@
import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
+import android.util.ArraySet;
import com.android.internal.util.ArrayUtils;
+import java.util.LinkedHashSet;
import java.util.Set;
/** @hide */
@@ -545,7 +547,7 @@
ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
}
ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
- ai.resourceDirs = state.overlayPaths;
+ ai.resourceDirs = state.getAllOverlayPaths();
ai.icon = (PackageParser.sUseRoundIcon && ai.roundIconRes != 0)
? ai.roundIconRes : ai.iconRes;
}
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index 698876b..11cf2d6 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -18,6 +18,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
import android.os.CancellationSignal;
import android.os.Parcelable;
@@ -119,6 +120,7 @@
class AuthenticationResult {
private Identifier mIdentifier;
private CryptoObject mCryptoObject;
+ private @AuthenticationResultType int mAuthenticationType;
private int mUserId;
/**
@@ -129,27 +131,41 @@
/**
* Authentication result
* @param crypto
+ * @param authenticationType
* @param identifier
* @param userId
* @hide
*/
- public AuthenticationResult(CryptoObject crypto, Identifier identifier,
+ public AuthenticationResult(CryptoObject crypto,
+ @AuthenticationResultType int authenticationType, Identifier identifier,
int userId) {
mCryptoObject = crypto;
+ mAuthenticationType = authenticationType;
mIdentifier = identifier;
mUserId = userId;
}
/**
- * Obtain the crypto object associated with this transaction
- * @return crypto object provided to {@link BiometricAuthenticator#authenticate(
- * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
+ * Provides the crypto object associated with this transaction.
+ * @return The crypto object provided to {@link BiometricPrompt#authenticate(
+ * BiometricPrompt.CryptoObject, CancellationSignal, Executor,
+ * BiometricPrompt.AuthenticationCallback)}
*/
public CryptoObject getCryptoObject() {
return mCryptoObject;
}
/**
+ * Provides the type of authentication (e.g. device credential or biometric) that was
+ * requested from and successfully provided by the user.
+ *
+ * @return An integer value representing the authentication method used.
+ */
+ public @AuthenticationResultType int getAuthenticationType() {
+ return mAuthenticationType;
+ }
+
+ /**
* Obtain the biometric identifier associated with this operation. Applications are strongly
* discouraged from associating specific identifiers with specific applications or
* operations.
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index cb8fc8b..a695ce8 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -21,6 +21,7 @@
import static android.hardware.biometrics.BiometricManager.Authenticators;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -40,6 +41,8 @@
import com.android.internal.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.Signature;
import java.util.concurrent.Executor;
@@ -397,9 +400,11 @@
new IBiometricServiceReceiver.Stub() {
@Override
- public void onAuthenticationSucceeded() throws RemoteException {
+ public void onAuthenticationSucceeded(@AuthenticationResultType int authenticationType)
+ throws RemoteException {
mExecutor.execute(() -> {
- final AuthenticationResult result = new AuthenticationResult(mCryptoObject);
+ final AuthenticationResult result =
+ new AuthenticationResult(mCryptoObject, authenticationType);
mAuthenticationCallback.onAuthenticationSucceeded(result);
});
}
@@ -576,28 +581,62 @@
}
/**
- * Container for callback data from {@link #authenticate( CancellationSignal, Executor,
+ * Authentication type reported by {@link AuthenticationResult} when the user authenticated by
+ * entering their device PIN, pattern, or password.
+ */
+ public static final int AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL = 1;
+
+ /**
+ * Authentication type reported by {@link AuthenticationResult} when the user authenticated by
+ * presenting some form of biometric (e.g. fingerprint or face).
+ */
+ public static final int AUTHENTICATION_RESULT_TYPE_BIOMETRIC = 2;
+
+ /**
+ * An {@link IntDef} representing the type of auth, as reported by {@link AuthenticationResult}.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL, AUTHENTICATION_RESULT_TYPE_BIOMETRIC})
+ public @interface AuthenticationResultType {
+ }
+
+ /**
+ * Container for callback data from {@link #authenticate(CancellationSignal, Executor,
* AuthenticationCallback)} and {@link #authenticate(CryptoObject, CancellationSignal, Executor,
- * AuthenticationCallback)}
+ * AuthenticationCallback)}.
*/
public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult {
/**
* Authentication result
* @param crypto
+ * @param authenticationType
* @hide
*/
- public AuthenticationResult(CryptoObject crypto) {
+ public AuthenticationResult(CryptoObject crypto,
+ @AuthenticationResultType int authenticationType) {
// Identifier and userId is not used for BiometricPrompt.
- super(crypto, null /* identifier */, 0 /* userId */);
+ super(crypto, authenticationType, null /* identifier */, 0 /* userId */);
}
+
/**
- * Obtain the crypto object associated with this transaction
- * @return crypto object provided to {@link #authenticate( CryptoObject, CancellationSignal,
- * Executor, AuthenticationCallback)}
+ * Provides the crypto object associated with this transaction.
+ * @return The crypto object provided to {@link #authenticate(CryptoObject,
+ * CancellationSignal, Executor, AuthenticationCallback)}
*/
public CryptoObject getCryptoObject() {
return (CryptoObject) super.getCryptoObject();
}
+
+ /**
+ * Provides the type of authentication (e.g. device credential or biometric) that was
+ * requested from and successfully provided by the user.
+ *
+ * @return An integer value representing the authentication method used.
+ */
+ public @AuthenticationResultType int getAuthenticationType() {
+ return super.getAuthenticationType();
+ }
}
/**
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
index c960049..1d43aa6 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
@@ -20,8 +20,8 @@
* @hide
*/
oneway interface IBiometricServiceReceiver {
- // Notify BiometricPrompt that authentication was successful
- void onAuthenticationSucceeded();
+ // Notify BiometricPrompt that authentication was successful.
+ void onAuthenticationSucceeded(int authenticationType);
// Noties that authentication failed.
void onAuthenticationFailed();
// Notify BiometricPrompt that an error has occurred.
diff --git a/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java b/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java
index 07d2443..6ead64c 100644
--- a/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java
@@ -25,9 +25,12 @@
* A constrained high speed capture session for a {@link CameraDevice}, used for capturing high
* speed images from the {@link CameraDevice} for high speed video recording use case.
* <p>
- * A CameraHighSpeedCaptureSession is created by providing a set of target output surfaces to
- * {@link CameraDevice#createConstrainedHighSpeedCaptureSession}, Once created, the session is
- * active until a new session is created by the camera device, or the camera device is closed.
+ * A CameraConstrainedHighSpeedCaptureSession is created by providing a session configuration to
+ * {@link CameraDevice#createCaptureSession(SessionConfiguration)} with a type of
+ * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_HIGH_SPEED}. The
+ * CameraCaptureSession returned from {@link CameraCaptureSession.StateCallback} can then be cast to
+ * a CameraConstrainedHighSpeedCaptureSession. Once created, the session is active until a new
+ * session is created by the camera device, or the camera device is closed.
* </p>
* <p>
* An active high speed capture session is a specialized capture session that is only targeted at
@@ -37,8 +40,8 @@
* accepts request lists created via {@link #createHighSpeedRequestList}, and the request list can
* only be submitted to this session via {@link CameraCaptureSession#captureBurst captureBurst}, or
* {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}. See
- * {@link CameraDevice#createConstrainedHighSpeedCaptureSession} for more details of the
- * limitations.
+ * {@link CameraDevice#createCaptureSession(android.hardware.camera2.params.SessionConfiguration)}
+ * for more details of the limitations.
* </p>
* <p>
* Creating a session is an expensive operation and can take several hundred milliseconds, since it
@@ -50,13 +53,6 @@
* completed, then the {@link CameraCaptureSession.StateCallback#onConfigureFailed} is called, and
* the session will not become active.
* </p>
- * <!--
- * <p>
- * Any capture requests (repeating or non-repeating) submitted before the session is ready will be
- * queued up and will begin capture once the session becomes ready. In case the session cannot be
- * configured and {@link CameraCaptureSession.StateCallback#onConfigureFailed onConfigureFailed} is
- * called, all queued capture requests are discarded. </p>
- * -->
* <p>
* If a new session is created by the camera device, then the previous session is closed, and its
* associated {@link CameraCaptureSession.StateCallback#onClosed onClosed} callback will be
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index fb1ece2..cc06681 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -16,24 +16,22 @@
package android.hardware.camera2;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import static android.hardware.camera2.ICameraDeviceUser.NORMAL_MODE;
-import static android.hardware.camera2.ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE;
import android.hardware.camera2.params.InputConfiguration;
-import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.Handler;
import android.view.Surface;
-import java.util.List;
-import java.util.Set;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Set;
/**
* <p>The CameraDevice class is a representation of a single camera connected to an
@@ -220,6 +218,224 @@
* <p>Create a new camera capture session by providing the target output set of Surfaces to the
* camera device.</p>
*
+ * @param outputs The new set of Surfaces that should be made available as
+ * targets for captured image data.
+ * @param callback The callback to notify about the status of the new capture session.
+ * @param handler The handler on which the callback should be invoked, or {@code null} to use
+ * the current thread's {@link android.os.Looper looper}.
+ *
+ * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
+ * the callback is null, or the handler is null but the current
+ * thread has no looper.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see CameraCaptureSession
+ * @see StreamConfigurationMap#getOutputFormats()
+ * @see StreamConfigurationMap#getOutputSizes(int)
+ * @see StreamConfigurationMap#getOutputSizes(Class)
+ * @deprecated Please use @{link
+ * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+ * full set of configuration options available.
+ */
+ @Deprecated
+ public abstract void createCaptureSession(@NonNull List<Surface> outputs,
+ @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
+ throws CameraAccessException;
+
+ /**
+ * <p>Create a new camera capture session by providing the target output set of Surfaces and
+ * its corresponding surface configuration to the camera device.</p>
+ *
+ * @see #createCaptureSession
+ * @see OutputConfiguration
+ * @deprecated Please use @{link
+ * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+ * full set of configuration options available.
+ */
+ @Deprecated
+ public abstract void createCaptureSessionByOutputConfigurations(
+ List<OutputConfiguration> outputConfigurations,
+ CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
+ throws CameraAccessException;
+ /**
+ * Create a new reprocessable camera capture session by providing the desired reprocessing
+ * input Surface configuration and the target output set of Surfaces to the camera device.
+ *
+ * @param inputConfig The configuration for the input {@link Surface}
+ * @param outputs The new set of Surfaces that should be made available as
+ * targets for captured image data.
+ * @param callback The callback to notify about the status of the new capture session.
+ * @param handler The handler on which the callback should be invoked, or {@code null} to use
+ * the current thread's {@link android.os.Looper looper}.
+ *
+ * @throws IllegalArgumentException if the input configuration is null or not supported, the set
+ * of output Surfaces do not meet the requirements, the
+ * callback is null, or the handler is null but the current
+ * thread has no looper.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see #createCaptureSession
+ * @see CameraCaptureSession
+ * @see StreamConfigurationMap#getInputFormats
+ * @see StreamConfigurationMap#getInputSizes
+ * @see StreamConfigurationMap#getValidOutputFormatsForInput
+ * @see StreamConfigurationMap#getOutputSizes
+ * @see android.media.ImageWriter
+ * @see android.media.ImageReader
+ * @deprecated Please use @{link
+ * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+ * full set of configuration options available.
+ */
+ @Deprecated
+ public abstract void createReprocessableCaptureSession(@NonNull InputConfiguration inputConfig,
+ @NonNull List<Surface> outputs, @NonNull CameraCaptureSession.StateCallback callback,
+ @Nullable Handler handler)
+ throws CameraAccessException;
+
+ /**
+ * Create a new reprocessable camera capture session by providing the desired reprocessing
+ * input configuration and output {@link OutputConfiguration}
+ * to the camera device.
+ *
+ * @see #createReprocessableCaptureSession
+ * @see OutputConfiguration
+ * @deprecated Please use @{link
+ * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+ * full set of configuration options available.
+ */
+ @Deprecated
+ public abstract void createReprocessableCaptureSessionByConfigurations(
+ @NonNull InputConfiguration inputConfig,
+ @NonNull List<OutputConfiguration> outputs,
+ @NonNull CameraCaptureSession.StateCallback callback,
+ @Nullable Handler handler)
+ throws CameraAccessException;
+
+ /**
+ * <p>Create a new constrained high speed capture session.</p>
+ *
+ * @param outputs The new set of Surfaces that should be made available as
+ * targets for captured high speed image data.
+ * @param callback The callback to notify about the status of the new capture session.
+ * @param handler The handler on which the callback should be invoked, or {@code null} to use
+ * the current thread's {@link android.os.Looper looper}.
+ *
+ * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
+ * the callback is null, or the handler is null but the current
+ * thread has no looper, or the camera device doesn't support
+ * high speed video capability.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see #createCaptureSession
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+ * @see StreamConfigurationMap#getHighSpeedVideoSizes
+ * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
+ * @see CameraCaptureSession#captureBurst
+ * @see CameraCaptureSession#setRepeatingBurst
+ * @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList
+ * @deprecated Please use @{link
+ * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+ * full set of configuration options available.
+ */
+ @Deprecated
+ public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List<Surface> outputs,
+ @NonNull CameraCaptureSession.StateCallback callback,
+ @Nullable Handler handler)
+ throws CameraAccessException;
+
+ /**
+ * Standard camera operation mode.
+ *
+ * @see #createCustomCaptureSession
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int SESSION_OPERATION_MODE_NORMAL =
+ 0; // ICameraDeviceUser.NORMAL_MODE;
+
+ /**
+ * Constrained high-speed operation mode.
+ *
+ * @see #createCustomCaptureSession
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED =
+ 1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE;
+
+ /**
+ * First vendor-specific operating mode
+ *
+ * @see #createCustomCaptureSession
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int SESSION_OPERATION_MODE_VENDOR_START =
+ 0x8000; // ICameraDeviceUser.VENDOR_MODE_START;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SESSION_OPERATION_MODE"}, value =
+ {SESSION_OPERATION_MODE_NORMAL,
+ SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED,
+ SESSION_OPERATION_MODE_VENDOR_START})
+ public @interface SessionOperatingMode {};
+
+ /**
+ * Create a new camera capture session with a custom operating mode.
+ *
+ * @param inputConfig The configuration for the input {@link Surface} if a reprocessing session
+ * is desired, or {@code null} otherwise.
+ * @param outputs The new set of {@link OutputConfiguration OutputConfigurations} that should be
+ * made available as targets for captured image data.
+ * @param operatingMode The custom operating mode to use; a nonnegative value, either a custom
+ * vendor value or one of the SESSION_OPERATION_MODE_* values.
+ * @param callback The callback to notify about the status of the new capture session.
+ * @param handler The handler on which the callback should be invoked, or {@code null} to use
+ * the current thread's {@link android.os.Looper looper}.
+ *
+ * @throws IllegalArgumentException if the input configuration is null or not supported, the set
+ * of output Surfaces do not meet the requirements, the
+ * callback is null, or the handler is null but the current
+ * thread has no looper.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see #createCaptureSession
+ * @see #createReprocessableCaptureSession
+ * @see CameraCaptureSession
+ * @see OutputConfiguration
+ * @deprecated Please use @{link
+ * #createCaptureSession(android.hardware.camera2.params.SessionConfiguration)} for the
+ * full set of configuration options available.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @Deprecated
+ public abstract void createCustomCaptureSession(
+ InputConfiguration inputConfig,
+ @NonNull List<OutputConfiguration> outputs,
+ @SessionOperatingMode int operatingMode,
+ @NonNull CameraCaptureSession.StateCallback callback,
+ @Nullable Handler handler)
+ throws CameraAccessException;
+
+ /**
+ * <p>Create a new {@link CameraCaptureSession} using a {@link SessionConfiguration} helper
+ * object that aggregates all supported parameters.</p>
* <p>The active capture session determines the set of potential output Surfaces for
* the camera device for each capture request. A given request may use all
* or only some of the outputs. Once the CameraCaptureSession is created, requests can be
@@ -308,11 +524,15 @@
* <p>Configuring a session with an empty or null list will close the current session, if
* any. This can be used to release the current session's target surfaces for another use.</p>
*
+ * <h3>Regular capture</h3>
+ *
* <p>While any of the sizes from {@link StreamConfigurationMap#getOutputSizes} can be used when
* a single output stream is configured, a given camera device may not be able to support all
* combination of sizes, formats, and targets when multiple outputs are configured at once. The
* tables below list the maximum guaranteed resolutions for combinations of streams and targets,
- * given the capabilities of the camera device.</p>
+ * given the capabilities of the camera device. These are valid for when the
+ * {@link android.hardware.camera2.params.SessionConfiguration#setInputConfiguration
+ * input configuration} is not set and therefore no reprocessing is active.</p>
*
* <p>If an application tries to create a session using a set of targets that exceed the limits
* described in the below tables, one of three possibilities may occur. First, the session may
@@ -488,56 +708,22 @@
* (either width or height) might not be supported, and capture session creation will fail if it
* is not.</p>
*
- * @param outputs The new set of Surfaces that should be made available as
- * targets for captured image data.
- * @param callback The callback to notify about the status of the new capture session.
- * @param handler The handler on which the callback should be invoked, or {@code null} to use
- * the current thread's {@link android.os.Looper looper}.
- *
- * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
- * the callback is null, or the handler is null but the current
- * thread has no looper.
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalStateException if the camera device has been closed
- *
- * @see CameraCaptureSession
- * @see StreamConfigurationMap#getOutputFormats()
- * @see StreamConfigurationMap#getOutputSizes(int)
- * @see StreamConfigurationMap#getOutputSizes(Class)
- */
- public abstract void createCaptureSession(@NonNull List<Surface> outputs,
- @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
- throws CameraAccessException;
-
- /**
- * <p>Create a new camera capture session by providing the target output set of Surfaces and
- * its corresponding surface configuration to the camera device.</p>
- *
- * @see #createCaptureSession
- * @see OutputConfiguration
- */
- public abstract void createCaptureSessionByOutputConfigurations(
- List<OutputConfiguration> outputConfigurations,
- CameraCaptureSession.StateCallback callback, @Nullable Handler handler)
- throws CameraAccessException;
- /**
- * Create a new reprocessable camera capture session by providing the desired reprocessing
- * input Surface configuration and the target output set of Surfaces to the camera device.
+ * <h3>Reprocessing</h3>
*
* <p>If a camera device supports YUV reprocessing
* ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING}) or PRIVATE
* reprocessing
- * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING}), besides
- * the capture session created via {@link #createCaptureSession createCaptureSession}, the
+ * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING}), the
* application can also create a reprocessable capture session to submit reprocess capture
- * requests in addition to regular capture requests. A reprocess capture request takes the next
- * available buffer from the session's input Surface, and sends it through the camera device's
- * processing pipeline again, to produce buffers for the request's target output Surfaces. No
- * new image data is captured for a reprocess request. However the input buffer provided by
- * the application must be captured previously by the same camera device in the same session
- * directly (e.g. for Zero-Shutter-Lag use case) or indirectly (e.g. combining multiple output
- * images).</p>
+ * requests in addition to regular capture requests, by setting an
+ * {@link android.hardware.camera2.params.SessionConfiguration#setInputConfiguration
+ * input configuration} for the session. A reprocess capture request takes the next available
+ * buffer from the
+ * session's input Surface, and sends it through the camera device's processing pipeline again,
+ * to produce buffers for the request's target output Surfaces. No new image data is captured
+ * for a reprocess request. However the input buffer provided by the application must be
+ * captured previously by the same camera device in the same session directly (e.g. for
+ * Zero-Shutter-Lag use case) or indirectly (e.g. combining multiple output images).</p>
*
* <p>The active reprocessable capture session determines an input {@link Surface} and the set
* of potential output Surfaces for the camera devices for each capture request. The application
@@ -570,10 +756,7 @@
* <p>Starting from API level 30, recreating a reprocessable capture session will flush all the
* queued but not yet processed buffers from the input surface.</p>
*
- * <p>The guaranteed stream configurations listed in
- * {@link #createCaptureSession createCaptureSession} are also guaranteed to work for
- * {@link #createReprocessableCaptureSession createReprocessableCaptureSession}. In addition,
- * the configurations in the tables below are also guaranteed for creating a reprocessable
+ * <p>The configurations in the tables below are guaranteed for creating a reprocessable
* capture session if the camera device supports YUV reprocessing or PRIVATE reprocessing.
* However, not all output targets used to create a reprocessable session may be used in a
* {@link CaptureRequest} simultaneously. For devices that support only 1 output target in a
@@ -602,7 +785,7 @@
* <p>LIMITED-level ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
* {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices
* support at least the following stream combinations for creating a reprocessable capture
- * session in addition to those listed in {@link #createCaptureSession createCaptureSession} for
+ * session in addition to those listed earlier for regular captures for
* {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices:
*
* <table>
@@ -671,74 +854,30 @@
* </table><br>
* </p>
*
- * <p>Clients can access the above mandatory stream combination tables via
- * {@link android.hardware.camera2.params.MandatoryStreamCombination}.</p>
+ * <h3>Constrained high-speed recording</h3>
*
- * @param inputConfig The configuration for the input {@link Surface}
- * @param outputs The new set of Surfaces that should be made available as
- * targets for captured image data.
- * @param callback The callback to notify about the status of the new capture session.
- * @param handler The handler on which the callback should be invoked, or {@code null} to use
- * the current thread's {@link android.os.Looper looper}.
- *
- * @throws IllegalArgumentException if the input configuration is null or not supported, the set
- * of output Surfaces do not meet the requirements, the
- * callback is null, or the handler is null but the current
- * thread has no looper.
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalStateException if the camera device has been closed
- *
- * @see #createCaptureSession
- * @see CameraCaptureSession
- * @see StreamConfigurationMap#getInputFormats
- * @see StreamConfigurationMap#getInputSizes
- * @see StreamConfigurationMap#getValidOutputFormatsForInput
- * @see StreamConfigurationMap#getOutputSizes
- * @see android.media.ImageWriter
- * @see android.media.ImageReader
- */
- public abstract void createReprocessableCaptureSession(@NonNull InputConfiguration inputConfig,
- @NonNull List<Surface> outputs, @NonNull CameraCaptureSession.StateCallback callback,
- @Nullable Handler handler)
- throws CameraAccessException;
-
- /**
- * Create a new reprocessable camera capture session by providing the desired reprocessing
- * input configuration and output {@link OutputConfiguration}
- * to the camera device.
- *
- * @see #createReprocessableCaptureSession
- * @see OutputConfiguration
- *
- */
- public abstract void createReprocessableCaptureSessionByConfigurations(
- @NonNull InputConfiguration inputConfig,
- @NonNull List<OutputConfiguration> outputs,
- @NonNull CameraCaptureSession.StateCallback callback,
- @Nullable Handler handler)
- throws CameraAccessException;
-
- /**
- * <p>Create a new constrained high speed capture session.</p>
- *
- * <p>The application can use normal capture session (created via {@link #createCaptureSession})
+ * <p>The application can use a
+ * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_REGULAR
+ * normal capture session}
* for high speed capture if the desired high speed FPS ranges are advertised by
* {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES}, in which case all API
* semantics associated with normal capture sessions applies.</p>
*
- * <p>The method creates a specialized capture session that is only targeted at high speed
- * video recording (>=120fps) use case if the camera device supports high speed video
- * capability (i.e., {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} contains
- * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO}).
- * Therefore, it has special characteristics compared with a normal capture session:</p>
+ * <p>A
+ * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_HIGH_SPEED
+ * high-speed capture session}
+ * can be use for high speed video recording (>=120fps) when the camera device supports high
+ * speed video capability (i.e., {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
+ * contains {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO}).
+ * A constrained high-speed capture session has special limitations compared with a normal
+ * capture session:</p>
*
* <ul>
*
- * <li>In addition to the output target Surface requirements specified by the
- * {@link #createCaptureSession} method, an active high speed capture session will support up
- * to 2 output Surfaces, though the application might choose to configure just one Surface
- * (e.g., preview only). All Surfaces must be either video encoder surfaces (acquired by
+ * <li>In addition to the output target Surface requirements specified above for regular
+ * captures, a high speed capture session will only support up to 2 output Surfaces, though
+ * the application might choose to configure just one Surface (e.g., preview only). All
+ * Surfaces must be either video encoder surfaces (acquired by
* {@link android.media.MediaRecorder#getSurface} or
* {@link android.media.MediaCodec#createInputSurface}) or preview surfaces (obtained from
* {@link android.view.SurfaceView}, {@link android.graphics.SurfaceTexture} via
@@ -774,116 +913,6 @@
*
* </ul>
*
- * @param outputs The new set of Surfaces that should be made available as
- * targets for captured high speed image data.
- * @param callback The callback to notify about the status of the new capture session.
- * @param handler The handler on which the callback should be invoked, or {@code null} to use
- * the current thread's {@link android.os.Looper looper}.
- *
- * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
- * the callback is null, or the handler is null but the current
- * thread has no looper, or the camera device doesn't support
- * high speed video capability.
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalStateException if the camera device has been closed
- *
- * @see #createCaptureSession
- * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
- * @see StreamConfigurationMap#getHighSpeedVideoSizes
- * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
- * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
- * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
- * @see CameraCaptureSession#captureBurst
- * @see CameraCaptureSession#setRepeatingBurst
- * @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList
- */
- public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List<Surface> outputs,
- @NonNull CameraCaptureSession.StateCallback callback,
- @Nullable Handler handler)
- throws CameraAccessException;
-
- /**
- * Standard camera operation mode.
- *
- * @see #createCustomCaptureSession
- * @hide
- */
- @SystemApi
- @TestApi
- public static final int SESSION_OPERATION_MODE_NORMAL =
- 0; // ICameraDeviceUser.NORMAL_MODE;
-
- /**
- * Constrained high-speed operation mode.
- *
- * @see #createCustomCaptureSession
- * @hide
- */
- @SystemApi
- @TestApi
- public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED =
- 1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE;
-
- /**
- * First vendor-specific operating mode
- *
- * @see #createCustomCaptureSession
- * @hide
- */
- @SystemApi
- @TestApi
- public static final int SESSION_OPERATION_MODE_VENDOR_START =
- 0x8000; // ICameraDeviceUser.VENDOR_MODE_START;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"SESSION_OPERATION_MODE"}, value =
- {SESSION_OPERATION_MODE_NORMAL,
- SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED,
- SESSION_OPERATION_MODE_VENDOR_START})
- public @interface SessionOperatingMode {};
-
- /**
- * Create a new camera capture session with a custom operating mode.
- *
- * @param inputConfig The configuration for the input {@link Surface} if a reprocessing session
- * is desired, or {@code null} otherwise.
- * @param outputs The new set of {@link OutputConfiguration OutputConfigurations} that should be
- * made available as targets for captured image data.
- * @param operatingMode The custom operating mode to use; a nonnegative value, either a custom
- * vendor value or one of the SESSION_OPERATION_MODE_* values.
- * @param callback The callback to notify about the status of the new capture session.
- * @param handler The handler on which the callback should be invoked, or {@code null} to use
- * the current thread's {@link android.os.Looper looper}.
- *
- * @throws IllegalArgumentException if the input configuration is null or not supported, the set
- * of output Surfaces do not meet the requirements, the
- * callback is null, or the handler is null but the current
- * thread has no looper.
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalStateException if the camera device has been closed
- *
- * @see #createCaptureSession
- * @see #createReprocessableCaptureSession
- * @see CameraCaptureSession
- * @see OutputConfiguration
- * @hide
- */
- @SystemApi
- @TestApi
- public abstract void createCustomCaptureSession(
- InputConfiguration inputConfig,
- @NonNull List<OutputConfiguration> outputs,
- @SessionOperatingMode int operatingMode,
- @NonNull CameraCaptureSession.StateCallback callback,
- @Nullable Handler handler)
- throws CameraAccessException;
-
- /**
- * <p>Create a new {@link CameraCaptureSession} using a {@link SessionConfiguration} helper
- * object that aggregates all supported parameters.</p>
*
* @param config A session configuration (see {@link SessionConfiguration}).
*
@@ -997,7 +1026,7 @@
*
* @see CaptureRequest.Builder
* @see TotalCaptureResult
- * @see CameraDevice#createReprocessableCaptureSession
+ * @see CameraDevice#createCaptureSession(android.hardware.camera2.params.SessionConfiguration)
* @see android.media.ImageWriter
*/
@NonNull
@@ -1028,7 +1057,8 @@
* <p>This method performs a runtime check of a given {@link SessionConfiguration}. The result
* confirms whether or not the passed session configuration can be successfully used to
* create a camera capture session using
- * {@link CameraDevice#createCaptureSession(SessionConfiguration)}.
+ * {@link CameraDevice#createCaptureSession(
+ * android.hardware.camera2.params.SessionConfiguration)}.
* </p>
*
* <p>The method can be called at any point before, during and after active capture session.
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 8e0a46d..ec13a36 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1121,12 +1121,16 @@
//
/**
- * <p>Timestamps from {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} are in nanoseconds and monotonic,
- * but can not be compared to timestamps from other subsystems
- * (e.g. accelerometer, gyro etc.), or other instances of the same or different
- * camera devices in the same system. Timestamps between streams and results for
- * a single camera instance are comparable, and the timestamps for all buffers
- * and the result metadata generated by a single capture are identical.</p>
+ * <p>Timestamps from {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} are in nanoseconds and monotonic, but can
+ * not be compared to timestamps from other subsystems (e.g. accelerometer, gyro etc.),
+ * or other instances of the same or different camera devices in the same system with
+ * accuracy. However, the timestamps are roughly in the same timebase as
+ * {@link android.os.SystemClock#uptimeMillis }. The accuracy is sufficient for tasks
+ * like A/V synchronization for video recording, at least, and the timestamps can be
+ * directly used together with timestamps from the audio subsystem for that task.</p>
+ * <p>Timestamps between streams and results for a single camera instance are comparable,
+ * and the timestamps for all buffers and the result metadata generated by a single
+ * capture are identical.</p>
*
* @see CaptureResult#SENSOR_TIMESTAMP
* @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
@@ -1137,6 +1141,14 @@
* <p>Timestamps from {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} are in the same timebase as
* {@link android.os.SystemClock#elapsedRealtimeNanos },
* and they can be compared to other timestamps using that base.</p>
+ * <p>When buffers from a REALTIME device are passed directly to a video encoder from the
+ * camera, automatic compensation is done to account for differing timebases of the
+ * audio and camera subsystems. If the application is receiving buffers and then later
+ * sending them to a video encoder or other application where they are compared with
+ * audio subsystem timestamps or similar, this compensation is not present. In those
+ * cases, applications need to adjust the timestamps themselves. Since {@link android.os.SystemClock#elapsedRealtimeNanos } and {@link android.os.SystemClock#uptimeMillis } only diverge while the device is asleep, an
+ * offset between the two sources can be measured once per active session and applied
+ * to timestamps for sufficient accuracy for A/V sync.</p>
*
* @see CaptureResult#SENSOR_TIMESTAMP
* @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 555ff9a..47a897c 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -17,10 +17,11 @@
package android.hardware.camera2.params;
+import static com.android.internal.util.Preconditions.*;
+
import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
@@ -32,14 +33,12 @@
import android.os.Parcelable;
import android.util.Log;
-import java.util.Collections;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.concurrent.Executor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-
-import static com.android.internal.util.Preconditions.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executor;
/**
* A helper class that aggregates all supported arguments for capture session initialization.
@@ -61,6 +60,12 @@
* A high speed session type that can only contain instances of {@link OutputConfiguration}.
* The outputs can run using high speed FPS ranges. Calls to {@link #setInputConfiguration}
* are not supported.
+ * <p>
+ * When using this type, the CameraCaptureSession returned by
+ * {@link android.hardware.camera2.CameraCaptureSession.StateCallback} can be cast to a
+ * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession} to access the extra
+ * methods for constrained high speed recording.
+ * </p>
*
* @see CameraDevice#createConstrainedHighSpeedCaptureSession
*/
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 60e466e..456ebf2 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -303,16 +303,20 @@
@NonNull
public final UUID vendorUuid;
+ /** vendor specific version number of the model */
+ public final int version;
+
/** Opaque data. For use by vendor implementation and enrollment application */
@UnsupportedAppUsage
@NonNull
public final byte[] data;
public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, int type,
- @Nullable byte[] data) {
+ @Nullable byte[] data, int version) {
this.uuid = requireNonNull(uuid);
this.vendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0);
this.type = type;
+ this.version = version;
this.data = data != null ? data : new byte[0];
}
@@ -320,6 +324,7 @@
public int hashCode() {
final int prime = 31;
int result = 1;
+ result = prime * result + version;
result = prime * result + Arrays.hashCode(data);
result = prime * result + type;
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
@@ -350,6 +355,8 @@
return false;
if (!Arrays.equals(data, other.data))
return false;
+ if (version != other.version)
+ return false;
return true;
}
}
@@ -499,14 +506,19 @@
@NonNull
public final Keyphrase[] keyphrases; // keyword phrases in model
- @UnsupportedAppUsage
public KeyphraseSoundModel(
@NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data,
- @Nullable Keyphrase[] keyphrases) {
- super(uuid, vendorUuid, TYPE_KEYPHRASE, data);
+ @Nullable Keyphrase[] keyphrases, int version) {
+ super(uuid, vendorUuid, TYPE_KEYPHRASE, data, version);
this.keyphrases = keyphrases != null ? keyphrases : new Keyphrase[0];
}
+ @UnsupportedAppUsage
+ public KeyphraseSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
+ @Nullable byte[] data, @Nullable Keyphrase[] keyphrases) {
+ this(uuid, vendorUuid, data, keyphrases, -1);
+ }
+
public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR
= new Parcelable.Creator<KeyphraseSoundModel>() {
public KeyphraseSoundModel createFromParcel(Parcel in) {
@@ -525,9 +537,10 @@
if (length >= 0) {
vendorUuid = UUID.fromString(in.readString());
}
+ int version = in.readInt();
byte[] data = in.readBlob();
Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR);
- return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases);
+ return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases, version);
}
@Override
@@ -544,6 +557,7 @@
dest.writeInt(vendorUuid.toString().length());
dest.writeString(vendorUuid.toString());
}
+ dest.writeInt(version);
dest.writeBlob(data);
dest.writeTypedArray(keyphrases, flags);
}
@@ -552,7 +566,9 @@
public String toString() {
return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases)
+ ", uuid=" + uuid + ", vendorUuid=" + vendorUuid
- + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]";
+ + ", type=" + type
+ + ", data=" + (data == null ? 0 : data.length)
+ + ", version=" + version + "]";
}
@Override
@@ -598,10 +614,15 @@
}
};
+ public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
+ @Nullable byte[] data, int version) {
+ super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data, version);
+ }
+
@UnsupportedAppUsage
public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
@Nullable byte[] data) {
- super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data);
+ this(uuid, vendorUuid, data, -1);
}
@Override
@@ -617,7 +638,8 @@
vendorUuid = UUID.fromString(in.readString());
}
byte[] data = in.readBlob();
- return new GenericSoundModel(uuid, vendorUuid, data);
+ int version = in.readInt();
+ return new GenericSoundModel(uuid, vendorUuid, data, version);
}
@Override
@@ -630,12 +652,15 @@
dest.writeString(vendorUuid.toString());
}
dest.writeBlob(data);
+ dest.writeInt(version);
}
@Override
public String toString() {
return "GenericSoundModel [uuid=" + uuid + ", vendorUuid=" + vendorUuid
- + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]";
+ + ", type=" + type
+ + ", data=" + (data == null ? 0 : data.length)
+ + ", version=" + version + "]";
}
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 7291419..03d29a3 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -370,6 +370,12 @@
}
@Override
+ public synchronized void onModuleDied() {
+ Message m = mHandler.obtainMessage(EVENT_SERVICE_DIED);
+ mHandler.sendMessage(m);
+ }
+
+ @Override
public synchronized void binderDied() {
Message m = mHandler.obtainMessage(EVENT_SERVICE_DIED);
mHandler.sendMessage(m);
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index a66fcae..fb35b4b 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -60,6 +60,18 @@
@SystemApi
@TestApi
public static final int APP_RETURN_WANTED_AS_IS = 2;
+ /** Event offset of request codes from captive portal application. */
+ private static final int APP_REQUEST_BASE = 100;
+ /**
+ * Request code from the captive portal application, indicating that the network condition may
+ * have changed and the network should be re-validated.
+ * @see ICaptivePortal#appRequest(int)
+ * @see android.net.INetworkMonitor#forceReevaluation(int)
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int APP_REQUEST_REEVALUATION_REQUIRED = APP_REQUEST_BASE + 0;
private final IBinder mBinder;
@@ -136,6 +148,19 @@
}
/**
+ * Request that the system reevaluates the captive portal status.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public void reevaluateNetwork() {
+ try {
+ ICaptivePortal.Stub.asInterface(mBinder).appRequest(APP_REQUEST_REEVALUATION_REQUIRED);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Log a captive portal login event.
* @param eventId one of the CAPTIVE_PORTAL_LOGIN_* constants in metrics_constants.proto.
* @param packageName captive portal application package name.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 03d4200..46559ec 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -517,7 +517,7 @@
* The absence of a connection type.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
+ @SystemApi
public static final int TYPE_NONE = -1;
/**
@@ -3121,8 +3121,6 @@
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
public int registerNetworkProvider(@NonNull NetworkProvider provider) {
if (provider.getProviderId() != NetworkProvider.ID_NONE) {
- // TODO: Provide a better method for checking this by moving NetworkFactory.SerialNumber
- // out of NetworkFactory.
throw new IllegalStateException("NetworkProviders can only be registered once");
}
@@ -3171,26 +3169,24 @@
/**
* @hide
* Register a NetworkAgent with ConnectivityService.
- * @return NetID corresponding to NetworkAgent.
+ * @return Network corresponding to NetworkAgent.
*/
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
- public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
- NetworkCapabilities nc, int score, NetworkMisc misc) {
- return registerNetworkAgent(messenger, ni, lp, nc, score, misc,
- NetworkFactory.SerialNumber.NONE);
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
+ NetworkCapabilities nc, int score, NetworkAgentConfig config) {
+ return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
}
/**
* @hide
* Register a NetworkAgent with ConnectivityService.
- * @return NetID corresponding to NetworkAgent.
+ * @return Network corresponding to NetworkAgent.
*/
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
- public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
- NetworkCapabilities nc, int score, NetworkMisc misc, int factorySerialNumber) {
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
+ NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
try {
- return mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc,
- factorySerialNumber);
+ return mService.registerNetworkAgent(messenger, ni, lp, nc, score, config, providerId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3626,14 +3622,26 @@
/**
* Helper function to request a network with a particular legacy type.
*
- * This is temporarily public @hide so it can be called by system code that uses the
- * NetworkRequest API to request networks but relies on CONNECTIVITY_ACTION broadcasts for
- * instead network notifications.
+ * @deprecated This is temporarily public for tethering to backwards compatibility that uses
+ * the NetworkRequest API to request networks with legacy type and relies on
+ * CONNECTIVITY_ACTION broadcasts instead of NetworkCallbacks. New caller should use
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback, Handler)} instead.
*
* TODO: update said system code to rely on NetworkCallbacks and make this method private.
+
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
+ * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
+ * before {@link NetworkCallback#onUnavailable()} is called. The timeout must
+ * be a positive value (i.e. >0).
+ * @param legacyType to specify the network type(#TYPE_*).
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
*
* @hide
*/
+ @SystemApi
+ @Deprecated
public void requestNetwork(@NonNull NetworkRequest request,
@NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType,
@NonNull Handler handler) {
diff --git a/core/java/android/net/ICaptivePortal.aidl b/core/java/android/net/ICaptivePortal.aidl
index 707b4f6..fe21905 100644
--- a/core/java/android/net/ICaptivePortal.aidl
+++ b/core/java/android/net/ICaptivePortal.aidl
@@ -21,6 +21,7 @@
* @hide
*/
oneway interface ICaptivePortal {
+ void appRequest(int request);
void appResponse(int response);
void logEvent(int eventId, String packageName);
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index e6a0379..186196bd 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -20,9 +20,9 @@
import android.net.ConnectionInfo;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
@@ -152,8 +152,9 @@
void declareNetworkRequestUnfulfillable(in NetworkRequest request);
- int registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
- in NetworkCapabilities nc, int score, in NetworkMisc misc, in int factorySerialNumber);
+ Network registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
+ in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+ in int factorySerialNumber);
NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
in Messenger messenger, int timeoutSec, in IBinder binder, int legacy);
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 9994f9f..5fa515a 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -23,6 +23,8 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.netstats.provider.INetworkStatsProvider;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.IBinder;
import android.os.Messenger;
import com.android.internal.net.VpnInfo;
@@ -89,4 +91,7 @@
/** Get the total network stats information since boot */
long getTotalStats(int type);
+ /** Registers a network stats provider */
+ INetworkStatsProviderCallback registerNetworkStatsProvider(String tag,
+ in INetworkStatsProvider provider);
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 2792c56..be8e561 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -21,6 +21,8 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.LinkPropertiesUtils;
+import android.net.util.LinkPropertiesUtils.CompareResult;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -84,36 +86,6 @@
/**
* @hide
*/
- public static class CompareResult<T> {
- public final List<T> removed = new ArrayList<>();
- public final List<T> added = new ArrayList<>();
-
- public CompareResult() {}
-
- public CompareResult(Collection<T> oldItems, Collection<T> newItems) {
- if (oldItems != null) {
- removed.addAll(oldItems);
- }
- if (newItems != null) {
- for (T newItem : newItems) {
- if (!removed.remove(newItem)) {
- added.add(newItem);
- }
- }
- }
- }
-
- @Override
- public String toString() {
- return "removed=[" + TextUtils.join(",", removed)
- + "] added=[" + TextUtils.join(",", added)
- + "]";
- }
- }
-
- /**
- * @hide
- */
@UnsupportedAppUsage(implicitMember =
"values()[Landroid/net/LinkProperties$ProvisioningChange;")
public enum ProvisioningChange {
@@ -1295,7 +1267,7 @@
*/
@UnsupportedAppUsage
public boolean isIdenticalInterfaceName(@NonNull LinkProperties target) {
- return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
+ return LinkPropertiesUtils.isIdenticalInterfaceName(target, this);
}
/**
@@ -1318,10 +1290,7 @@
*/
@UnsupportedAppUsage
public boolean isIdenticalAddresses(@NonNull LinkProperties target) {
- Collection<InetAddress> targetAddresses = target.getAddresses();
- Collection<InetAddress> sourceAddresses = getAddresses();
- return (sourceAddresses.size() == targetAddresses.size()) ?
- sourceAddresses.containsAll(targetAddresses) : false;
+ return LinkPropertiesUtils.isIdenticalAddresses(target, this);
}
/**
@@ -1333,15 +1302,7 @@
*/
@UnsupportedAppUsage
public boolean isIdenticalDnses(@NonNull LinkProperties target) {
- Collection<InetAddress> targetDnses = target.getDnsServers();
- String targetDomains = target.getDomains();
- if (mDomains == null) {
- if (targetDomains != null) return false;
- } else {
- if (!mDomains.equals(targetDomains)) return false;
- }
- return (mDnses.size() == targetDnses.size()) ?
- mDnses.containsAll(targetDnses) : false;
+ return LinkPropertiesUtils.isIdenticalDnses(target, this);
}
/**
@@ -1394,9 +1355,7 @@
*/
@UnsupportedAppUsage
public boolean isIdenticalRoutes(@NonNull LinkProperties target) {
- Collection<RouteInfo> targetRoutes = target.getRoutes();
- return (mRoutes.size() == targetRoutes.size()) ?
- mRoutes.containsAll(targetRoutes) : false;
+ return LinkPropertiesUtils.isIdenticalRoutes(target, this);
}
/**
@@ -1408,8 +1367,7 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean isIdenticalHttpProxy(@NonNull LinkProperties target) {
- return getHttpProxy() == null ? target.getHttpProxy() == null :
- getHttpProxy().equals(target.getHttpProxy());
+ return LinkPropertiesUtils.isIdenticalHttpProxy(target, this);
}
/**
@@ -1541,26 +1499,6 @@
}
/**
- * Compares the addresses in this LinkProperties with another
- * LinkProperties, examining only addresses on the base link.
- *
- * @param target a LinkProperties with the new list of addresses
- * @return the differences between the addresses.
- * @hide
- */
- public @NonNull CompareResult<LinkAddress> compareAddresses(@Nullable LinkProperties target) {
- /*
- * Duplicate the LinkAddresses into removed, we will be removing
- * address which are common between mLinkAddresses and target
- * leaving the addresses that are different. And address which
- * are in target but not in mLinkAddresses are placed in the
- * addedAddresses.
- */
- return new CompareResult<>(mLinkAddresses,
- target != null ? target.getLinkAddresses() : null);
- }
-
- /**
* Compares the DNS addresses in this LinkProperties with another
* LinkProperties, examining only DNS addresses on the base link.
*
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 74c9aac..0e10c42 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -20,11 +20,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.MacAddressUtils;
import android.net.wifi.WifiInfo;
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.util.BitUtils;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -33,7 +33,6 @@
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.Arrays;
-import java.util.Random;
/**
* Representation of a MAC address.
@@ -109,21 +108,13 @@
if (equals(BROADCAST_ADDRESS)) {
return TYPE_BROADCAST;
}
- if (isMulticastAddress()) {
+ if ((mAddr & MULTICAST_MASK) != 0) {
return TYPE_MULTICAST;
}
return TYPE_UNICAST;
}
/**
- * @return true if this MacAddress is a multicast address.
- * @hide
- */
- public boolean isMulticastAddress() {
- return (mAddr & MULTICAST_MASK) != 0;
- }
-
- /**
* @return true if this MacAddress is a locally assigned address.
*/
public boolean isLocallyAssigned() {
@@ -192,7 +183,7 @@
* @hide
*/
public static boolean isMacAddress(byte[] addr) {
- return addr != null && addr.length == ETHER_ADDR_LEN;
+ return MacAddressUtils.isMacAddress(addr);
}
/**
@@ -261,26 +252,11 @@
}
private static byte[] byteAddrFromLongAddr(long addr) {
- byte[] bytes = new byte[ETHER_ADDR_LEN];
- int index = ETHER_ADDR_LEN;
- while (index-- > 0) {
- bytes[index] = (byte) addr;
- addr = addr >> 8;
- }
- return bytes;
+ return MacAddressUtils.byteAddrFromLongAddr(addr);
}
private static long longAddrFromByteAddr(byte[] addr) {
- Preconditions.checkNotNull(addr);
- if (!isMacAddress(addr)) {
- throw new IllegalArgumentException(
- Arrays.toString(addr) + " was not a valid MAC address");
- }
- long longAddr = 0;
- for (byte b : addr) {
- longAddr = (longAddr << 8) + BitUtils.uint8(b);
- }
- return longAddr;
+ return MacAddressUtils.longAddrFromByteAddr(addr);
}
// Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr))
@@ -350,50 +326,7 @@
* @hide
*/
public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() {
- return createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
- }
-
- /**
- * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
- * unicast bit, are randomly selected.
- *
- * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
- *
- * @return a random locally assigned, unicast MacAddress.
- *
- * @hide
- */
- public static @NonNull MacAddress createRandomUnicastAddress() {
- return createRandomUnicastAddress(null, new SecureRandom());
- }
-
- /**
- * Returns a randomly generated MAC address using the given Random object and the same
- * OUI values as the given MacAddress.
- *
- * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
- *
- * @param base a base MacAddress whose OUI is used for generating the random address.
- * If base == null then the OUI will also be randomized.
- * @param r a standard Java Random object used for generating the random address.
- * @return a random locally assigned MacAddress.
- *
- * @hide
- */
- public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
- long addr;
- if (base == null) {
- addr = r.nextLong() & VALID_LONG_MASK;
- } else {
- addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
- }
- addr |= LOCALLY_ASSIGNED_MASK;
- addr &= ~MULTICAST_MASK;
- MacAddress mac = new MacAddress(addr);
- if (mac.equals(DEFAULT_MAC_ADDRESS)) {
- return createRandomUnicastAddress(base, r);
- }
- return mac;
+ return MacAddressUtils.createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
}
// Convenience function for working around the lack of byte literals.
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java
index 3fb52f1..bd39c13 100644
--- a/core/java/android/net/NattKeepalivePacketData.java
+++ b/core/java/android/net/NattKeepalivePacketData.java
@@ -16,9 +16,11 @@
package android.net;
-import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
-import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
+import static android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS;
+import static android.net.InvalidPacketException.ERROR_INVALID_PORT;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.net.util.IpUtils;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,20 +32,22 @@
import java.nio.ByteOrder;
/** @hide */
+@SystemApi
public final class NattKeepalivePacketData extends KeepalivePacketData implements Parcelable {
private static final int IPV4_HEADER_LENGTH = 20;
private static final int UDP_HEADER_LENGTH = 8;
// This should only be constructed via static factory methods, such as
// nattKeepalivePacket
- private NattKeepalivePacketData(InetAddress srcAddress, int srcPort,
- InetAddress dstAddress, int dstPort, byte[] data) throws
+ public NattKeepalivePacketData(@NonNull InetAddress srcAddress, int srcPort,
+ @NonNull InetAddress dstAddress, int dstPort, @NonNull byte[] data) throws
InvalidPacketException {
super(srcAddress, srcPort, dstAddress, dstPort, data);
}
/**
* Factory method to create Nat-T keepalive packet structure.
+ * @hide
*/
public static NattKeepalivePacketData nattKeepalivePacket(
InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
@@ -87,7 +91,7 @@
}
/** Write to parcel */
- public void writeToParcel(Parcel out, int flags) {
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeString(srcAddress.getHostAddress());
out.writeString(dstAddress.getHostAddress());
out.writeInt(srcPort);
@@ -95,7 +99,7 @@
}
/** Parcelable Creator */
- public static final Parcelable.Creator<NattKeepalivePacketData> CREATOR =
+ public static final @NonNull Parcelable.Creator<NattKeepalivePacketData> CREATOR =
new Parcelable.Creator<NattKeepalivePacketData>() {
public NattKeepalivePacketData createFromParcel(Parcel in) {
final InetAddress srcAddress =
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 60bd573..d286204 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -43,11 +43,13 @@
*
* @hide
*/
-public abstract class NetworkAgent extends Handler {
- // Guaranteed to be valid (not NETID_UNSET), otherwise registerNetworkAgent() would have thrown
- // an exception.
- public final int netId;
+public abstract class NetworkAgent {
+ // Guaranteed to be non-null, otherwise registerNetworkAgent() would have thrown
+ // an exception. Be careful in tests when mocking though.
+ @NonNull
+ public final Network network;
+ private final Handler mHandler;
private volatile AsyncChannel mAsyncChannel;
private final String LOG_TAG;
private static final boolean DBG = true;
@@ -58,7 +60,7 @@
private static final long BW_REFRESH_MIN_WIN_MS = 500;
private boolean mPollLceScheduled = false;
private AtomicBoolean mPollLcePending = new AtomicBoolean(false);
- public final int mFactorySerialNumber;
+ public final int mProviderId;
private static final int BASE = Protocol.BASE_NETWORK_AGENT;
@@ -219,25 +221,25 @@
// the entire tree.
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, NetworkFactory.SerialNumber.NONE);
+ this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
}
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
- NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
- this(looper, context, logTag, ni, nc, lp, score, misc, NetworkFactory.SerialNumber.NONE);
+ NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
+ this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
}
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
- NetworkCapabilities nc, LinkProperties lp, int score, int factorySerialNumber) {
- this(looper, context, logTag, ni, nc, lp, score, null, factorySerialNumber);
+ NetworkCapabilities nc, LinkProperties lp, int score, int providerId) {
+ this(looper, context, logTag, ni, nc, lp, score, null, providerId);
}
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
- NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc,
- int factorySerialNumber) {
- super(looper);
+ NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
+ int providerId) {
+ mHandler = new NetworkAgentHandler(looper);
LOG_TAG = logTag;
mContext = context;
- mFactorySerialNumber = factorySerialNumber;
+ mProviderId = providerId;
if (ni == null || nc == null || lp == null) {
throw new IllegalArgumentException();
}
@@ -245,117 +247,124 @@
if (VDBG) log("Registering NetworkAgent");
ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
- netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
- new LinkProperties(lp), new NetworkCapabilities(nc), score, misc,
- factorySerialNumber);
+ network = cm.registerNetworkAgent(new Messenger(mHandler), new NetworkInfo(ni),
+ new LinkProperties(lp), new NetworkCapabilities(nc), score, config,
+ providerId);
}
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
- if (mAsyncChannel != null) {
- log("Received new connection while already connected!");
- } else {
- if (VDBG) log("NetworkAgent fully connected");
- AsyncChannel ac = new AsyncChannel();
- ac.connected(null, this, msg.replyTo);
- ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
- AsyncChannel.STATUS_SUCCESSFUL);
- synchronized (mPreConnectedQueue) {
- mAsyncChannel = ac;
- for (Message m : mPreConnectedQueue) {
- ac.sendMessage(m);
- }
- mPreConnectedQueue.clear();
- }
- }
- break;
- }
- case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
- if (VDBG) log("CMD_CHANNEL_DISCONNECT");
- if (mAsyncChannel != null) mAsyncChannel.disconnect();
- break;
- }
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
- if (DBG) log("NetworkAgent channel lost");
- // let the client know CS is done with us.
- unwanted();
- synchronized (mPreConnectedQueue) {
- mAsyncChannel = null;
- }
- break;
- }
- case CMD_SUSPECT_BAD: {
- log("Unhandled Message " + msg);
- break;
- }
- case CMD_REQUEST_BANDWIDTH_UPDATE: {
- long currentTimeMs = System.currentTimeMillis();
- if (VDBG) {
- log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
- }
- if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
- mPollLceScheduled = false;
- if (mPollLcePending.getAndSet(true) == false) {
- pollLceData();
- }
- } else {
- // deliver the request at a later time rather than discard it completely.
- if (!mPollLceScheduled) {
- long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS -
- currentTimeMs + 1;
- mPollLceScheduled = sendEmptyMessageDelayed(
- CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
- }
- }
- break;
- }
- case CMD_REPORT_NETWORK_STATUS: {
- String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY);
- if (VDBG) {
- log("CMD_REPORT_NETWORK_STATUS(" +
- (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl);
- }
- networkStatus(msg.arg1, redirectUrl);
- break;
- }
- case CMD_SAVE_ACCEPT_UNVALIDATED: {
- saveAcceptUnvalidated(msg.arg1 != 0);
- break;
- }
- case CMD_START_SOCKET_KEEPALIVE: {
- startSocketKeepalive(msg);
- break;
- }
- case CMD_STOP_SOCKET_KEEPALIVE: {
- stopSocketKeepalive(msg);
- break;
- }
+ private class NetworkAgentHandler extends Handler {
+ NetworkAgentHandler(Looper looper) {
+ super(looper);
+ }
- case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
- ArrayList<Integer> thresholds =
- ((Bundle) msg.obj).getIntegerArrayList("thresholds");
- // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
- // rather than convert to int[].
- int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
- for (int i = 0; i < intThresholds.length; i++) {
- intThresholds[i] = thresholds.get(i);
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+ if (mAsyncChannel != null) {
+ log("Received new connection while already connected!");
+ } else {
+ if (VDBG) log("NetworkAgent fully connected");
+ AsyncChannel ac = new AsyncChannel();
+ ac.connected(null, this, msg.replyTo);
+ ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_SUCCESSFUL);
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = ac;
+ for (Message m : mPreConnectedQueue) {
+ ac.sendMessage(m);
+ }
+ mPreConnectedQueue.clear();
+ }
+ }
+ break;
}
- setSignalStrengthThresholds(intThresholds);
- break;
- }
- case CMD_PREVENT_AUTOMATIC_RECONNECT: {
- preventAutomaticReconnect();
- break;
- }
- case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
- addKeepalivePacketFilter(msg);
- break;
- }
- case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
- removeKeepalivePacketFilter(msg);
- break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+ if (VDBG) log("CMD_CHANNEL_DISCONNECT");
+ if (mAsyncChannel != null) mAsyncChannel.disconnect();
+ break;
+ }
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+ if (DBG) log("NetworkAgent channel lost");
+ // let the client know CS is done with us.
+ unwanted();
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = null;
+ }
+ break;
+ }
+ case CMD_SUSPECT_BAD: {
+ log("Unhandled Message " + msg);
+ break;
+ }
+ case CMD_REQUEST_BANDWIDTH_UPDATE: {
+ long currentTimeMs = System.currentTimeMillis();
+ if (VDBG) {
+ log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
+ }
+ if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
+ mPollLceScheduled = false;
+ if (!mPollLcePending.getAndSet(true)) {
+ pollLceData();
+ }
+ } else {
+ // deliver the request at a later time rather than discard it completely.
+ if (!mPollLceScheduled) {
+ long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS
+ - currentTimeMs + 1;
+ mPollLceScheduled = sendEmptyMessageDelayed(
+ CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
+ }
+ }
+ break;
+ }
+ case CMD_REPORT_NETWORK_STATUS: {
+ String redirectUrl = ((Bundle) msg.obj).getString(REDIRECT_URL_KEY);
+ if (VDBG) {
+ log("CMD_REPORT_NETWORK_STATUS("
+ + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
+ + redirectUrl);
+ }
+ networkStatus(msg.arg1, redirectUrl);
+ break;
+ }
+ case CMD_SAVE_ACCEPT_UNVALIDATED: {
+ saveAcceptUnvalidated(msg.arg1 != 0);
+ break;
+ }
+ case CMD_START_SOCKET_KEEPALIVE: {
+ startSocketKeepalive(msg);
+ break;
+ }
+ case CMD_STOP_SOCKET_KEEPALIVE: {
+ stopSocketKeepalive(msg);
+ break;
+ }
+
+ case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
+ ArrayList<Integer> thresholds =
+ ((Bundle) msg.obj).getIntegerArrayList("thresholds");
+ // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
+ // rather than convert to int[].
+ int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
+ for (int i = 0; i < intThresholds.length; i++) {
+ intThresholds[i] = thresholds.get(i);
+ }
+ setSignalStrengthThresholds(intThresholds);
+ break;
+ }
+ case CMD_PREVENT_AUTOMATIC_RECONNECT: {
+ preventAutomaticReconnect();
+ break;
+ }
+ case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
+ addKeepalivePacketFilter(msg);
+ break;
+ }
+ case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
+ removeKeepalivePacketFilter(msg);
+ break;
+ }
}
}
}
diff --git a/core/java/android/net/NetworkMisc.aidl b/core/java/android/net/NetworkAgentConfig.aidl
similarity index 95%
rename from core/java/android/net/NetworkMisc.aidl
rename to core/java/android/net/NetworkAgentConfig.aidl
index c65583f..cb70bdd 100644
--- a/core/java/android/net/NetworkMisc.aidl
+++ b/core/java/android/net/NetworkAgentConfig.aidl
@@ -16,4 +16,4 @@
package android.net;
-parcelable NetworkMisc;
+parcelable NetworkAgentConfig;
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
new file mode 100644
index 0000000..abc6b67
--- /dev/null
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.annotation.NonNull;
+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.
+ *
+ * @hide
+ */
+@SystemApi
+public final class NetworkAgentConfig implements Parcelable {
+
+ /**
+ * If the {@link Network} is a VPN, whether apps are allowed to bypass the
+ * VPN. This is set by a {@link VpnService} and used by
+ * {@link ConnectivityManager} when creating a VPN.
+ *
+ * @hide
+ */
+ public boolean allowBypass;
+
+ /**
+ * Set if the network was manually/explicitly connected to by the user either from settings
+ * or a 3rd party app. For example, turning on cell data is not explicit but tapping on a wifi
+ * ap in the wifi settings to trigger a connection is explicit. A 3rd party app asking to
+ * connect to a particular access point is also explicit, though this may change in the future
+ * as we want apps to use the multinetwork apis.
+ *
+ * @hide
+ */
+ public boolean explicitlySelected;
+
+ /**
+ * Set if the user desires to use this network even if it is unvalidated. This field has meaning
+ * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
+ * appropriate value based on previous user choice.
+ *
+ * @hide
+ */
+ public boolean acceptUnvalidated;
+
+ /**
+ * Whether the user explicitly set that this network should be validated even if presence of
+ * only partial internet connectivity.
+ *
+ * @hide
+ */
+ public boolean acceptPartialConnectivity;
+
+ /**
+ * Set to avoid surfacing the "Sign in to network" notification.
+ * if carrier receivers/apps are registered to handle the carrier-specific provisioning
+ * procedure, a carrier specific provisioning notification will be placed.
+ * only one notification should be displayed. This field is set based on
+ * which notification should be used for provisioning.
+ *
+ * @hide
+ */
+ public boolean provisioningNotificationDisabled;
+
+ /**
+ *
+ * @return whether the sign in to network notification is enabled by this configuration.
+ */
+ public boolean isProvisioningNotificationEnabled() {
+ return !provisioningNotificationDisabled;
+ }
+
+ /**
+ * For mobile networks, this is the subscriber ID (such as IMSI).
+ *
+ * @hide
+ */
+ public String subscriberId;
+
+ /**
+ * @return the subscriber ID, or null if none.
+ */
+ @Nullable
+ public String getSubscriberId() {
+ return subscriberId;
+ }
+
+ /**
+ * Set to skip 464xlat. This means the device will treat the network as IPv6-only and
+ * will not attempt to detect a NAT64 via RFC 7050 DNS lookups.
+ *
+ * @hide
+ */
+ public boolean skip464xlat;
+
+ /**
+ * @return whether NAT64 prefix detection is enabled.
+ */
+ public boolean isNat64DetectionEnabled() {
+ return !skip464xlat;
+ }
+
+ /**
+ * 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.
+ *
+ * @hide
+ */
+ public boolean hasShownBroken;
+
+ /** @hide */
+ public NetworkAgentConfig() {
+ }
+
+ /** @hide */
+ public NetworkAgentConfig(@Nullable NetworkAgentConfig nac) {
+ if (nac != null) {
+ allowBypass = nac.allowBypass;
+ explicitlySelected = nac.explicitlySelected;
+ acceptUnvalidated = nac.acceptUnvalidated;
+ subscriberId = nac.subscriberId;
+ provisioningNotificationDisabled = nac.provisioningNotificationDisabled;
+ skip464xlat = nac.skip464xlat;
+ }
+ }
+
+ /**
+ * Builder class to facilitate constructing {@link NetworkAgentConfig} objects.
+ */
+ public static class Builder {
+ private final NetworkAgentConfig mConfig = new NetworkAgentConfig();
+
+ /**
+ * Sets the subscriber ID for this network.
+ *
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder setSubscriberId(@Nullable String subscriberId) {
+ mConfig.subscriberId = subscriberId;
+ return this;
+ }
+
+ /**
+ * Disables active detection of NAT64 (e.g., via RFC 7050 DNS lookups). Used to save power
+ * and reduce idle traffic on networks that are known to be IPv6-only without a NAT64.
+ *
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder disableNat64Detection() {
+ mConfig.skip464xlat = true;
+ return this;
+ }
+
+ /**
+ * Disables the "Sign in to network" notification. Used if the network transport will
+ * perform its own carrier-specific provisioning procedure.
+ *
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder disableProvisioningNotification() {
+ mConfig.provisioningNotificationDisabled = true;
+ return this;
+ }
+
+ /**
+ * Returns the constructed {@link NetworkAgentConfig} object.
+ */
+ @NonNull
+ public NetworkAgentConfig build() {
+ return mConfig;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(allowBypass ? 1 : 0);
+ out.writeInt(explicitlySelected ? 1 : 0);
+ out.writeInt(acceptUnvalidated ? 1 : 0);
+ out.writeString(subscriberId);
+ out.writeInt(provisioningNotificationDisabled ? 1 : 0);
+ out.writeInt(skip464xlat ? 1 : 0);
+ }
+
+ public static final @NonNull Creator<NetworkAgentConfig> CREATOR =
+ new Creator<NetworkAgentConfig>() {
+ @Override
+ public NetworkAgentConfig createFromParcel(Parcel in) {
+ NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
+ networkAgentConfig.allowBypass = in.readInt() != 0;
+ networkAgentConfig.explicitlySelected = in.readInt() != 0;
+ networkAgentConfig.acceptUnvalidated = in.readInt() != 0;
+ networkAgentConfig.subscriberId = in.readString();
+ networkAgentConfig.provisioningNotificationDisabled = in.readInt() != 0;
+ networkAgentConfig.skip464xlat = in.readInt() != 0;
+ return networkAgentConfig;
+ }
+
+ @Override
+ public NetworkAgentConfig[] newArray(int size) {
+ return new NetworkAgentConfig[size];
+ }
+ };
+}
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index f10ea20..e271037 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -91,7 +91,7 @@
* with the same NetworkRequest but an updated score.
* Also, network conditions may change for this bearer
* allowing for a better score in the future.
- * msg.arg2 = the serial number of the factory currently responsible for the
+ * msg.arg2 = the ID of the NetworkProvider currently responsible for the
* NetworkAgent handling this request, or NetworkProvider.ID_NONE if none.
*/
public static final int CMD_REQUEST_NETWORK = BASE;
@@ -115,13 +115,6 @@
*/
private static final int CMD_SET_FILTER = BASE + 3;
- /**
- * Sent by NetworkFactory to ConnectivityService to indicate that a request is
- * unfulfillable.
- * @see #releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest).
- */
- public static final int EVENT_UNFULFILLABLE_REQUEST = BASE + 4;
-
private final Context mContext;
private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
private final String LOG_TAG;
@@ -156,8 +149,8 @@
mProvider = new NetworkProvider(mContext, NetworkFactory.this.getLooper(), LOG_TAG) {
@Override
public void onNetworkRequested(@NonNull NetworkRequest request, int score,
- int servingFactoryProviderId) {
- handleAddRequest((NetworkRequest) request, score, servingFactoryProviderId);
+ int servingProviderId) {
+ handleAddRequest((NetworkRequest) request, score, servingProviderId);
}
@Override
@@ -228,8 +221,6 @@
*
* @param request the request to handle.
* @param score the score of the NetworkAgent currently satisfying this request.
- * @param servingProviderId the serial number of the NetworkFactory that
- * created the NetworkAgent currently satisfying this request.
*/
// TODO : remove this method. It is a stopgap measure to help sheperding a number
// of dependent changes that would conflict throughout the automerger graph. Having this
@@ -237,7 +228,7 @@
// the entire tree.
@VisibleForTesting
protected void handleAddRequest(NetworkRequest request, int score) {
- handleAddRequest(request, score, SerialNumber.NONE);
+ handleAddRequest(request, score, NetworkProvider.ID_NONE);
}
/**
@@ -246,24 +237,23 @@
*
* @param request the request to handle.
* @param score the score of the NetworkAgent currently satisfying this request.
- * @param servingProviderId the serial number of the NetworkFactory that
- * created the NetworkAgent currently satisfying this request.
+ * @param servingProviderId the ID of the NetworkProvider that created the NetworkAgent
+ * currently satisfying this request.
*/
@VisibleForTesting
- protected void handleAddRequest(NetworkRequest request, int score,
- int servingProviderId) {
+ protected void handleAddRequest(NetworkRequest request, int score, int servingProviderId) {
NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
if (n == null) {
if (DBG) {
log("got request " + request + " with score " + score
- + " and serial " + servingProviderId);
+ + " and providerId " + servingProviderId);
}
n = new NetworkRequestInfo(request, score, servingProviderId);
mNetworkRequests.put(n.request.requestId, n);
} else {
if (VDBG) {
log("new score " + score + " for exisiting request " + request
- + " with serial " + servingProviderId);
+ + " and providerId " + servingProviderId);
}
n.score = score;
n.providerId = servingProviderId;
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
deleted file mode 100644
index 4ad52d5..0000000
--- a/core/java/android/net/NetworkMisc.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2014 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;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A grab-bag of information (metadata, policies, properties, etc) about a
- * {@link Network}. Since this contains PII, it should not be sent outside the
- * system.
- *
- * @hide
- */
-public class NetworkMisc implements Parcelable {
-
- /**
- * If the {@link Network} is a VPN, whether apps are allowed to bypass the
- * VPN. This is set by a {@link VpnService} and used by
- * {@link ConnectivityManager} when creating a VPN.
- */
- public boolean allowBypass;
-
- /**
- * Set if the network was manually/explicitly connected to by the user either from settings
- * or a 3rd party app. For example, turning on cell data is not explicit but tapping on a wifi
- * ap in the wifi settings to trigger a connection is explicit. A 3rd party app asking to
- * connect to a particular access point is also explicit, though this may change in the future
- * as we want apps to use the multinetwork apis.
- */
- public boolean explicitlySelected;
-
- /**
- * Set if the user desires to use this network even if it is unvalidated. This field has meaning
- * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
- * appropriate value based on previous user choice.
- */
- public boolean acceptUnvalidated;
-
- /**
- * Whether the user explicitly set that this network should be validated even if presence of
- * only partial internet connectivity.
- */
- public boolean acceptPartialConnectivity;
-
- /**
- * Set to avoid surfacing the "Sign in to network" notification.
- * if carrier receivers/apps are registered to handle the carrier-specific provisioning
- * procedure, a carrier specific provisioning notification will be placed.
- * only one notification should be displayed. This field is set based on
- * which notification should be used for provisioning.
- */
- public boolean provisioningNotificationDisabled;
-
- /**
- * For mobile networks, this is the subscriber ID (such as IMSI).
- */
- public String subscriberId;
-
- /**
- * Set to skip 464xlat. This means the device will treat the network as IPv6-only and
- * will not attempt to detect a NAT64 via RFC 7050 DNS lookups.
- */
- public boolean skip464xlat;
-
- /**
- * 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.
- */
- public boolean hasShownBroken;
-
- public NetworkMisc() {
- }
-
- public NetworkMisc(NetworkMisc nm) {
- if (nm != null) {
- allowBypass = nm.allowBypass;
- explicitlySelected = nm.explicitlySelected;
- acceptUnvalidated = nm.acceptUnvalidated;
- subscriberId = nm.subscriberId;
- provisioningNotificationDisabled = nm.provisioningNotificationDisabled;
- skip464xlat = nm.skip464xlat;
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(allowBypass ? 1 : 0);
- out.writeInt(explicitlySelected ? 1 : 0);
- out.writeInt(acceptUnvalidated ? 1 : 0);
- out.writeString(subscriberId);
- out.writeInt(provisioningNotificationDisabled ? 1 : 0);
- out.writeInt(skip464xlat ? 1 : 0);
- }
-
- public static final @android.annotation.NonNull Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
- @Override
- public NetworkMisc createFromParcel(Parcel in) {
- NetworkMisc networkMisc = new NetworkMisc();
- networkMisc.allowBypass = in.readInt() != 0;
- networkMisc.explicitlySelected = in.readInt() != 0;
- networkMisc.acceptUnvalidated = in.readInt() != 0;
- networkMisc.subscriberId = in.readString();
- networkMisc.provisioningNotificationDisabled = in.readInt() != 0;
- networkMisc.skip464xlat = in.readInt() != 0;
- return networkMisc;
- }
-
- @Override
- public NetworkMisc[] newArray(int size) {
- return new NetworkMisc[size];
- }
- };
-}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 9731f3c..7fd86f6 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -455,6 +455,19 @@
}
/**
+ * Returns true iff. the capabilities requested in this NetworkRequest are satisfied by the
+ * provided {@link NetworkCapabilities}.
+ *
+ * @param nc Capabilities that should satisfy this NetworkRequest. null capabilities do not
+ * satisfy any request.
+ * @hide
+ */
+ @SystemApi
+ public boolean satisfiedBy(@Nullable NetworkCapabilities nc) {
+ return networkCapabilities.satisfiedByNetworkCapabilities(nc);
+ }
+
+ /**
* @see Builder#addTransportType(int)
*/
public boolean hasTransport(@Transport int transportType) {
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index f6dc525..c233ec0 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -163,27 +163,26 @@
public static final String EXTRA_NEW_SCORER = "newScorer";
/** @hide */
- @IntDef({CACHE_FILTER_NONE, CACHE_FILTER_CURRENT_NETWORK, CACHE_FILTER_SCAN_RESULTS})
+ @IntDef({SCORE_FILTER_NONE, SCORE_FILTER_CURRENT_NETWORK, SCORE_FILTER_SCAN_RESULTS})
@Retention(RetentionPolicy.SOURCE)
- public @interface CacheUpdateFilter {}
+ public @interface ScoreUpdateFilter {}
/**
- * Do not filter updates sent to the cache.
- * @hide
+ * Do not filter updates sent to the {@link NetworkScoreCallback}].
*/
- public static final int CACHE_FILTER_NONE = 0;
+ public static final int SCORE_FILTER_NONE = 0;
/**
- * Only send cache updates when the network matches the connected network.
- * @hide
+ * Only send updates to the {@link NetworkScoreCallback} when the network matches the connected
+ * network.
*/
- public static final int CACHE_FILTER_CURRENT_NETWORK = 1;
+ public static final int SCORE_FILTER_CURRENT_NETWORK = 1;
/**
- * Only send cache updates when the network is part of the current scan result set.
- * @hide
+ * Only send updates to the {@link NetworkScoreCallback} when the network is part of the
+ * current scan result set.
*/
- public static final int CACHE_FILTER_SCAN_RESULTS = 2;
+ public static final int SCORE_FILTER_SCAN_RESULTS = 2;
/** @hide */
@IntDef({RECOMMENDATIONS_ENABLED_FORCED_OFF, RECOMMENDATIONS_ENABLED_OFF,
@@ -404,13 +403,13 @@
* @throws SecurityException if the caller does not hold the
* {@link permission#REQUEST_NETWORK_SCORES} permission.
* @throws IllegalArgumentException if a score cache is already registered for this type.
- * @deprecated equivalent to registering for cache updates with CACHE_FILTER_NONE.
+ * @deprecated equivalent to registering for cache updates with {@link #SCORE_FILTER_NONE}.
* @hide
*/
@RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
@Deprecated // migrate to registerNetworkScoreCache(int, INetworkScoreCache, int)
public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
- registerNetworkScoreCache(networkType, scoreCache, CACHE_FILTER_NONE);
+ registerNetworkScoreCache(networkType, scoreCache, SCORE_FILTER_NONE);
}
/**
@@ -418,7 +417,7 @@
*
* @param networkType the type of network this cache can handle. See {@link NetworkKey#type}
* @param scoreCache implementation of {@link INetworkScoreCache} to store the scores
- * @param filterType the {@link CacheUpdateFilter} to apply
+ * @param filterType the {@link ScoreUpdateFilter} to apply
* @throws SecurityException if the caller does not hold the
* {@link permission#REQUEST_NETWORK_SCORES} permission.
* @throws IllegalArgumentException if a score cache is already registered for this type.
@@ -426,7 +425,7 @@
*/
@RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache,
- @CacheUpdateFilter int filterType) {
+ @ScoreUpdateFilter int filterType) {
try {
mService.registerNetworkScoreCache(networkType, scoreCache, filterType);
} catch (RemoteException e) {
@@ -510,7 +509,7 @@
* Register a network score callback.
*
* @param networkType the type of network this cache can handle. See {@link NetworkKey#type}
- * @param filterType the {@link CacheUpdateFilter} to apply
+ * @param filterType the {@link ScoreUpdateFilter} to apply
* @param callback implementation of {@link NetworkScoreCallback} that will be invoked when the
* scores change.
* @param executor The executor on which to execute the callbacks.
@@ -522,7 +521,7 @@
@SystemApi
@RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
public void registerNetworkScoreCallback(@NetworkKey.NetworkType int networkType,
- @CacheUpdateFilter int filterType,
+ @ScoreUpdateFilter int filterType,
@NonNull @CallbackExecutor Executor executor,
@NonNull NetworkScoreCallback callback) throws SecurityException {
if (callback == null || executor == null) {
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 08cc4e2..779f7bc 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -31,7 +31,6 @@
import java.io.FileDescriptor;
import java.math.BigInteger;
import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
@@ -313,15 +312,6 @@
}
/**
- * Check if IP address type is consistent between two InetAddress.
- * @return true if both are the same type. False otherwise.
- */
- public static boolean addressTypeMatches(InetAddress left, InetAddress right) {
- return (((left instanceof Inet4Address) && (right instanceof Inet4Address)) ||
- ((left instanceof Inet6Address) && (right instanceof Inet6Address)));
- }
-
- /**
* Convert a 32 char hex string into a Inet6Address.
* throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
* made into an Inet6Address
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index ea6002c..e088094 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -22,6 +22,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.NetUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -441,21 +442,7 @@
@UnsupportedAppUsage
@Nullable
public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
- if ((routes == null) || (dest == null)) return null;
-
- RouteInfo bestRoute = null;
- // pick a longest prefix match under same address type
- for (RouteInfo route : routes) {
- if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
- if ((bestRoute != null) &&
- (bestRoute.mDestination.getPrefixLength() >=
- route.mDestination.getPrefixLength())) {
- continue;
- }
- if (route.matches(dest)) bestRoute = route;
- }
- }
- return bestRoute;
+ return NetUtils.selectBestRoute(routes, dest);
}
/**
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index fb224fb..fc9a8f6 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -20,6 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -53,7 +54,11 @@
public abstract class SocketKeepalive implements AutoCloseable {
static final String TAG = "SocketKeepalive";
- /** @hide */
+ /**
+ * No errors.
+ * @hide
+ */
+ @SystemApi
public static final int SUCCESS = 0;
/** @hide */
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index b0c2546..ac70b52 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -631,10 +631,12 @@
}
}
- private static void sendDeathNotice(DeathRecipient recipient) {
- if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
+ private static void sendDeathNotice(DeathRecipient recipient, IBinder binderProxy) {
+ if (false) {
+ Log.v("JavaBinder", "sendDeathNotice to " + recipient + " for " + binderProxy);
+ }
try {
- recipient.binderDied();
+ recipient.binderDied(binderProxy);
} catch (RuntimeException exc) {
Log.w("BinderNative", "Uncaught exception from death notification",
exc);
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index f336fda..f5fe9c3 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -285,6 +285,13 @@
*/
public interface DeathRecipient {
public void binderDied();
+
+ /**
+ * @hide
+ */
+ default void binderDied(IBinder who) {
+ binderDied();
+ }
}
/**
diff --git a/core/java/android/os/TimestampedValue.java b/core/java/android/os/TimestampedValue.java
index 348574e..f4c87ac 100644
--- a/core/java/android/os/TimestampedValue.java
+++ b/core/java/android/os/TimestampedValue.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import java.util.Objects;
@@ -35,19 +36,27 @@
* @param <T> the type of the value with an associated timestamp
* @hide
*/
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class TimestampedValue<T> implements Parcelable {
private final long mReferenceTimeMillis;
+ @Nullable
private final T mValue;
- public TimestampedValue(long referenceTimeMillis, T value) {
+ public TimestampedValue(long referenceTimeMillis, @Nullable T value) {
mReferenceTimeMillis = referenceTimeMillis;
mValue = value;
}
+ /** Returns the reference time value. See {@link TimestampedValue} for more information. */
public long getReferenceTimeMillis() {
return mReferenceTimeMillis;
}
+ /**
+ * Returns the value associated with the timestamp. See {@link TimestampedValue} for more
+ * information.
+ */
+ @Nullable
public T getValue() {
return mValue;
}
@@ -86,6 +95,8 @@
return one.mReferenceTimeMillis - two.mReferenceTimeMillis;
}
+ /** @hide */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final @NonNull Parcelable.Creator<TimestampedValue<?>> CREATOR =
new Parcelable.ClassLoaderCreator<TimestampedValue<?>>() {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a31c3d1..089122d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -510,28 +510,33 @@
"android.settings.WIFI_IP_SETTINGS";
/**
- * Activity Action: Show setting page to process an Easy Connect (Wi-Fi DPP) QR code and start
+ * Activity Action: Show setting page to process a Wi-Fi Easy Connect (aka DPP) URI and start
* configuration. This intent should be used when you want to use this device to take on the
- * configurator role for an IoT/other device. When provided with a valid DPP URI string Settings
- * will open a wifi selection screen for the user to indicate which network they would like to
- * configure the device specified in the DPP URI string for and carry them through the rest of
- * the flow for provisioning the device.
+ * configurator role for an IoT/other device. When provided with a valid DPP URI
+ * string, Settings will open a Wi-Fi selection screen for the user to indicate which network
+ * they would like to configure the device specified in the DPP URI string and
+ * carry them through the rest of the flow for provisioning the device.
* <p>
- * In some cases, a matching Activity may not exist, so ensure you safeguard against this by
- * checking WifiManager.isEasyConnectSupported();
+ * In some cases, a matching Activity may not exist, so ensure to safeguard against this by
+ * checking {@link WifiManager#isEasyConnectSupported()}.
* <p>
* Input: The Intent's data URI specifies bootstrapping information for authenticating and
- * provisioning the peer, with the "DPP" scheme.
+ * provisioning the peer, and uses a "DPP" scheme. The URI should be attached to the intent
+ * using {@link Intent#setData(Uri)}. The calling app can obtain a DPP URI in any
+ * way, e.g. by scanning a QR code or other out-of-band methods. The calling app may also
+ * attach the {@link #EXTRA_EASY_CONNECT_BAND_LIST} extra to provide information
+ * about the bands supported by the enrollee device.
* <p>
- * Output: After {@code startActivityForResult}, the callback {@code onActivityResult} will have
- * resultCode {@link android.app.Activity#RESULT_OK} if Wi-Fi Easy Connect configuration succeeded
- * and the user tapped 'Done' button, or {@link android.app.Activity#RESULT_CANCELED} if operation
- * failed and user tapped 'Cancel'. In case the operation has failed, a status code from {@link
- * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode} will be returned as
- * Extra {@link #EXTRA_EASY_CONNECT_ERROR_CODE}. Easy Connect R2 Enrollees report additional
- * details about the error they encountered, which will be provided in the {@link
- * #EXTRA_EASY_CONNECT_ATTEMPTED_SSID}, {@link #EXTRA_EASY_CONNECT_CHANNEL_LIST}, and {@link
- * #EXTRA_EASY_CONNECT_BAND_LIST}.
+ * Output: After calling {@link android.app.Activity#startActivityForResult}, the callback
+ * {@code onActivityResult} will have resultCode {@link android.app.Activity#RESULT_OK} if
+ * the Wi-Fi Easy Connect configuration succeeded and the user tapped the 'Done' button, or
+ * {@link android.app.Activity#RESULT_CANCELED} if the operation failed and user tapped the
+ * 'Cancel' button. In case the operation has failed, a status code from
+ * {@link android.net.wifi.EasyConnectStatusCallback} {@code EASY_CONNECT_EVENT_FAILURE_*} will
+ * be returned as an Extra {@link #EXTRA_EASY_CONNECT_ERROR_CODE}. Easy Connect R2
+ * Enrollees report additional details about the error they encountered, which will be
+ * provided in the {@link #EXTRA_EASY_CONNECT_ATTEMPTED_SSID},
+ * {@link #EXTRA_EASY_CONNECT_CHANNEL_LIST}, and {@link #EXTRA_EASY_CONNECT_BAND_LIST}.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_URI =
@@ -540,12 +545,15 @@
/**
* Activity Extra: The Easy Connect operation error code
* <p>
- * An extra returned on the result intent received when using the {@link
- * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
- * extra contains the error code of the operation - one of
- * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode}.
- * If there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK},
+ * An extra returned on the result intent received when using the
+ * {@link #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation.
+ * This extra contains the integer error code of the operation - one of
+ * {@link android.net.wifi.EasyConnectStatusCallback} {@code EASY_CONNECT_EVENT_FAILURE_*}. If
+ * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK},
* then this extra is not attached to the result intent.
+ * <p>
+ * Use the {@link Intent#hasExtra(String)} to determine whether the extra is attached and
+ * {@link Intent#getIntExtra(String, int)} to obtain the error code data.
*/
public static final String EXTRA_EASY_CONNECT_ERROR_CODE =
"android.provider.extra.EASY_CONNECT_ERROR_CODE";
@@ -557,11 +565,13 @@
* #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
* extra contains the SSID of the Access Point that the remote Enrollee tried to connect to.
* This value is populated only by remote R2 devices, and only for the following error codes:
- * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}
- * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION}.
+ * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}
+ * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION}.
* Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If
* there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then
* this extra is not attached to the result intent.
+ * <p>
+ * Use the {@link Intent#getStringExtra(String)} to obtain the SSID.
*/
public static final String EXTRA_EASY_CONNECT_ATTEMPTED_SSID =
"android.provider.extra.EASY_CONNECT_ATTEMPTED_SSID";
@@ -571,13 +581,15 @@
* <p>
* An extra returned on the result intent received when using the {@link
* #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
- * extra contains the list channels the Enrollee used to scan for a network. This value is
+ * extra contains the channel list that the Enrollee scanned for a network. This value is
* populated only by remote R2 devices, and only for the following error code: {@link
- * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}.
+ * android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}.
* Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If
* there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then
* this extra is not attached to the result intent. The list is JSON formatted, as an array
* (Wi-Fi global operating classes) of arrays (Wi-Fi channels).
+ * <p>
+ * Use the {@link Intent#getStringExtra(String)} to obtain the list.
*/
public static final String EXTRA_EASY_CONNECT_CHANNEL_LIST =
"android.provider.extra.EASY_CONNECT_CHANNEL_LIST";
@@ -585,17 +597,31 @@
/**
* Activity Extra: The Band List that the Enrollee supports.
* <p>
- * An extra returned on the result intent received when using the {@link
- * #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation. This
- * extra contains the bands the Enrollee supports, expressed as the Global Operating Class,
- * see Table E-4 in IEEE Std 802.11-2016 -Global operating classes. This value is populated only
- * by remote R2 devices, and only for the following error codes: {@link
- * android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK}
- * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION}
- * {@link android.net.wifi.EasyConnectStatusCallback.EasyConnectFailureStatusCode#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION}.
- * Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}. If
- * there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}, then
- * this extra is not attached to the result intent.
+ * This extra contains the bands the Enrollee supports, expressed as the Global Operating
+ * Class, see Table E-4 in IEEE Std 802.11-2016 Global operating classes. It is used both as
+ * input, to configure the Easy Connect operation and as output of the operation.
+ * <p>
+ * As input: an optional extra to be attached to the
+ * {@link #ACTION_PROCESS_WIFI_EASY_CONNECT_URI}. If attached, it indicates the bands which
+ * the remote device (enrollee, device-to-be-configured) supports. The Settings operation
+ * may take this into account when presenting the user with list of networks configurations
+ * to be used. The calling app may obtain this information in any out-of-band method. The
+ * information should be attached as an array of raw integers - using the
+ * {@link Intent#putExtra(String, int[])}.
+ * <p>
+ * As output: an extra returned on the result intent received when using the
+ * {@link #ACTION_PROCESS_WIFI_EASY_CONNECT_URI} intent to launch the Easy Connect Operation
+ * . This value is populated only by remote R2 devices, and only for the following error
+ * codes:
+ * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK},
+ * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION},
+ * or
+ * {@link android.net.wifi.EasyConnectStatusCallback#EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION}.
+ * Therefore, always check if this extra is available using {@link Intent#hasExtra(String)}.
+ * If there is no error, i.e. if the operation returns {@link android.app.Activity#RESULT_OK}
+ * , then this extra is not attached to the result intent.
+ * <p>
+ * Use the {@link Intent#getIntArrayExtra(String)} to obtain the list.
*/
public static final String EXTRA_EASY_CONNECT_BAND_LIST =
"android.provider.extra.EASY_CONNECT_BAND_LIST";
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 2e7ac3f5..f369064 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4142,7 +4142,6 @@
* <li>{@link #ENABLE_CMAS_PRESIDENTIAL_PREF}</li>
* <li>{@link #ENABLE_ALERT_VIBRATION_PREF}</li>
* <li>{@link #ENABLE_EMERGENCY_PERF}</li>
- * <li>{@link #ENABLE_FULL_VOLUME_PREF}</li>
* <li>{@link #ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF}</li>
* </ul>
* @hide
@@ -4205,10 +4204,6 @@
public static final @NonNull String ENABLE_EMERGENCY_PERF =
"enable_emergency_alerts";
- /** Preference to enable volume for alerts */
- public static final @NonNull String ENABLE_FULL_VOLUME_PREF =
- "use_full_volume";
-
/** Preference to enable receive alerts in second language */
public static final @NonNull String ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF =
"receive_cmas_in_second_language";
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index d646e23..00060ab 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -22,14 +22,11 @@
package android.se.omapi;
-import android.app.ActivityThread;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -143,10 +140,6 @@
throw new NullPointerException("Arguments must not be null");
}
- if (!hasOMAPIReaders()) {
- throw new UnsupportedOperationException("Device does not support any OMAPI reader");
- }
-
mContext = context;
mSEListener.mListener = listener;
mSEListener.mExecutor = executor;
@@ -277,23 +270,4 @@
throw new IllegalStateException(e.getMessage());
}
}
-
- /**
- * Helper to check if this device support any OMAPI readers
- */
- private static boolean hasOMAPIReaders() {
- IPackageManager pm = ActivityThread.getPackageManager();
- if (pm == null) {
- Log.e(TAG, "Cannot get package manager, assuming OMAPI readers supported");
- return true;
- }
- try {
- return pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC, 0)
- || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE, 0)
- || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD, 0);
- } catch (RemoteException e) {
- Log.e(TAG, "Package manager query failed, assuming OMAPI readers supported", e);
- return true;
- }
- }
}
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index d827b30..262d989 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.IntentSender;
import android.os.Parcel;
import android.os.Parcelable;
@@ -238,6 +239,7 @@
public Builder(@NonNull RemoteViews presentation,
@NonNull InlinePresentation inlinePresentation) {
Preconditions.checkNotNull(presentation, "presentation must be non-null");
+ Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
mPresentation = presentation;
mInlinePresentation = inlinePresentation;
}
@@ -248,7 +250,8 @@
* @param presentation The presentation used to visualize this dataset.
*/
public Builder(@NonNull RemoteViews presentation) {
- this(presentation, null);
+ Preconditions.checkNotNull(presentation, "presentation must be non-null");
+ mPresentation = presentation;
}
/**
@@ -262,7 +265,9 @@
* @hide
*/
@SystemApi
+ @TestApi
public Builder(@NonNull InlinePresentation inlinePresentation) {
+ Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
mInlinePresentation = inlinePresentation;
}
@@ -576,6 +581,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public @NonNull Builder setInlinePresentation(@NonNull AutofillId id,
@Nullable AutofillValue value, @Nullable Pattern filter,
@NonNull InlinePresentation inlinePresentation) {
@@ -672,11 +678,13 @@
// using specially crafted parcels.
final RemoteViews presentation = parcel.readParcelable(null);
final InlinePresentation inlinePresentation = parcel.readParcelable(null);
- final Builder builder = presentation == null
- ? new Builder(inlinePresentation)
- : inlinePresentation == null
+ final Builder builder = presentation != null
+ ? inlinePresentation == null
? new Builder(presentation)
- : new Builder(presentation, inlinePresentation);
+ : new Builder(presentation, inlinePresentation)
+ : inlinePresentation == null
+ ? new Builder()
+ : new Builder(inlinePresentation);
final ArrayList<AutofillId> ids =
parcel.createTypedArrayList(AutofillId.CREATOR);
final ArrayList<AutofillValue> values =
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 1b2db36..9d22d30 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -42,6 +42,8 @@
public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
+ public static final String NOTIF_CONVO_BYPASS_SHORTCUT_REQ =
+ "settings_notif_convo_bypass_shortcut_req";
private static final Map<String, String> DEFAULT_FLAGS;
@@ -60,6 +62,7 @@
DEFAULT_FLAGS.put("settings_work_profile", "true");
DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false");
DEFAULT_FLAGS.put("settings_conditionals", "false");
+ DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
}
/**
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index fa994ba..0892c94 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -16,6 +16,8 @@
package android.util;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
@@ -25,172 +27,270 @@
import android.net.NetworkInfo;
import android.net.SntpClient;
import android.os.SystemClock;
-import android.os.TimestampedValue;
import android.provider.Settings;
import android.text.TextUtils;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+import java.util.function.Supplier;
+
/**
- * {@link TrustedTime} that connects with a remote NTP server as its trusted
- * time source.
+ * A singleton that connects with a remote NTP server as its trusted time source. This class
+ * is thread-safe. The {@link #forceRefresh()} method is synchronous, i.e. it may occupy the
+ * current thread while performing an NTP request. All other threads calling {@link #forceRefresh()}
+ * will block during that request.
*
* @hide
*/
public class NtpTrustedTime implements TrustedTime {
+
+ /**
+ * The result of a successful NTP query.
+ *
+ * @hide
+ */
+ public static class TimeResult {
+ private final long mTimeMillis;
+ private final long mElapsedRealtimeMillis;
+ private final long mCertaintyMillis;
+
+ public TimeResult(long timeMillis, long elapsedRealtimeMillis, long certaintyMillis) {
+ mTimeMillis = timeMillis;
+ mElapsedRealtimeMillis = elapsedRealtimeMillis;
+ mCertaintyMillis = certaintyMillis;
+ }
+
+ public long getTimeMillis() {
+ return mTimeMillis;
+ }
+
+ public long getElapsedRealtimeMillis() {
+ return mElapsedRealtimeMillis;
+ }
+
+ public long getCertaintyMillis() {
+ return mCertaintyMillis;
+ }
+
+ /** Calculates and returns the current time accounting for the age of this result. */
+ public long currentTimeMillis() {
+ return mTimeMillis + getAgeMillis();
+ }
+
+ /** Calculates and returns the age of this result. */
+ public long getAgeMillis() {
+ return SystemClock.elapsedRealtime() - mElapsedRealtimeMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "TimeResult{"
+ + "mTimeMillis=" + mTimeMillis
+ + ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis
+ + ", mCertaintyMillis=" + mCertaintyMillis
+ + '}';
+ }
+ }
+
private static final String TAG = "NtpTrustedTime";
private static final boolean LOGD = false;
private static NtpTrustedTime sSingleton;
- private static Context sContext;
- private final String mServer;
- private final long mTimeout;
+ @NonNull
+ private final Context mContext;
- private ConnectivityManager mCM;
+ /**
+ * A supplier that returns the ConnectivityManager. The Supplier can return null if
+ * ConnectivityService isn't running yet.
+ */
+ private final Supplier<ConnectivityManager> mConnectivityManagerSupplier =
+ new Supplier<ConnectivityManager>() {
+ private ConnectivityManager mConnectivityManager;
- private boolean mHasCache;
- private long mCachedNtpTime;
- private long mCachedNtpElapsedRealtime;
- private long mCachedNtpCertainty;
+ @Nullable
+ @Override
+ public synchronized ConnectivityManager get() {
+ // We can't do this at initialization time: ConnectivityService might not be running
+ // yet.
+ if (mConnectivityManager == null) {
+ mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
+ }
+ return mConnectivityManager;
+ }
+ };
- private NtpTrustedTime(String server, long timeout) {
- if (LOGD) Log.d(TAG, "creating NtpTrustedTime using " + server);
- mServer = server;
- mTimeout = timeout;
+ // Declared volatile and accessed outside of synchronized blocks to avoid blocking reads during
+ // forceRefresh().
+ private volatile TimeResult mTimeResult;
+
+ private NtpTrustedTime(Context context) {
+ mContext = Objects.requireNonNull(context);
}
@UnsupportedAppUsage
public static synchronized NtpTrustedTime getInstance(Context context) {
if (sSingleton == null) {
- final Resources res = context.getResources();
- final ContentResolver resolver = context.getContentResolver();
-
- final String defaultServer = res.getString(
- com.android.internal.R.string.config_ntpServer);
- final long defaultTimeout = res.getInteger(
- com.android.internal.R.integer.config_ntpTimeout);
-
- final String secureServer = Settings.Global.getString(
- resolver, Settings.Global.NTP_SERVER);
- final long timeout = Settings.Global.getLong(
- resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout);
-
- final String server = secureServer != null ? secureServer : defaultServer;
- sSingleton = new NtpTrustedTime(server, timeout);
- sContext = context;
+ Context appContext = context.getApplicationContext();
+ sSingleton = new NtpTrustedTime(appContext);
}
-
return sSingleton;
}
- @Override
@UnsupportedAppUsage
public boolean forceRefresh() {
- // We can't do this at initialization time: ConnectivityService might not be running yet.
synchronized (this) {
- if (mCM == null) {
- mCM = sContext.getSystemService(ConnectivityManager.class);
+ NtpConnectionInfo connectionInfo = getNtpConnectionInfo();
+ if (connectionInfo == null) {
+ // missing server config, so no trusted time available
+ if (LOGD) Log.d(TAG, "forceRefresh: invalid server config");
+ return false;
}
- }
- final Network network = mCM == null ? null : mCM.getActiveNetwork();
- return forceRefresh(network);
- }
-
- public boolean forceRefresh(Network network) {
- if (TextUtils.isEmpty(mServer)) {
- // missing server, so no trusted time available
- return false;
- }
-
- // We can't do this at initialization time: ConnectivityService might not be running yet.
- synchronized (this) {
- if (mCM == null) {
- mCM = sContext.getSystemService(ConnectivityManager.class);
+ ConnectivityManager connectivityManager = mConnectivityManagerSupplier.get();
+ if (connectivityManager == null) {
+ if (LOGD) Log.d(TAG, "forceRefresh: no ConnectivityManager");
+ return false;
}
- }
+ final Network network = connectivityManager.getActiveNetwork();
+ final NetworkInfo ni = connectivityManager.getNetworkInfo(network);
+ if (ni == null || !ni.isConnected()) {
+ if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
+ return false;
+ }
- final NetworkInfo ni = mCM == null ? null : mCM.getNetworkInfo(network);
- if (ni == null || !ni.isConnected()) {
- if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
- return false;
- }
-
-
- if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
- final SntpClient client = new SntpClient();
- if (client.requestTime(mServer, (int) mTimeout, network)) {
- mHasCache = true;
- mCachedNtpTime = client.getNtpTime();
- mCachedNtpElapsedRealtime = client.getNtpTimeReference();
- mCachedNtpCertainty = client.getRoundTripTime() / 2;
- return true;
- } else {
- return false;
+ if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
+ final SntpClient client = new SntpClient();
+ final String serverName = connectionInfo.getServer();
+ final int timeoutMillis = connectionInfo.getTimeoutMillis();
+ if (client.requestTime(serverName, timeoutMillis, network)) {
+ long ntpCertainty = client.getRoundTripTime() / 2;
+ mTimeResult = new TimeResult(
+ client.getNtpTime(), client.getNtpTimeReference(), ntpCertainty);
+ return true;
+ } else {
+ return false;
+ }
}
}
- @Override
+ /**
+ * Only kept for UnsupportedAppUsage.
+ *
+ * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically.
+ */
+ @Deprecated
@UnsupportedAppUsage
public boolean hasCache() {
- return mHasCache;
+ return mTimeResult != null;
}
+ /**
+ * Only kept for UnsupportedAppUsage.
+ *
+ * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically.
+ */
+ @Deprecated
@Override
public long getCacheAge() {
- if (mHasCache) {
- return SystemClock.elapsedRealtime() - mCachedNtpElapsedRealtime;
+ TimeResult timeResult = mTimeResult;
+ if (timeResult != null) {
+ return SystemClock.elapsedRealtime() - timeResult.getElapsedRealtimeMillis();
} else {
return Long.MAX_VALUE;
}
}
- @Override
- public long getCacheCertainty() {
- if (mHasCache) {
- return mCachedNtpCertainty;
- } else {
- return Long.MAX_VALUE;
- }
- }
-
- @Override
+ /**
+ * Only kept for UnsupportedAppUsage.
+ *
+ * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically.
+ */
+ @Deprecated
@UnsupportedAppUsage
public long currentTimeMillis() {
- if (!mHasCache) {
+ TimeResult timeResult = mTimeResult;
+ if (timeResult == null) {
throw new IllegalStateException("Missing authoritative time source");
}
if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit");
// current time is age after the last ntp cache; callers who
- // want fresh values will hit makeAuthoritative() first.
- return mCachedNtpTime + getCacheAge();
- }
-
- @UnsupportedAppUsage
- public long getCachedNtpTime() {
- if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit");
- return mCachedNtpTime;
- }
-
- @UnsupportedAppUsage
- public long getCachedNtpTimeReference() {
- return mCachedNtpElapsedRealtime;
+ // want fresh values will hit forceRefresh() first.
+ return timeResult.currentTimeMillis();
}
/**
- * Returns the combination of {@link #getCachedNtpTime()} and {@link
- * #getCachedNtpTimeReference()} as a {@link TimestampedValue}. This method is useful when
- * passing the time to another component that will adjust for elapsed time.
+ * Only kept for UnsupportedAppUsage.
*
- * @throws IllegalStateException if there is no cached value
+ * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically.
*/
- public TimestampedValue<Long> getCachedNtpTimeSignal() {
- if (!mHasCache) {
- throw new IllegalStateException("Missing authoritative time source");
- }
- if (LOGD) Log.d(TAG, "getCachedNtpTimeSignal() cache hit");
-
- return new TimestampedValue<>(mCachedNtpElapsedRealtime, mCachedNtpTime);
+ @Deprecated
+ @UnsupportedAppUsage
+ public long getCachedNtpTime() {
+ if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit");
+ TimeResult timeResult = mTimeResult;
+ return timeResult == null ? 0 : timeResult.getTimeMillis();
}
+ /**
+ * Only kept for UnsupportedAppUsage.
+ *
+ * @deprecated Use {@link #getCachedTimeResult()} to obtain a {@link TimeResult} atomically.
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public long getCachedNtpTimeReference() {
+ TimeResult timeResult = mTimeResult;
+ return timeResult == null ? 0 : timeResult.getElapsedRealtimeMillis();
+ }
+
+ /**
+ * Returns an object containing the latest NTP information available. Can return {@code null} if
+ * no information is available.
+ */
+ @Nullable
+ public TimeResult getCachedTimeResult() {
+ return mTimeResult;
+ }
+
+ private static class NtpConnectionInfo {
+
+ @NonNull private final String mServer;
+ private final int mTimeoutMillis;
+
+ NtpConnectionInfo(@NonNull String server, int timeoutMillis) {
+ mServer = Objects.requireNonNull(server);
+ mTimeoutMillis = timeoutMillis;
+ }
+
+ @NonNull
+ public String getServer() {
+ return mServer;
+ }
+
+ int getTimeoutMillis() {
+ return mTimeoutMillis;
+ }
+ }
+
+ @GuardedBy("this")
+ private NtpConnectionInfo getNtpConnectionInfo() {
+ final ContentResolver resolver = mContext.getContentResolver();
+
+ final Resources res = mContext.getResources();
+ final String defaultServer = res.getString(
+ com.android.internal.R.string.config_ntpServer);
+ final int defaultTimeoutMillis = res.getInteger(
+ com.android.internal.R.integer.config_ntpTimeout);
+
+ final String secureServer = Settings.Global.getString(
+ resolver, Settings.Global.NTP_SERVER);
+ final int timeoutMillis = Settings.Global.getInt(
+ resolver, Settings.Global.NTP_TIMEOUT, defaultTimeoutMillis);
+
+ final String server = secureServer != null ? secureServer : defaultServer;
+ return TextUtils.isEmpty(server) ? null : new NtpConnectionInfo(server, timeoutMillis);
+ }
}
diff --git a/core/java/android/util/TrustedTime.java b/core/java/android/util/TrustedTime.java
index 1360f87..f41fe85 100644
--- a/core/java/android/util/TrustedTime.java
+++ b/core/java/android/util/TrustedTime.java
@@ -20,42 +20,48 @@
/**
* Interface that provides trusted time information, possibly coming from an NTP
- * server. Implementations may cache answers until {@link #forceRefresh()}.
+ * server.
*
* @hide
+ * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime}
*/
public interface TrustedTime {
/**
* Force update with an external trusted time source, returning {@code true}
* when successful.
+ *
+ * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime}
*/
+ @Deprecated
@UnsupportedAppUsage
public boolean forceRefresh();
/**
* Check if this instance has cached a response from a trusted time source.
+ *
+ * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime}
*/
+ @Deprecated
@UnsupportedAppUsage
- public boolean hasCache();
+ boolean hasCache();
/**
* Return time since last trusted time source contact, or
* {@link Long#MAX_VALUE} if never contacted.
+ *
+ * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime}
*/
+ @Deprecated
@UnsupportedAppUsage
public long getCacheAge();
/**
- * Return certainty of cached trusted time in milliseconds, or
- * {@link Long#MAX_VALUE} if never contacted. Smaller values are more
- * precise.
- */
- public long getCacheCertainty();
-
- /**
* Return current time similar to {@link System#currentTimeMillis()},
* possibly using a cached authoritative time source.
+ *
+ * @deprecated Only kept for UnsupportedAppUsage. Do not use. See {@link NtpTrustedTime}
*/
+ @Deprecated
@UnsupportedAppUsage
- public long currentTimeMillis();
+ long currentTimeMillis();
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4368115..178b3c0 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
@@ -1010,6 +1011,9 @@
* @return Supported WCG color spaces.
* @hide
*/
+ @SuppressLint("VisiblySynchronized")
+ @NonNull
+ @TestApi
public @ColorMode ColorSpace[] getSupportedWideColorGamut() {
synchronized (this) {
final ColorSpace[] defaultColorSpaces = new ColorSpace[0];
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index c67ff6e..7986ceb 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -128,6 +128,19 @@
}
/**
+ * Called when a focus event is received.
+ *
+ * @param hasFocus if true, the window associated with this input channel has just received
+ * focus
+ * if false, the window associated with this input channel has just lost focus
+ * @param inTouchMode if true, the device is in touch mode
+ * if false, the device is not in touch mode
+ */
+ // Called from native code.
+ public void onFocusEvent(boolean hasFocus, boolean inTouchMode) {
+ }
+
+ /**
* Called when a batched input event is pending.
*
* The batched input event will continue to accumulate additional movement
@@ -213,8 +226,13 @@
onBatchedInputEventPending();
}
- public static interface Factory {
- public InputEventReceiver createInputEventReceiver(
- InputChannel inputChannel, Looper looper);
+ /**
+ * Factory for InputEventReceiver
+ */
+ public interface Factory {
+ /**
+ * Create a new InputReceiver for a given inputChannel
+ */
+ InputEventReceiver createInputEventReceiver(InputChannel inputChannel, Looper looper);
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ca8ba4c..2ef944f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -826,6 +826,10 @@
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
+ if (WindowManagerGlobal.USE_BLAST_ADAPTER) {
+ mWindowAttributes.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
+ }
attrs = mWindowAttributes;
setTag();
@@ -1308,7 +1312,7 @@
mWindowAttributes.privateFlags |= compatibleWindowFlag;
if (WindowManagerGlobal.USE_BLAST_ADAPTER) {
- mWindowAttributes.privateFlags =
+ mWindowAttributes.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
}
@@ -8040,6 +8044,11 @@
}
@Override
+ public void onFocusEvent(boolean hasFocus, boolean inTouchMode) {
+ windowFocusChanged(hasFocus, inTouchMode);
+ }
+
+ @Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 386c9cb..860ce90 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -17,6 +17,8 @@
package android.view.inputmethod;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.os.Parcelable;
import android.view.inline.InlinePresentationSpec;
@@ -49,6 +51,21 @@
*/
private final @NonNull List<InlinePresentationSpec> mPresentationSpecs;
+ /**
+ * The package name of the app that requests for the inline suggestions and will host the
+ * embedded suggestion views. The app does not have to set the value for the field because
+ * it'll be set by the system for safety reasons.
+ */
+ private @NonNull String mHostPackageName;
+
+ /**
+ * @hide
+ * @see {@link #mHostPackageName}.
+ */
+ public void setHostPackageName(@NonNull String hostPackageName) {
+ mHostPackageName = hostPackageName;
+ }
+
private void onConstructed() {
Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size());
}
@@ -57,9 +74,15 @@
return SUGGESTION_COUNT_UNLIMITED;
}
+ private static String defaultHostPackageName() {
+ return ActivityThread.currentPackageName();
+ }
+
/** @hide */
abstract static class BaseBuilder {
abstract Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value);
+
+ abstract Builder setHostPackageName(@Nullable String value);
}
@@ -80,11 +103,15 @@
@DataClass.Generated.Member
/* package-private */ InlineSuggestionsRequest(
int maxSuggestionCount,
- @NonNull List<InlinePresentationSpec> presentationSpecs) {
+ @NonNull List<InlinePresentationSpec> presentationSpecs,
+ @NonNull String hostPackageName) {
this.mMaxSuggestionCount = maxSuggestionCount;
this.mPresentationSpecs = presentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPresentationSpecs);
+ this.mHostPackageName = hostPackageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHostPackageName);
onConstructed();
}
@@ -108,6 +135,16 @@
return mPresentationSpecs;
}
+ /**
+ * The package name of the app that requests for the inline suggestions and will host the
+ * embedded suggestion views. The app does not have to set the value for the field because
+ * it'll be set by the system for safety reasons.
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getHostPackageName() {
+ return mHostPackageName;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -116,13 +153,14 @@
return "InlineSuggestionsRequest { " +
"maxSuggestionCount = " + mMaxSuggestionCount + ", " +
- "presentationSpecs = " + mPresentationSpecs +
+ "presentationSpecs = " + mPresentationSpecs + ", " +
+ "hostPackageName = " + mHostPackageName +
" }";
}
@Override
@DataClass.Generated.Member
- public boolean equals(@android.annotation.Nullable Object o) {
+ public boolean equals(@Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
// boolean fieldNameEquals(InlineSuggestionsRequest other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
@@ -134,7 +172,8 @@
//noinspection PointlessBooleanExpression
return true
&& mMaxSuggestionCount == that.mMaxSuggestionCount
- && java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs);
+ && java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs)
+ && java.util.Objects.equals(mHostPackageName, that.mHostPackageName);
}
@Override
@@ -146,6 +185,7 @@
int _hash = 1;
_hash = 31 * _hash + mMaxSuggestionCount;
_hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpecs);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mHostPackageName);
return _hash;
}
@@ -157,6 +197,7 @@
dest.writeInt(mMaxSuggestionCount);
dest.writeParcelableList(mPresentationSpecs, flags);
+ dest.writeString(mHostPackageName);
}
@Override
@@ -173,11 +214,15 @@
int maxSuggestionCount = in.readInt();
List<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
in.readParcelableList(presentationSpecs, InlinePresentationSpec.class.getClassLoader());
+ String hostPackageName = in.readString();
this.mMaxSuggestionCount = maxSuggestionCount;
this.mPresentationSpecs = presentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPresentationSpecs);
+ this.mHostPackageName = hostPackageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHostPackageName);
onConstructed();
}
@@ -205,6 +250,7 @@
private int mMaxSuggestionCount;
private @NonNull List<InlinePresentationSpec> mPresentationSpecs;
+ private @NonNull String mHostPackageName;
private long mBuilderFieldsSet = 0L;
@@ -260,22 +306,40 @@
return this;
}
+ /**
+ * The package name of the app that requests for the inline suggestions and will host the
+ * embedded suggestion views. The app does not have to set the value for the field because
+ * it'll be set by the system for safety reasons.
+ */
+ @DataClass.Generated.Member
+ @Override
+ @NonNull Builder setHostPackageName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mHostPackageName = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull InlineSuggestionsRequest build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x4; // Mark builder used
+ mBuilderFieldsSet |= 0x8; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mMaxSuggestionCount = defaultMaxSuggestionCount();
}
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mHostPackageName = defaultHostPackageName();
+ }
InlineSuggestionsRequest o = new InlineSuggestionsRequest(
mMaxSuggestionCount,
- mPresentationSpecs);
+ mPresentationSpecs,
+ mHostPackageName);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x4) != 0) {
+ if ((mBuilderFieldsSet & 0x8) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -283,10 +347,10 @@
}
@DataClass.Generated(
- time = 1576637222199L,
+ time = 1578948035951L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\npublic void setHostPackageName(java.lang.String)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 20af76b..f851e10 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5737,7 +5737,7 @@
private boolean mIsDraggingCursor;
public void onTouchEvent(MotionEvent event) {
- if (getSelectionController().isCursorBeingModified()) {
+ if (hasSelectionController() && getSelectionController().isCursorBeingModified()) {
return;
}
switch (event.getActionMasked()) {
diff --git a/core/java/com/android/ims/internal/uce/common/CapInfo.java b/core/java/com/android/ims/internal/uce/common/CapInfo.java
index c45a3a4..2bb3f1f 100644
--- a/core/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/core/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -64,6 +64,20 @@
private boolean mRcsIpVideoCallSupported = false;
/** RCS IP Video call support . */
private boolean mRcsIpVideoOnlyCallSupported = false;
+ /** IP Geo location Push using SMS. */
+ private boolean mGeoSmsSupported = false;
+ /** RCS call composer support. */
+ private boolean mCallComposerSupported = false;
+ /** RCS post-call support. */
+ private boolean mPostCallSupported = false;
+ /** Shared map support. */
+ private boolean mSharedMapSupported = false;
+ /** Shared Sketch supported. */
+ private boolean mSharedSketchSupported = false;
+ /** Chatbot communication support. */
+ private boolean mChatbotSupported = false;
+ /** Chatbot role support. */
+ private boolean mChatbotRoleSupported = false;
/** List of supported extensions. */
private String[] mExts = new String[10];
/** Time used to compute when to query again. */
@@ -386,6 +400,104 @@
this.mRcsIpVideoOnlyCallSupported = rcsIpVideoOnlyCallSupported;
}
+ /**
+ * Checks whether Geo Push via SMS is supported.
+ */
+ public boolean isGeoSmsSupported() {
+ return mGeoSmsSupported;
+ }
+
+ /**
+ * Sets Geolocation Push via SMS as supported or not supported.
+ */
+ public void setGeoSmsSupported(boolean geoSmsSupported) {
+ this.mGeoSmsSupported = geoSmsSupported;
+ }
+
+ /**
+ * Checks whether RCS call composer is supported.
+ */
+ public boolean isCallComposerSupported() {
+ return mCallComposerSupported;
+ }
+
+ /**
+ * Sets call composer as supported or not supported.
+ */
+ public void setCallComposerSupported(boolean callComposerSupported) {
+ this.mCallComposerSupported = callComposerSupported;
+ }
+
+ /**
+ * Checks whether post call is supported.
+ */
+ public boolean isPostCallSupported(){
+ return mPostCallSupported;
+ }
+
+ /**
+ * Sets post call as supported or not supported.
+ */
+ public void setPostCallSupported(boolean postCallSupported) {
+ this.mPostCallSupported = postCallSupported;
+ }
+
+ /**
+ * Checks whether shared map is supported.
+ */
+ public boolean isSharedMapSupported() {
+ return mSharedMapSupported;
+ }
+
+ /**
+ * Sets shared map as supported or not supported.
+ */
+ public void setSharedMapSupported(boolean sharedMapSupported) {
+ this.mSharedMapSupported = sharedMapSupported;
+ }
+
+ /**
+ * Checks whether shared sketch is supported.
+ */
+ public boolean isSharedSketchSupported() {
+ return mSharedSketchSupported;
+ }
+
+ /**
+ * Sets shared sketch as supported or not supported.
+ */
+ public void setSharedSketchSupported(boolean sharedSketchSupported) {
+ this.mSharedSketchSupported = sharedSketchSupported;
+ }
+
+ /**
+ * Checks whether chatbot communication is supported.
+ */
+ public boolean isChatbotSupported() {
+ return mChatbotSupported;
+ }
+
+ /**
+ * Sets chatbot communication as supported or not supported.
+ */
+ public void setChatbotSupported(boolean chatbotSupported) {
+ this.mChatbotSupported = chatbotSupported;
+ }
+
+ /**
+ * Checks whether chatbot role is supported.
+ */
+ public boolean isChatbotRoleSupported() {
+ return mChatbotRoleSupported;
+ }
+
+ /**
+ * Sets chatbot role as supported or not supported.
+ */
+ public void setChatbotRoleSupported(boolean chatbotRoleSupported) {
+ this.mChatbotRoleSupported = chatbotRoleSupported;
+ }
+
/** Gets the list of supported extensions. */
public String[] getExts() {
return mExts;
@@ -434,6 +546,13 @@
dest.writeInt(mGeoPushSupported ? 1 : 0);
dest.writeInt(mSmSupported ? 1 : 0);
dest.writeInt(mFullSnFGroupChatSupported ? 1 : 0);
+ dest.writeInt(mGeoSmsSupported ? 1 : 0);
+ dest.writeInt(mCallComposerSupported ? 1 : 0);
+ dest.writeInt(mPostCallSupported ? 1 : 0);
+ dest.writeInt(mSharedMapSupported ? 1 : 0);
+ dest.writeInt(mSharedSketchSupported ? 1 : 0);
+ dest.writeInt(mChatbotSupported ? 1 : 0);
+ dest.writeInt(mChatbotRoleSupported ? 1 : 0);
dest.writeInt(mRcsIpVoiceCallSupported ? 1 : 0);
dest.writeInt(mRcsIpVideoCallSupported ? 1 : 0);
@@ -476,6 +595,13 @@
mGeoPushSupported = (source.readInt() == 0) ? false : true;
mSmSupported = (source.readInt() == 0) ? false : true;
mFullSnFGroupChatSupported = (source.readInt() == 0) ? false : true;
+ mGeoSmsSupported = (source.readInt() == 0) ? false : true;
+ mCallComposerSupported = (source.readInt() == 0) ? false : true;
+ mPostCallSupported = (source.readInt() == 0) ? false : true;
+ mSharedMapSupported = (source.readInt() == 0) ? false : true;
+ mSharedSketchSupported = (source.readInt() == 0) ? false : true;
+ mChatbotSupported = (source.readInt() == 0) ? false : true;
+ mChatbotRoleSupported = (source.readInt() == 0) ? false : true;
mRcsIpVoiceCallSupported = (source.readInt() == 0) ? false : true;
mRcsIpVideoCallSupported = (source.readInt() == 0) ? false : true;
diff --git a/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
index a50a22f..fdff86f 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
@@ -47,6 +47,10 @@
public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_IWLAN = 8;
/** Trigger is unknown. */
public static final int UCE_PRES_PUBLISH_TRIGGER_UNKNOWN = 9;
+ /** Move to 5G NR with VoPS disabled. */
+ public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10;
+ /** Move to 5G NR with VoPS enabled. */
+ public static final int UCE_PRES_PUBLISH_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11;
@@ -113,4 +117,4 @@
public void readFromParcel(Parcel source) {
mPublishTriggerType = source.readInt();
}
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 9e23c28..9fbc1b7 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -350,7 +350,12 @@
* (boolean) Whether screenshot flow going to the corner (instead of shown in a notification)
* is enabled.
*/
- public static final String SCREENSHOT_CORNER_FLOW = "screenshot_corner_flow";
+ public static final String SCREENSHOT_CORNER_FLOW = "enable_screenshot_corner_flow";
+
+ /**
+ * (boolean) Whether scrolling screenshots are enabled.
+ */
+ public static final String SCREENSHOT_SCROLLING_ENABLED = "enable_screenshot_scrolling";
private SystemUiDeviceConfigFlags() {
}
diff --git a/core/java/com/android/internal/util/ConnectivityUtil.java b/core/java/com/android/internal/util/ConnectivityUtil.java
new file mode 100644
index 0000000..799352b
--- /dev/null
+++ b/core/java/com/android/internal/util/ConnectivityUtil.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+
+/**
+ * Utility methods for common functionality using by different networks.
+ *
+ * @hide
+ */
+public class ConnectivityUtil {
+
+ private static final String TAG = "ConnectivityUtil";
+
+ private final Context mContext;
+ private final AppOpsManager mAppOps;
+ private final UserManager mUserManager;
+
+ public ConnectivityUtil(Context context) {
+ mContext = context;
+ mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ }
+
+ /**
+ * API to determine if the caller has fine/coarse location permission (depending on
+ * config/targetSDK level) and the location mode is enabled for the user. SecurityException is
+ * thrown if the caller has no permission or the location mode is disabled.
+ * @param pkgName package name of the application requesting access
+ * @param featureId The feature in the package
+ * @param uid The uid of the package
+ * @param message A message describing why the permission was checked. Only needed if this is
+ * not inside of a two-way binder call from the data receiver
+ */
+ public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid,
+ @Nullable String message)
+ throws SecurityException {
+ checkPackage(uid, pkgName);
+
+ // Location mode must be enabled
+ if (!isLocationModeEnabled()) {
+ // Location mode is disabled, scan results cannot be returned
+ throw new SecurityException("Location mode is disabled for the device");
+ }
+
+ // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
+ // location information.
+ boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, featureId,
+ uid, /* coarseForTargetSdkLessThanQ */ true, message);
+
+ // If neither caller or app has location access, there is no need to check
+ // any other permissions. Deny access to scan results.
+ if (!canAppPackageUseLocation) {
+ throw new SecurityException("UID " + uid + " has no location permission");
+ }
+ // If the User or profile is current, permission is granted
+ // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+ if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) {
+ throw new SecurityException("UID " + uid + " profile not permitted");
+ }
+ }
+
+ /**
+ * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or
+ * android.Manifest.permission.ACCESS_COARSE_LOCATION (depending on config/targetSDK level)
+ * and a corresponding app op is allowed for this package and uid.
+ *
+ * @param pkgName PackageName of the application requesting access
+ * @param featureId The feature in the package
+ * @param uid The uid of the package
+ * @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE
+ * else (false or targetSDK >= Q) then will check for FINE
+ * @param message A message describing why the permission was checked. Only needed if this is
+ * not inside of a two-way binder call from the data receiver
+ */
+ public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId,
+ int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) {
+ boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
+
+ String permissionType = Manifest.permission.ACCESS_FINE_LOCATION;
+ if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
+ // Having FINE permission implies having COARSE permission (but not the reverse)
+ permissionType = Manifest.permission.ACCESS_COARSE_LOCATION;
+ }
+ if (getUidPermission(permissionType, uid)
+ == PackageManager.PERMISSION_DENIED) {
+ return false;
+ }
+
+ // Always checking FINE - even if will not enforce. This will record the request for FINE
+ // so that a location request by the app is surfaced to the user.
+ boolean isFineLocationAllowed = noteAppOpAllowed(
+ AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid, message);
+ if (isFineLocationAllowed) {
+ return true;
+ }
+ if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
+ return noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName, featureId, uid,
+ message);
+ }
+ return false;
+ }
+
+ /**
+ * Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
+ */
+ public boolean isLocationModeEnabled() {
+ LocationManager locationManager =
+ (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+ try {
+ return locationManager.isLocationEnabledForUser(UserHandle.of(
+ getCurrentUser()));
+ } catch (Exception e) {
+ Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
+ return false;
+ }
+ }
+
+ private boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (mContext.getPackageManager().getApplicationInfoAsUser(
+ packageName, 0,
+ UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion
+ < versionCode) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // In case of exception, assume unknown app (more strict checking)
+ // Note: This case will never happen since checkPackage is
+ // called to verify validity before checking App's version.
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return false;
+ }
+
+ private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId,
+ int uid, @Nullable String message) {
+ return mAppOps.noteOp(op, uid, pkgName, featureId, message) == AppOpsManager.MODE_ALLOWED;
+ }
+
+ private void checkPackage(int uid, String pkgName) throws SecurityException {
+ if (pkgName == null) {
+ throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
+ }
+ mAppOps.checkPackage(uid, pkgName);
+ }
+
+ private boolean isCurrentProfile(int uid) {
+ UserHandle currentUser = UserHandle.of(getCurrentUser());
+ UserHandle callingUser = UserHandle.getUserHandleForUid(uid);
+ return currentUser.equals(callingUser)
+ || mUserManager.isSameProfileGroup(currentUser, callingUser);
+ }
+
+ private boolean checkInteractAcrossUsersFull(int uid) {
+ return getUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @VisibleForTesting
+ protected int getCurrentUser() {
+ return ActivityManager.getCurrentUser();
+ }
+
+ private int getUidPermission(String permissionType, int uid) {
+ // We don't care about pid, pass in -1
+ return mContext.checkPermission(permissionType, -1, uid);
+ }
+}
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index 43a227a..d7a01c4 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -2011,13 +2011,27 @@
}
if (!dispatchNestedPreFling(velocityX, velocityY)) {
- final boolean canScroll = canScrollHorizontal || canScrollVertical;
- dispatchNestedFling(velocityX, velocityY, canScroll);
+ final View firstChild = mLayout.getChildAt(0);
+ final View lastChild = mLayout.getChildAt(mLayout.getChildCount() - 1);
+ boolean consumed = false;
+ if (velocityY < 0) {
+ consumed = getChildAdapterPosition(firstChild) > 0
+ || firstChild.getTop() < getPaddingTop();
+ }
+
+ if (velocityY > 0) {
+ consumed = getChildAdapterPosition(lastChild) < mAdapter.getItemCount() - 1
+ || lastChild.getBottom() > getHeight() - getPaddingBottom();
+ }
+
+ dispatchNestedFling(velocityX, velocityY, consumed);
if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
return true;
}
+ final boolean canScroll = canScrollHorizontal || canScrollVertical;
+
if (canScroll) {
velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 13bfc1b..31b5e49 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -44,21 +44,21 @@
import com.android.internal.util.XmlUtils;
import com.android.server.DropboxLogTags;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
/**
* Performs a number of miscellaneous, non-system-critical actions
* after the system has finished booting.
@@ -424,7 +424,23 @@
for (String propPostfix : MOUNT_DURATION_PROPS_POSTFIX) {
int duration = SystemProperties.getInt("ro.boottime.init.mount_all." + propPostfix, 0);
if (duration != 0) {
- MetricsLogger.histogram(null, "boot_mount_all_duration_" + propPostfix, duration);
+ int eventType;
+ switch (propPostfix) {
+ case "early":
+ eventType = StatsLog.BOOT_TIME_EVENT_DURATION__EVENT__MOUNT_EARLY_DURATION;
+ break;
+ case "default":
+ eventType =
+ StatsLog.BOOT_TIME_EVENT_DURATION__EVENT__MOUNT_DEFAULT_DURATION;
+ break;
+ case "late":
+ eventType = StatsLog.BOOT_TIME_EVENT_DURATION__EVENT__MOUNT_LATE_DURATION;
+ break;
+ default:
+ continue;
+ }
+ StatsLog.write(StatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, eventType,
+ duration);
}
}
}
@@ -555,16 +571,19 @@
Pattern pattern = Pattern.compile(LAST_SHUTDOWN_TIME_PATTERN, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(lines);
if (matcher.find()) {
- MetricsLogger.histogram(null, "boot_fs_shutdown_duration",
+ StatsLog.write(StatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ StatsLog.BOOT_TIME_EVENT_DURATION__EVENT__SHUTDOWN_DURATION,
Integer.parseInt(matcher.group(1)));
- MetricsLogger.histogram(null, "boot_fs_shutdown_umount_stat",
+ StatsLog.write(StatsLog.BOOT_TIME_EVENT_ERROR_CODE_REPORTED,
+ StatsLog.BOOT_TIME_EVENT_ERROR_CODE__EVENT__SHUTDOWN_UMOUNT_STAT,
Integer.parseInt(matcher.group(2)));
Slog.i(TAG, "boot_fs_shutdown," + matcher.group(1) + "," + matcher.group(2));
} else { // not found
// This can happen when a device has too much kernel log after file system unmount
// ,exceeding maxReadSize. And having that much kernel logging can affect overall
// performance as well. So it is better to fix the kernel to reduce the amount of log.
- MetricsLogger.histogram(null, "boot_fs_shutdown_umount_stat",
+ StatsLog.write(StatsLog.BOOT_TIME_EVENT_ERROR_CODE_REPORTED,
+ StatsLog.BOOT_TIME_EVENT_ERROR_CODE__EVENT__SHUTDOWN_UMOUNT_STAT,
UMOUNT_STATUS_NOT_AVAILABLE);
Slog.w(TAG, "boot_fs_shutdown, string not found");
}
@@ -674,7 +693,11 @@
return;
}
stat = fixFsckFsStat(partition, stat, lines, startLineNumber, endLineNumber);
- MetricsLogger.histogram(null, "boot_fs_stat_" + partition, stat);
+ if ("userdata".equals(partition) || "data".equals(partition)) {
+ StatsLog.write(StatsLog.BOOT_TIME_EVENT_ERROR_CODE_REPORTED,
+ StatsLog.BOOT_TIME_EVENT_ERROR_CODE__EVENT__FS_MGR_FS_STAT_DATA_PARTITION,
+ stat);
+ }
Slog.i(TAG, "fs_stat, partition:" + partition + " stat:0x" + Integer.toHexString(stat));
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 6e6746f..a2f514a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -344,7 +344,6 @@
cppflags: ["-Wno-conversion-null"],
srcs: [
- "android/graphics/apex/android_bitmap.cpp",
"android/graphics/apex/android_matrix.cpp",
"android/graphics/apex/android_paint.cpp",
"android/graphics/apex/android_region.cpp",
@@ -430,6 +429,7 @@
android: {
srcs: [ // sources that depend on android only libraries
"android/graphics/apex/android_canvas.cpp",
+ "android/graphics/apex/android_bitmap.cpp",
"android/graphics/apex/renderthread.cpp",
"android/graphics/apex/jni_runtime.cpp",
diff --git a/core/jni/android/graphics/apex/android_bitmap.cpp b/core/jni/android/graphics/apex/android_bitmap.cpp
index 90cc986..6a3c01e 100644
--- a/core/jni/android/graphics/apex/android_bitmap.cpp
+++ b/core/jni/android/graphics/apex/android_bitmap.cpp
@@ -122,6 +122,98 @@
return getInfo(bitmap->info(), bitmap->rowBytes());
}
+static bool nearlyEqual(float a, float b) {
+ // By trial and error, this is close enough to match for the ADataSpaces we
+ // compare for.
+ return ::fabs(a - b) < .002f;
+}
+
+static bool nearlyEqual(const skcms_TransferFunction& x, const skcms_TransferFunction& y) {
+ return nearlyEqual(x.g, y.g)
+ && nearlyEqual(x.a, y.a)
+ && nearlyEqual(x.b, y.b)
+ && nearlyEqual(x.c, y.c)
+ && nearlyEqual(x.d, y.d)
+ && nearlyEqual(x.e, y.e)
+ && nearlyEqual(x.f, y.f);
+}
+
+static bool nearlyEqual(const skcms_Matrix3x3& x, const skcms_Matrix3x3& y) {
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ if (!nearlyEqual(x.vals[i][j], y.vals[i][j])) return false;
+ }
+ }
+ return true;
+}
+
+static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
+
+// Skia's SkNamedGamut::kDCIP3 is based on a white point of D65. This gamut
+// matches the white point used by ColorSpace.Named.DCIP3.
+static constexpr skcms_Matrix3x3 kDCIP3 = {{
+ {0.486143, 0.323835, 0.154234},
+ {0.226676, 0.710327, 0.0629966},
+ {0.000800549, 0.0432385, 0.78275},
+}};
+
+ADataSpace ABitmap_getDataSpace(ABitmap* bitmapHandle) {
+ Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+ const SkImageInfo& info = bitmap->info();
+ SkColorSpace* colorSpace = info.colorSpace();
+ if (!colorSpace) {
+ return ADATASPACE_UNKNOWN;
+ }
+
+ if (colorSpace->isSRGB()) {
+ if (info.colorType() == kRGBA_F16_SkColorType) {
+ return ADATASPACE_SCRGB;
+ }
+ return ADATASPACE_SRGB;
+ }
+
+ skcms_TransferFunction fn;
+ LOG_ALWAYS_FATAL_IF(!colorSpace->isNumericalTransferFn(&fn));
+
+ skcms_Matrix3x3 gamut;
+ LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&gamut));
+
+ if (nearlyEqual(gamut, SkNamedGamut::kSRGB)) {
+ if (nearlyEqual(fn, SkNamedTransferFn::kLinear)) {
+ // Skia doesn't differentiate amongst the RANGES. In Java, we associate
+ // LINEAR_EXTENDED_SRGB with F16, and LINEAR_SRGB with other Configs.
+ // Make the same association here.
+ if (info.colorType() == kRGBA_F16_SkColorType) {
+ return ADATASPACE_SCRGB_LINEAR;
+ }
+ return ADATASPACE_SRGB_LINEAR;
+ }
+
+ if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) {
+ return ADATASPACE_BT709;
+ }
+ }
+
+ if (nearlyEqual(fn, SkNamedTransferFn::kSRGB) && nearlyEqual(gamut, SkNamedGamut::kDCIP3)) {
+ return ADATASPACE_DISPLAY_P3;
+ }
+
+ if (nearlyEqual(fn, SkNamedTransferFn::k2Dot2) && nearlyEqual(gamut, SkNamedGamut::kAdobeRGB)) {
+ return ADATASPACE_ADOBE_RGB;
+ }
+
+ if (nearlyEqual(fn, SkNamedTransferFn::kRec2020) &&
+ nearlyEqual(gamut, SkNamedGamut::kRec2020)) {
+ return ADATASPACE_BT2020;
+ }
+
+ if (nearlyEqual(fn, k2Dot6) && nearlyEqual(gamut, kDCIP3)) {
+ return ADATASPACE_DCI_P3;
+ }
+
+ return ADATASPACE_UNKNOWN;
+}
+
AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj) {
uint32_t rowBytes = 0;
SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes);
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 f231eed..32b8a45 100644
--- a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
+++ b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
@@ -17,6 +17,7 @@
#define ANDROID_GRAPHICS_BITMAP_H
#include <android/bitmap.h>
+#include <android/data_space.h>
#include <jni.h>
#include <sys/cdefs.h>
@@ -49,6 +50,7 @@
void ABitmap_releaseRef(ABitmap* bitmap);
AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap);
+ADataSpace ABitmap_getDataSpace(ABitmap* bitmap);
void* ABitmap_getPixels(ABitmap* bitmap);
void ABitmap_notifyPixelsChanged(ABitmap* bitmap);
@@ -106,6 +108,7 @@
ABitmap* get() const { return mBitmap; }
AndroidBitmapInfo getInfo() const { return ABitmap_getInfo(mBitmap); }
+ ADataSpace getDataSpace() const { return ABitmap_getDataSpace(mBitmap); }
void* getPixels() const { return ABitmap_getPixels(mBitmap); }
void notifyPixelsChanged() const { ABitmap_notifyPixelsChanged(mBitmap); }
@@ -119,4 +122,4 @@
}; // namespace android
#endif // __cplusplus
-#endif // ANDROID_GRAPHICS_BITMAP_H
\ No newline at end of file
+#endif // ANDROID_GRAPHICS_BITMAP_H
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index ff596b4..062b886 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -220,8 +220,12 @@
// ----------------------------------------------------------------------------
+static std::unique_ptr<DynamicLibManager> sDynamicLibManager =
+ std::make_unique<DynamicLibManager>();
+
// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance.
struct GuardedAssetManager : public ::AAssetManager {
+ GuardedAssetManager() : guarded_assetmanager(sDynamicLibManager.get()) {}
Guarded<AssetManager2> guarded_assetmanager;
};
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 0992beb..fb8e633 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -537,9 +537,9 @@
LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
if (mObject != NULL) {
JNIEnv* env = javavm_to_jnienv(mVM);
-
+ jobject jBinderProxy = javaObjectForIBinder(env, who.promote());
env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
- gBinderProxyOffsets.mSendDeathNotice, mObject);
+ gBinderProxyOffsets.mSendDeathNotice, mObject, jBinderProxy);
if (env->ExceptionCheck()) {
jthrowable excep = env->ExceptionOccurred();
report_exception(env, excep,
@@ -1532,8 +1532,9 @@
gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, clazz, "getInstance",
"(JJ)Landroid/os/BinderProxy;");
- gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
- "(Landroid/os/IBinder$DeathRecipient;)V");
+ gBinderProxyOffsets.mSendDeathNotice =
+ GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
+ "(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
clazz = FindClassOrDie(env, "java/lang/Class");
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 59dab0c8..649e5d2 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -40,10 +40,15 @@
static const bool kDebugDispatchCycle = false;
+static const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
static struct {
jclass clazz;
jmethodID dispatchInputEvent;
+ jmethodID onFocusEvent;
jmethodID dispatchBatchedInputEventPending;
} gInputEventReceiverClassInfo;
@@ -219,8 +224,7 @@
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64,
- getInputChannelName().c_str(),
- consumeBatches ? "true" : "false", frameTime);
+ getInputChannelName().c_str(), toString(consumeBatches), frameTime);
}
if (consumeBatches) {
@@ -235,6 +239,7 @@
for (;;) {
uint32_t seq;
InputEvent* inputEvent;
+
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
if (status) {
@@ -302,6 +307,19 @@
inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
break;
}
+ case AINPUT_EVENT_TYPE_FOCUS: {
+ FocusEvent* focusEvent = static_cast<FocusEvent*>(inputEvent);
+ if (kDebugDispatchCycle) {
+ ALOGD("channel '%s' ~ Received focus event: hasFocus=%s, inTouchMode=%s.",
+ getInputChannelName().c_str(), toString(focusEvent->getHasFocus()),
+ toString(focusEvent->getInTouchMode()));
+ }
+ env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.onFocusEvent,
+ jboolean(focusEvent->getHasFocus()),
+ jboolean(focusEvent->getInTouchMode()));
+ finishInputEvent(seq, true /* handled */);
+ return OK;
+ }
default:
assert(false); // InputConsumer should prevent this from ever happening
@@ -421,6 +439,8 @@
gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env,
gInputEventReceiverClassInfo.clazz,
"dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
+ gInputEventReceiverClassInfo.onFocusEvent =
+ GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(ZZ)V");
gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env,
gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 4a7276c..b47080f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -263,7 +263,8 @@
status_t res = ScreenshotClient::capture(displayToken, dataspace,
ui::PixelFormat::RGBA_8888,
sourceCrop, width, height,
- useIdentityTransform, rotation, captureSecureLayers, &buffer, capturedSecureLayers);
+ useIdentityTransform, ui::toRotation(rotation),
+ captureSecureLayers, &buffer, capturedSecureLayers);
if (res != NO_ERROR) {
return NULL;
}
@@ -724,7 +725,8 @@
{
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- transaction->setDisplayProjection(token, orientation, layerStackRect, displayRect);
+ transaction->setDisplayProjection(token, static_cast<ui::Rotation>(orientation),
+ layerStackRect, displayRect);
}
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5039213..673772a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -746,9 +746,6 @@
const userid_t user_id = multiuser_get_user_id(uid);
const std::string user_source = StringPrintf("/mnt/user/%d", user_id);
- const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
- bool isFuse = GetBoolProperty(kPropFuse, false);
-
// Shell is neither AID_ROOT nor AID_EVERYBODY. Since it equally needs 'execute' access to
// /mnt/user/0 to 'adb shell ls /sdcard' for instance, we set the uid bit of /mnt/user/0 to
// AID_SHELL. This gives shell access along with apps running as group everybody (user 0 apps)
@@ -757,9 +754,15 @@
PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL,
multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
+ bool isFuse = GetBoolProperty(kPropFuse, false);
+
if (isFuse) {
if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
+ const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
BindMount(pass_through_source, "/storage", fail_fn);
+ } else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
+ const std::string installer_source = StringPrintf("/mnt/installer/%d", user_id);
+ BindMount(installer_source, "/storage", fail_fn);
} else {
BindMount(user_source, "/storage", fail_fn);
}
@@ -1153,6 +1156,36 @@
createAndMountAppData(package_name, ce_data_path, mirrorCePath, actualCePath, fail_fn);
}
+// Relabel directory
+static void relabelDir(const char* path, security_context_t context, fail_fn_t fail_fn) {
+ if (setfilecon(path, context) != 0) {
+ fail_fn(CREATE_ERROR("Failed to setfilecon %s %s", path, strerror(errno)));
+ }
+}
+
+// Relabel all directories under a path non-recursively.
+static void relabelAllDirs(const char* path, security_context_t context, fail_fn_t fail_fn) {
+ DIR* dir = opendir(path);
+ if (dir == nullptr) {
+ fail_fn(CREATE_ERROR("Failed to opendir %s", path));
+ }
+ struct dirent* ent;
+ while ((ent = readdir(dir))) {
+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue;
+ auto filePath = StringPrintf("%s/%s", path, ent->d_name);
+ if (ent->d_type == DT_DIR) {
+ relabelDir(filePath.c_str(), context, fail_fn);
+ } else if (ent->d_type == DT_LNK) {
+ if (lsetfilecon(filePath.c_str(), context) != 0) {
+ fail_fn(CREATE_ERROR("Failed to lsetfilecon %s %s", filePath.c_str(), strerror(errno)));
+ }
+ } else {
+ fail_fn(CREATE_ERROR("Unexpected type: %d %s", ent->d_type, filePath.c_str()));
+ }
+ }
+ closedir(dir);
+}
+
/**
* Make other apps data directory not visible in CE, DE storage.
*
@@ -1212,10 +1245,36 @@
snprintf(internalDePath, PATH_MAX, "/data/user_de");
snprintf(externalPrivateMountPath, PATH_MAX, "/mnt/expand");
+ security_context_t dataDataContext = nullptr;
+ if (getfilecon(internalDePath, &dataDataContext) < 0) {
+ fail_fn(CREATE_ERROR("Unable to getfilecon on %s %s", internalDePath,
+ strerror(errno)));
+ }
+
MountAppDataTmpFs(internalLegacyCePath, fail_fn);
MountAppDataTmpFs(internalCePath, fail_fn);
MountAppDataTmpFs(internalDePath, fail_fn);
- MountAppDataTmpFs(externalPrivateMountPath, fail_fn);
+
+ // Mount tmpfs on all external vols DE and CE storage
+ DIR* dir = opendir(externalPrivateMountPath);
+ if (dir == nullptr) {
+ fail_fn(CREATE_ERROR("Failed to opendir %s", externalPrivateMountPath));
+ }
+ struct dirent* ent;
+ while ((ent = readdir(dir))) {
+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue;
+ if (ent->d_type != DT_DIR) {
+ fail_fn(CREATE_ERROR("Unexpected type: %d %s", ent->d_type, ent->d_name));
+ }
+ auto volPath = StringPrintf("%s/%s", externalPrivateMountPath, ent->d_name);
+ auto cePath = StringPrintf("%s/user", volPath.c_str());
+ auto dePath = StringPrintf("%s/user_de", volPath.c_str());
+ MountAppDataTmpFs(cePath.c_str(), fail_fn);
+ MountAppDataTmpFs(dePath.c_str(), fail_fn);
+ }
+ closedir(dir);
+
+ bool legacySymlinkCreated = false;
for (int i = 0; i < size; i += 3) {
jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i));
@@ -1263,7 +1322,14 @@
// If it's user 0, create a symlink /data/user/0 -> /data/data,
// otherwise create /data/user/$USER
if (userId == 0) {
- symlink(internalLegacyCePath, internalCeUserPath);
+ if (!legacySymlinkCreated) {
+ legacySymlinkCreated = true;
+ int result = symlink(internalLegacyCePath, internalCeUserPath);
+ if (result != 0) {
+ fail_fn(CREATE_ERROR("Failed to create symlink %s %s", internalCeUserPath,
+ strerror(errno)));
+ }
+ }
actualCePath = internalLegacyCePath;
} else {
PrepareDirIfNotPresent(internalCeUserPath, DEFAULT_DATA_DIR_PERMISSION,
@@ -1277,6 +1343,43 @@
isolateAppDataPerPackage(userId, packageName, volUuid, ceDataInode,
actualCePath, actualDePath, fail_fn);
}
+ // We set the label AFTER everything is done, as we are applying
+ // the file operations on tmpfs. If we set the label when we mount
+ // tmpfs, SELinux will not happy as we are changing system_data_files.
+ // Relabel dir under /data/user, including /data/user/0
+ relabelAllDirs(internalCePath, dataDataContext, fail_fn);
+
+ // Relabel /data/user
+ relabelDir(internalCePath, dataDataContext, fail_fn);
+
+ // Relabel /data/data
+ relabelDir(internalLegacyCePath, dataDataContext, fail_fn);
+
+ // Relabel dir under /data/user_de
+ relabelAllDirs(internalDePath, dataDataContext, fail_fn);
+
+ // Relabel /data/user_de
+ relabelDir(internalDePath, dataDataContext, fail_fn);
+
+ // Relabel CE and DE dirs under /mnt/expand
+ dir = opendir(externalPrivateMountPath);
+ if (dir == nullptr) {
+ fail_fn(CREATE_ERROR("Failed to opendir %s", externalPrivateMountPath));
+ }
+ while ((ent = readdir(dir))) {
+ if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue;
+ auto volPath = StringPrintf("%s/%s", externalPrivateMountPath, ent->d_name);
+ auto cePath = StringPrintf("%s/user", volPath.c_str());
+ auto dePath = StringPrintf("%s/user_de", volPath.c_str());
+
+ relabelAllDirs(cePath.c_str(), dataDataContext, fail_fn);
+ relabelDir(cePath.c_str(), dataDataContext, fail_fn);
+ relabelAllDirs(dePath.c_str(), dataDataContext, fail_fn);
+ relabelDir(dePath.c_str(), dataDataContext, fail_fn);
+ }
+ closedir(dir);
+
+ freecon(dataDataContext);
}
// Utility routine to specialize a zygote child process.
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 06040a5..b71e539 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -32,8 +32,8 @@
import "frameworks/base/core/proto/android/server/job/enums.proto";
import "frameworks/base/core/proto/android/server/statlogger.proto";
import "frameworks/base/core/proto/android/privacy.proto";
+import "frameworks/base/core/proto/android/util/quotatracker.proto";
-// Next tag: 21
message JobSchedulerServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -160,10 +160,13 @@
optional JobConcurrencyManagerProto concurrency_manager = 20;
optional JobStorePersistStatsProto persist_stats = 21;
+
+ optional .android.util.quota.CountQuotaTrackerProto quota_tracker = 22;
+
+ // Next tag: 23
}
// A com.android.server.job.JobSchedulerService.Constants object.
-// Next tag: 29
message ConstantsProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -246,6 +249,14 @@
// Whether to use heartbeats or rolling window for quota management. True
// will use heartbeats, false will use a rolling window.
reserved 23; // use_heartbeats
+ // Whether to enable quota limits on APIs.
+ optional bool enable_api_quotas = 31;
+ // The maximum number of schedule() calls an app can make in a set amount of time.
+ optional int32 api_quota_schedule_count = 32;
+ // The time window that {@link #API_QUOTA_SCHEDULE_COUNT} should be evaluated over.
+ optional int64 api_quota_schedule_window_ms = 33;
+ // Whether or not to throw an exception when an app hits its schedule quota limit.
+ optional bool api_quota_schedule_throw_exception = 34;
message QuotaController {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -331,7 +342,7 @@
// In this time after screen turns on, we increase job concurrency.
optional int32 screen_off_job_concurrency_increase_delay_ms = 28;
- // Next tag: 31
+ // Next tag: 35
}
// Next tag: 4
@@ -651,8 +662,7 @@
optional bool is_active = 2;
// The time this timer last became active. Only valid if is_active is true.
optional int64 start_time_elapsed = 3;
- // How many background jobs are currently running. Valid only if the device is_active
- // is true.
+ // How many background jobs are currently running. Valid only if is_active is true.
optional int32 bg_job_count = 4;
// All of the jobs that the Timer is currently tracking.
repeated JobStatusShortInfoProto running_jobs = 5;
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 9054d54..0fca1d1 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -154,4 +154,5 @@
SET_AUTO_TIME = 127;
SET_AUTO_TIME_ZONE = 128;
SET_PACKAGES_PROTECTED = 129;
+ SET_FACTORY_RESET_PROTECTION = 130;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c050146..7ecd9b7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -445,6 +445,7 @@
<protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
<protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" />
+ <protected-broadcast android:name="android.app.action.RESET_PROTECTION_POLICY_CHANGED" />
<protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
<protected-broadcast android:name="android.app.action.MANAGED_USER_CREATED" />
@@ -640,10 +641,6 @@
<protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
- <!-- NETWORK_SET_TIME moved from com.android.phone to system server. It should ultimately be
- removed. -->
- <protected-broadcast android:name="android.telephony.action.NETWORK_SET_TIME" />
-
<!-- For tether entitlement recheck-->
<protected-broadcast
android:name="com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM" />
@@ -1791,6 +1788,11 @@
android:label="@string/permlab_preferredPaymentInfo"
android:protectionLevel="normal" />
+ <!-- @SystemApi Allows an internal user to use privileged SecureElement APIs.
+ @hide -->
+ <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
<!-- @deprecated This permission used to allow too broad access to sensitive methods and all its
uses have been replaced by a more appropriate permission. Most uses have been replaced with
a NETWORK_STACK or NETWORK_SETTINGS check. Please look up the documentation of the
@@ -2566,7 +2568,7 @@
<!-- Allows telephony to suggest the time / time zone.
<p>Not for use by third-party applications.
- @hide
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @hide
-->
<permission android:name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE"
android:protectionLevel="signature|telephony" />
@@ -3384,6 +3386,14 @@
<permission android:name="android.permission.NOTIFY_TV_INPUTS"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an application to interact with tuner resources through
+ Tuner Resource Manager.
+ <p>Protection level: signature|privileged
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.TUNER_RESOURCE_ACCESS"
+ android:protectionLevel="signature|privileged" />
+
<!-- Must be required by a {@link android.media.routing.MediaRouteService}
to ensure that only the system can interact with it.
@hide -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6691d4c..6f554f02 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1877,6 +1877,8 @@
<string name="config_defaultCallRedirection" translatable="false"></string>
<!-- The name of the package that will hold the call screening role by default. -->
<string name="config_defaultCallScreening" translatable="false"></string>
+ <!-- The name of the package that will hold the system gallery role. -->
+ <string name="config_systemGallery" translatable="false">com.android.gallery</string>
<!-- Enable/disable default bluetooth profiles:
HSP_AG, ObexObjectPush, Audio, NAP -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index bf7558c..d437aa1 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -777,4 +777,9 @@
<!-- Assistant handles -->
<dimen name="assist_handle_shadow_radius">2dp</dimen>
+ <!-- For Waterfall Display -->
+ <dimen name="waterfall_display_left_edge_size">0px</dimen>
+ <dimen name="waterfall_display_top_edge_size">0px</dimen>
+ <dimen name="waterfall_display_right_edge_size">0px</dimen>
+ <dimen name="waterfall_display_bottom_edge_size">0px</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 37c9710..6cf6a68 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3030,6 +3030,8 @@
<public name="config_defaultCallRedirection" />
<!-- @hide @SystemApi -->
<public name="config_defaultCallScreening" />
+ <!-- @hide @SystemApi @TestApi -->
+ <public name="config_systemGallery" />
</public-group>
<public-group type="bool" first-id="0x01110005">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9e11749..f01c2e8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3801,5 +3801,9 @@
<!-- Assistant handles -->
<java-symbol type="dimen" name="assist_handle_shadow_radius" />
-
+ <!-- For Waterfall Display -->
+ <java-symbol type="dimen" name="waterfall_display_left_edge_size" />
+ <java-symbol type="dimen" name="waterfall_display_top_edge_size" />
+ <java-symbol type="dimen" name="waterfall_display_right_edge_size" />
+ <java-symbol type="dimen" name="waterfall_display_bottom_edge_size" />
</resources>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index caae908..2df6d1c 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -8,6 +8,7 @@
"EnabledTestApp/src/**/*.java",
"BinderProxyCountingTestApp/src/**/*.java",
"BinderProxyCountingTestService/src/**/*.java",
+ "BinderDeathRecipientHelperApp/src/**/*.java",
"aidl/**/I*.aidl",
],
@@ -59,7 +60,11 @@
resource_dirs: ["res"],
resource_zips: [":FrameworksCoreTests_apks_as_resources"],
- data: [":BstatsTestApp"],
+ data: [
+ ":BstatsTestApp",
+ ":BinderDeathRecipientHelperApp1",
+ ":BinderDeathRecipientHelperApp2",
+ ],
}
// Rules to copy all the test apks to the intermediate raw resource directory
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 1aea98a..b85a332 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -95,6 +95,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.KILL_UID" />
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index b40aa87..ed9d3f5 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -21,6 +21,8 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="FrameworksCoreTests.apk" />
<option name="test-file-name" value="BstatsTestApp.apk" />
+ <option name="test-file-name" value="BinderDeathRecipientHelperApp1.apk" />
+ <option name="test-file-name" value="BinderDeathRecipientHelperApp2.apk" />
</target_preparer>
<option name="test-tag" value="FrameworksCoreTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp b/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp
new file mode 100644
index 0000000..25e4fc3
--- /dev/null
+++ b/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp
@@ -0,0 +1,19 @@
+android_test_helper_app {
+ name: "BinderDeathRecipientHelperApp1",
+
+ srcs: ["**/*.java"],
+
+ sdk_version: "current",
+}
+
+android_test_helper_app {
+ name: "BinderDeathRecipientHelperApp2",
+
+ srcs: ["**/*.java"],
+
+ sdk_version: "current",
+
+ aaptflags: [
+ "--rename-manifest-package com.android.frameworks.coretests.bdr_helper_app2",
+ ],
+}
diff --git a/core/tests/coretests/BinderDeathRecipientHelperApp/AndroidManifest.xml b/core/tests/coretests/BinderDeathRecipientHelperApp/AndroidManifest.xml
new file mode 100644
index 0000000..dbd1774
--- /dev/null
+++ b/core/tests/coretests/BinderDeathRecipientHelperApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.bdr_helper_app1">
+
+ <application>
+ <receiver android:name="com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver"
+ android:exported="true"/>
+
+ </application>
+</manifest>
diff --git a/core/tests/coretests/BinderDeathRecipientHelperApp/src/com/android/frameworks/coretests/bdr_helper_app/TestCommsReceiver.java b/core/tests/coretests/BinderDeathRecipientHelperApp/src/com/android/frameworks/coretests/bdr_helper_app/TestCommsReceiver.java
new file mode 100644
index 0000000..ab79e69
--- /dev/null
+++ b/core/tests/coretests/BinderDeathRecipientHelperApp/src/com/android/frameworks/coretests/bdr_helper_app/TestCommsReceiver.java
@@ -0,0 +1,51 @@
+/*
+ * 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.frameworks.coretests.bdr_helper_app;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Receiver used to hand off a binder owned by this process to
+ * {@link android.os.BinderDeathRecipientTest}.
+ */
+public class TestCommsReceiver extends BroadcastReceiver {
+ private static final String TAG = TestCommsReceiver.class.getSimpleName();
+ private static final String PACKAGE_NAME = "com.android.frameworks.coretests.bdr_helper_app";
+
+ public static final String ACTION_GET_BINDER = PACKAGE_NAME + ".action.GET_BINDER";
+ public static final String EXTRA_KEY_BINDER = PACKAGE_NAME + ".EXTRA_BINDER";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case ACTION_GET_BINDER:
+ final Bundle resultExtras = new Bundle();
+ resultExtras.putBinder(EXTRA_KEY_BINDER, new Binder());
+ setResult(Activity.RESULT_OK, null, resultExtras);
+ break;
+ default:
+ Log.e(TAG, "Unknown action " + intent.getAction());
+ break;
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java
new file mode 100644
index 0000000..0be52c1
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSchemaTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.app.appsearch;
+
+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;
+
+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()
+ ).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)
+ )
+ )
+
+ ).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)
+ )
+ )
+ ).build();
+
+ assertThat(schema.getProto()).isEqualTo(expectedProto);
+ }
+
+ @Test
+ public void testInvalidEnums() {
+ PropertyConfig.Builder builder = AppSearchSchema.newPropertyBuilder("test");
+ assertThrows(IllegalArgumentException.class, () -> builder.setDataType(99));
+ assertThrows(IllegalArgumentException.class, () -> builder.setCardinality(99));
+ }
+
+ @Test
+ public void testMissingFields() {
+ PropertyConfig.Builder builder = AppSearchSchema.newPropertyBuilder("test");
+ Exception e = expectThrows(IllegalSchemaException.class, builder::build);
+ assertThat(e).hasMessageThat().contains("Missing field: dataType");
+
+ builder.setDataType(PropertyConfig.DATA_TYPE_DOCUMENT);
+ e = expectThrows(IllegalSchemaException.class, builder::build);
+ assertThat(e).hasMessageThat().contains("Missing field: schemaType");
+
+ builder.setSchemaType("TestType");
+ e = expectThrows(IllegalSchemaException.class, builder::build);
+ assertThat(e).hasMessageThat().contains("Missing field: cardinality");
+
+ builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED);
+ builder.build();
+ }
+}
diff --git a/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
new file mode 100644
index 0000000..2cce43f
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderDeathRecipientTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests functionality of {@link android.os.IBinder.DeathRecipient} callbacks.
+ */
+@RunWith(AndroidJUnit4.class)
+public class BinderDeathRecipientTest {
+ private static final String TAG = BinderDeathRecipientTest.class.getSimpleName();
+ private static final String TEST_PACKAGE_NAME_1 =
+ "com.android.frameworks.coretests.bdr_helper_app1";
+ private static final String TEST_PACKAGE_NAME_2 =
+ "com.android.frameworks.coretests.bdr_helper_app2";
+
+ private Context mContext;
+ private Handler mHandler;
+ private ActivityManager mActivityManager;
+ private Set<Pair<IBinder, IBinder.DeathRecipient>> mLinkedDeathRecipients = new ArraySet<>();
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mActivityManager = mContext.getSystemService(ActivityManager.class);
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ private IBinder getNewRemoteBinder(String testPackage) throws InterruptedException {
+ final CountDownLatch resultLatch = new CountDownLatch(1);
+ final AtomicInteger resultCode = new AtomicInteger(Activity.RESULT_CANCELED);
+ final AtomicReference<Bundle> resultExtras = new AtomicReference<>();
+
+ final Intent intent = new Intent(TestCommsReceiver.ACTION_GET_BINDER)
+ .setClassName(testPackage, TestCommsReceiver.class.getName());
+ mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ resultCode.set(getResultCode());
+ resultExtras.set(getResultExtras(true));
+ resultLatch.countDown();
+ }
+ }, mHandler, Activity.RESULT_CANCELED, null, null);
+
+ assertTrue("Request for binder timed out", resultLatch.await(5, TimeUnit.SECONDS));
+ assertEquals(Activity.RESULT_OK, resultCode.get());
+ return resultExtras.get().getBinder(TestCommsReceiver.EXTRA_KEY_BINDER);
+ }
+
+ @Test
+ public void binderDied_noArgs() throws Exception {
+ final IBinder testAppBinder = getNewRemoteBinder(TEST_PACKAGE_NAME_1);
+ final CountDownLatch deathNotificationLatch = new CountDownLatch(1);
+ final IBinder.DeathRecipient simpleDeathRecipient =
+ () -> deathNotificationLatch.countDown();
+ testAppBinder.linkToDeath(simpleDeathRecipient, 0);
+ mLinkedDeathRecipients.add(Pair.create(testAppBinder, simpleDeathRecipient));
+
+ mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_1);
+ assertTrue("Death notification did not arrive",
+ deathNotificationLatch.await(10, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void binderDied_iBinderArg() throws Exception {
+ final IBinder testApp1Binder = getNewRemoteBinder(TEST_PACKAGE_NAME_1);
+ final IBinder testApp2Binder = getNewRemoteBinder(TEST_PACKAGE_NAME_2);
+ final CyclicBarrier barrier = new CyclicBarrier(2);
+
+ final AtomicReference<IBinder> binderThatDied = new AtomicReference<>();
+ final IBinder.DeathRecipient sameDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Log.e(TAG, "Should not have been called!");
+ }
+
+ @Override
+ public void binderDied(IBinder who) {
+ binderThatDied.set(who);
+ try {
+ barrier.await();
+ } catch (InterruptedException | BrokenBarrierException e) {
+ Log.e(TAG, "Unexpected exception while waiting on CyclicBarrier", e);
+ }
+ }
+ };
+ testApp1Binder.linkToDeath(sameDeathRecipient, 0);
+ mLinkedDeathRecipients.add(Pair.create(testApp1Binder, sameDeathRecipient));
+
+ testApp2Binder.linkToDeath(sameDeathRecipient, 0);
+ mLinkedDeathRecipients.add(Pair.create(testApp2Binder, sameDeathRecipient));
+
+ mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_1);
+ try {
+ barrier.await(10, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ fail("Timed out while waiting for 1st death notification: " + e.getMessage());
+ }
+ assertEquals("Different binder received", testApp1Binder, binderThatDied.get());
+
+ barrier.reset();
+ mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_2);
+ try {
+ barrier.await(10, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ fail("Timed out while waiting for 2nd death notification: " + e.getMessage());
+ }
+ assertEquals("Different binder received", testApp2Binder, binderThatDied.get());
+ }
+
+ @After
+ public void tearDown() {
+ for (Pair<IBinder, IBinder.DeathRecipient> linkedPair : mLinkedDeathRecipients) {
+ linkedPair.first.unlinkToDeath(linkedPair.second, 0);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 9042514..836cb95 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -124,6 +124,10 @@
public void setSoftKeyboardCallbackEnabled(boolean enabled) {}
+ public boolean switchToInputMethod(String imeId) {
+ return false;
+ }
+
public boolean isAccessibilityButtonAvailable() {
return false;
}
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index 89c2374..8c9b4d0 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -67,6 +67,7 @@
mOriginalFlagValue = Editor.FLAG_ENABLE_CURSOR_DRAG;
Editor.FLAG_ENABLE_CURSOR_DRAG = true;
}
+
@After
public void after() throws Throwable {
Editor.FLAG_ENABLE_CURSOR_DRAG = mOriginalFlagValue;
@@ -356,6 +357,23 @@
assertFalse(editor.getSelectionController().isCursorBeingModified());
}
+ @Test // Reproduces b/147366705
+ public void testCursorDrag_nonSelectableTextView() throws Throwable {
+ String text = "Hello world!";
+ TextView tv = mActivity.findViewById(R.id.nonselectable_textview);
+ tv.setText(text);
+ Editor editor = tv.getEditorForTesting();
+
+ // Simulate a tap. No error should be thrown.
+ long event1Time = 1001;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
+
+ // Swipe left to right. No error should be thrown.
+ onView(withId(R.id.nonselectable_textview)).perform(
+ dragOnText(text.indexOf("llo"), text.indexOf("!")));
+ }
+
private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) {
return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index b411668..aa55e08 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -19,7 +19,6 @@
import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemDisabled;
import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemEnabled;
import static android.widget.espresso.ContextMenuUtils.assertContextMenuIsNotDisplayed;
-import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
import static android.widget.espresso.DragHandleUtils.onHandleView;
import static android.widget.espresso.TextViewActions.mouseClick;
import static android.widget.espresso.TextViewActions.mouseClickOnTextAtIndex;
@@ -64,7 +63,6 @@
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
-@Suppress // Consistently failing. b/29591177
public class TextViewActivityMouseTest {
@Rule
@@ -86,11 +84,8 @@
onView(withId(R.id.textview)).perform(mouseClick());
onView(withId(R.id.textview)).perform(replaceText(helloWorld));
- assertNoSelectionHandles();
-
onView(withId(R.id.textview)).perform(
mouseDragOnText(helloWorld.indexOf("llo"), helloWorld.indexOf("ld!")));
-
onView(withId(R.id.textview)).check(hasSelection("llo wor"));
onHandleView(com.android.internal.R.id.selection_start_handle)
@@ -100,8 +95,6 @@
onView(withId(R.id.textview)).perform(mouseClickOnTextAtIndex(helloWorld.indexOf("w")));
onView(withId(R.id.textview)).check(hasSelection(""));
-
- assertNoSelectionHandles();
}
@Test
@@ -196,11 +189,11 @@
onView(withId(R.id.textview)).check(matches(withText("abc ghi.def")));
onView(withId(R.id.textview)).check(hasSelection(""));
- assertNoSelectionHandles();
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
}
@Test
+ @Suppress // Consistently failing. b/29591177
public void testDragAndDrop_longClick() {
final String text = "abc def ghi.";
onView(withId(R.id.textview)).perform(mouseClick());
@@ -213,7 +206,6 @@
onView(withId(R.id.textview)).check(matches(withText("abc ghi.def")));
onView(withId(R.id.textview)).check(hasSelection(""));
- assertNoSelectionHandles();
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
}
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
index abee736..1928d25 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
@@ -70,6 +70,8 @@
event.setSource(InputDevice.SOURCE_MOUSE);
if (event.getActionMasked() != MotionEvent.ACTION_UP) {
event.setButtonState(mButton);
+ } else {
+ event.setButtonState(0);
}
return mUiController.injectMotionEvent(event);
}
diff --git a/core/tests/overlaytests/remount/TEST_MAPPING b/core/tests/overlaytests/remount/TEST_MAPPING
new file mode 100644
index 0000000..54dd431
--- /dev/null
+++ b/core/tests/overlaytests/remount/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name" : "OverlayRemountedTest"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/core/tests/overlaytests/remount/host/Android.bp b/core/tests/overlaytests/remount/host/Android.bp
new file mode 100644
index 0000000..3825c55
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/Android.bp
@@ -0,0 +1,28 @@
+// 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.
+
+java_test_host {
+ name: "OverlayRemountedTest",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "tradefed",
+ "junit",
+ ],
+ test_suites: ["general-tests"],
+ java_resources: [
+ ":OverlayRemountedTest_SharedLibrary",
+ ":OverlayRemountedTest_SharedLibraryOverlay",
+ ":OverlayRemountedTest_Target",
+ ],
+}
diff --git a/core/tests/overlaytests/remount/host/AndroidTest.xml b/core/tests/overlaytests/remount/host/AndroidTest.xml
new file mode 100644
index 0000000..11eadf1a
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<configuration description="Test module config for OverlayRemountedTest">
+ <option name="test-tag" value="OverlayRemountedTest" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="remount" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.HostTest">
+ <option name="jar" value="OverlayRemountedTest.jar" />
+ </test>
+</configuration>
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java
new file mode 100644
index 0000000..84af187
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.overlaytest.remounted;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TemporaryFolder;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class OverlayHostTest extends BaseHostJUnit4Test {
+ private static final long TIME_OUT_MS = 30000;
+ private static final String RES_INSTRUMENTATION_ARG = "res";
+ private static final String OVERLAY_INSTRUMENTATION_ARG = "overlays";
+ private static final String RESOURCES_TYPE_SUFFIX = "_type";
+ private static final String RESOURCES_DATA_SUFFIX = "_data";
+
+ public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+ public final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, this::getDevice);
+
+ @Rule
+ public final RuleChain ruleChain = RuleChain.outerRule(mTemporaryFolder).around(mPreparer);
+ private Map<String, String> mLastResults;
+
+ /**
+ * Retrieves the values of the resources in the test package. The test package must use the
+ * {@link com.android.overlaytest.remounted.target.ResourceRetrievalRunner} instrumentation.
+ **/
+ void retrieveResource(String testPackageName, List<String> requiredOverlayPaths,
+ String... resourceNames) throws DeviceNotAvailableException {
+ final HashMap<String, String> args = new HashMap<>();
+ if (!requiredOverlayPaths.isEmpty()) {
+ // Enclose the require overlay paths in quotes so the arguments will be string arguments
+ // rather than file arguments.
+ args.put(OVERLAY_INSTRUMENTATION_ARG,
+ String.format("\"%s\"", String.join(" ", requiredOverlayPaths)));
+ }
+
+ if (resourceNames.length == 0) {
+ throw new IllegalArgumentException("Must specify at least one resource to retrieve.");
+ }
+
+ // Pass the names of the resources to retrieve into the test as one string.
+ args.put(RES_INSTRUMENTATION_ARG,
+ String.format("\"%s\"", String.join(" ", resourceNames)));
+
+ runDeviceTests(getDevice(), null, testPackageName, null, null, null, TIME_OUT_MS,
+ TIME_OUT_MS, TIME_OUT_MS, false, false, args);
+
+ // Retrieve the results of the most recently run test.
+ mLastResults = (getLastDeviceRunResults().getRunMetrics() == mLastResults) ? null :
+ getLastDeviceRunResults().getRunMetrics();
+ }
+
+ /** Returns the base resource directories of the specified packages. */
+ List<String> getPackagePaths(String... packageNames)
+ throws DeviceNotAvailableException {
+ final ArrayList<String> paths = new ArrayList<>();
+ for (String packageName : packageNames) {
+ // Use the package manager shell command to find the path of the package.
+ final String result = getDevice().executeShellCommand(
+ String.format("pm dump %s | grep \"resourcePath=\"", packageName));
+ assertNotNull("Failed to find path for package " + packageName, result);
+ int splitIndex = result.indexOf('=');
+ assertTrue(splitIndex >= 0);
+ paths.add(result.substring(splitIndex + 1).trim());
+ }
+ return paths;
+ }
+
+ /** Builds the full name of a resource in the form package:type/entry. */
+ String resourceName(String pkg, String type, String entry) {
+ return String.format("%s:%s/%s", pkg, type, entry);
+ }
+
+ /**
+ * Asserts that the type and data of a a previously retrieved is the same as expected.
+ * @param resourceName the full name of the resource in the form package:type/entry
+ * @param type the expected {@link android.util.TypedValue} type of the resource
+ * @param data the expected value of the resource when coerced to a string using
+ * {@link android.util.TypedValue#coerceToString()}
+ **/
+ void assertResource(String resourceName, int type, String data) {
+ assertNotNull("Failed to get test results", mLastResults);
+ assertNotEquals("No resource values were retrieved", mLastResults.size(), 0);
+ assertEquals("" + type, mLastResults.get(resourceName + RESOURCES_TYPE_SUFFIX));
+ assertEquals("" + data, mLastResults.get(resourceName + RESOURCES_DATA_SUFFIX));
+ }
+}
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java
new file mode 100644
index 0000000..4939e16
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.overlaytest.remounted;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class OverlaySharedLibraryTest extends OverlayHostTest {
+ private static final String TARGET_APK = "OverlayRemountedTest_Target.apk";
+ private static final String TARGET_PACKAGE = "com.android.overlaytest.remounted.target";
+ private static final String SHARED_LIBRARY_APK =
+ "OverlayRemountedTest_SharedLibrary.apk";
+ private static final String SHARED_LIBRARY_PACKAGE =
+ "com.android.overlaytest.remounted.shared_library";
+ private static final String SHARED_LIBRARY_OVERLAY_APK =
+ "OverlayRemountedTest_SharedLibraryOverlay.apk";
+ private static final String SHARED_LIBRARY_OVERLAY_PACKAGE =
+ "com.android.overlaytest.remounted.shared_library.overlay";
+
+ @Test
+ public void testSharedLibrary() throws Exception {
+ final String targetResource = resourceName(TARGET_PACKAGE, "bool",
+ "uses_shared_library_overlaid");
+ final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
+ "shared_library_overlaid");
+
+ mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
+ .installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE)
+ .reboot()
+ .setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, false)
+ .installResourceApk(TARGET_APK, TARGET_PACKAGE);
+
+ // The shared library resource is not currently overlaid.
+ retrieveResource(Collections.emptyList(), targetResource, libraryResource);
+ assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "false");
+ assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "false");
+
+ // Overlay the shared library resource.
+ mPreparer.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true);
+ retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource,
+ libraryResource);
+ assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+ assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+ }
+
+ @Test
+ public void testSharedLibraryPreEnabled() throws Exception {
+ final String targetResource = resourceName(TARGET_PACKAGE, "bool",
+ "uses_shared_library_overlaid");
+ final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
+ "shared_library_overlaid");
+
+ mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
+ .installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE)
+ .setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true)
+ .reboot()
+ .installResourceApk(TARGET_APK, TARGET_PACKAGE);
+
+ retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource,
+ libraryResource);
+ assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+ assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+ }
+
+ private void retrieveResource(List<String> requiredOverlayPaths, String... resourceNames)
+ throws DeviceNotAvailableException {
+ retrieveResource(TARGET_PACKAGE, requiredOverlayPaths, resourceNames);
+ }
+}
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java
new file mode 100644
index 0000000..7028f2f
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java
@@ -0,0 +1,158 @@
+/*
+ * 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.overlaytest.remounted;
+
+import static org.junit.Assert.assertTrue;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+import org.junit.Assert;
+import org.junit.rules.ExternalResource;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeoutException;
+
+class SystemPreparer extends ExternalResource {
+ private static final long REBOOT_SLEEP_MS = 30000;
+ private static final long OVERLAY_ENABLE_TIMEOUT_MS = 20000;
+
+ // The paths of the files pushed onto the device through this rule.
+ private ArrayList<String> mPushedFiles = new ArrayList<>();
+
+ // The package names of packages installed through this rule.
+ private ArrayList<String> mInstalledPackages = new ArrayList<>();
+
+ private final TemporaryFolder mHostTempFolder;
+ private final DeviceProvider mDeviceProvider;
+
+ SystemPreparer(TemporaryFolder hostTempFolder, DeviceProvider deviceProvider) {
+ mHostTempFolder = hostTempFolder;
+ mDeviceProvider = deviceProvider;
+ }
+
+ /** Copies a file within the host test jar to a path on device. */
+ SystemPreparer pushResourceFile(String resourcePath,
+ String outputPath) throws DeviceNotAvailableException, IOException {
+ final ITestDevice device = mDeviceProvider.getDevice();
+ assertTrue(device.pushFile(copyResourceToTemp(resourcePath), outputPath));
+ mPushedFiles.add(outputPath);
+ return this;
+ }
+
+ /** Installs an APK within the host test jar onto the device. */
+ SystemPreparer installResourceApk(String resourcePath, String packageName)
+ throws DeviceNotAvailableException, IOException {
+ final ITestDevice device = mDeviceProvider.getDevice();
+ final File tmpFile = copyResourceToTemp(resourcePath);
+ final String result = device.installPackage(tmpFile, true);
+ Assert.assertNull(result);
+ mInstalledPackages.add(packageName);
+ return this;
+ }
+
+ /** Sets the enable state of an overlay pacakage. */
+ SystemPreparer setOverlayEnabled(String packageName, boolean enabled)
+ throws ExecutionException, TimeoutException {
+ final ITestDevice device = mDeviceProvider.getDevice();
+
+ // Wait for the overlay to change its enabled state.
+ final FutureTask<Boolean> enabledListener = new FutureTask<>(() -> {
+ while (true) {
+ device.executeShellCommand(String.format("cmd overlay %s %s",
+ enabled ? "enable" : "disable", packageName));
+
+ final String pattern = (enabled ? "[x]" : "[ ]") + " " + packageName;
+ if (device.executeShellCommand("cmd overlay list").contains(pattern)) {
+ return true;
+ }
+ }
+ });
+
+ final Executor executor = (cmd) -> new Thread(cmd).start();
+ executor.execute(enabledListener);
+ try {
+ enabledListener.get(OVERLAY_ENABLE_TIMEOUT_MS, MILLISECONDS);
+ } catch (InterruptedException ignored) {
+ }
+
+ return this;
+ }
+
+ /** Restarts the device and waits until after boot is completed. */
+ SystemPreparer reboot() throws DeviceNotAvailableException {
+ final ITestDevice device = mDeviceProvider.getDevice();
+ device.executeShellCommand("stop");
+ device.executeShellCommand("start");
+ try {
+ // Sleep until the device is ready for test execution.
+ Thread.sleep(REBOOT_SLEEP_MS);
+ } catch (InterruptedException ignored) {
+ }
+
+ return this;
+ }
+
+ /** Copies a file within the host test jar to a temporary file on the host machine. */
+ private File copyResourceToTemp(String resourcePath) throws IOException {
+ final File tempFile = mHostTempFolder.newFile(resourcePath);
+ final ClassLoader classLoader = getClass().getClassLoader();
+ try (InputStream assetIs = classLoader.getResource(resourcePath).openStream();
+ FileOutputStream assetOs = new FileOutputStream(tempFile)) {
+ if (assetIs == null) {
+ throw new IllegalStateException("Failed to find resource " + resourcePath);
+ }
+
+ int b;
+ while ((b = assetIs.read()) >= 0) {
+ assetOs.write(b);
+ }
+ }
+
+ return tempFile;
+ }
+
+ /** Removes installed packages and files that were pushed to the device. */
+ @Override
+ protected void after() {
+ final ITestDevice device = mDeviceProvider.getDevice();
+ try {
+ for (final String file : mPushedFiles) {
+ device.deleteFile(file);
+ }
+ for (final String packageName : mInstalledPackages) {
+ device.uninstallPackage(packageName);
+ }
+ } catch (DeviceNotAvailableException e) {
+ Assert.fail(e.toString());
+ }
+ }
+
+ interface DeviceProvider {
+ ITestDevice getDevice();
+ }
+}
diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp
new file mode 100644
index 0000000..ffb0572
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/Android.bp
@@ -0,0 +1,19 @@
+// 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.
+
+android_test_helper_app {
+ name: "OverlayRemountedTest_SharedLibrary",
+ sdk_version: "current",
+ aaptflags: ["--shared-lib"],
+}
diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml
new file mode 100644
index 0000000..06e3f6a
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.remounted.shared_library">
+ <application>
+ <library android:name="com.android.overlaytest.remounted.shared_library" />
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml
new file mode 100644
index 0000000..1b06f6d
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/overlayable.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <overlayable name="TestResources">
+ <policy type="public">
+ <item type="bool" name="shared_library_overlaid" />
+ </policy>
+ </overlayable>
+</resources>
diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml
new file mode 100644
index 0000000..5b9db16
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/public.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <public type="bool" name="shared_library_overlaid" id="0x00050001"/>
+</resources>
\ No newline at end of file
diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml
new file mode 100644
index 0000000..2dc47a7
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibrary/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <bool name="shared_library_overlaid">false</bool>
+</resources>
diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp
new file mode 100644
index 0000000..0d29aec
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/Android.bp
@@ -0,0 +1,18 @@
+// 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.
+
+android_test_helper_app {
+ name: "OverlayRemountedTest_SharedLibraryOverlay",
+ sdk_version: "current",
+}
diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..53a4e61
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.remounted.shared_library.overlay">
+ <application android:hasCode="false" />
+ <overlay android:targetPackage="com.android.overlaytest.remounted.shared_library"
+ android:targetName="TestResources" />
+</manifest>
\ No newline at end of file
diff --git a/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml
new file mode 100644
index 0000000..f66448a
--- /dev/null
+++ b/core/tests/overlaytests/remount/host/test-apps/SharedLibraryOverlay/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <bool name="shared_library_overlaid">true</bool>
+</resources>
diff --git a/core/tests/overlaytests/remount/target/Android.bp b/core/tests/overlaytests/remount/target/Android.bp
new file mode 100644
index 0000000..83f9f28
--- /dev/null
+++ b/core/tests/overlaytests/remount/target/Android.bp
@@ -0,0 +1,20 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "OverlayRemountedTest_Target",
+ srcs: ["src/**/*.java"],
+ sdk_version: "test_current",
+ libs: ["OverlayRemountedTest_SharedLibrary"],
+}
diff --git a/core/tests/overlaytests/remount/target/AndroidManifest.xml b/core/tests/overlaytests/remount/target/AndroidManifest.xml
new file mode 100644
index 0000000..32fec43
--- /dev/null
+++ b/core/tests/overlaytests/remount/target/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.remounted.target">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <uses-library android:name="com.android.overlaytest.remounted.shared_library"
+ android:required="true" />
+ </application>
+
+ <instrumentation android:name="com.android.overlaytest.remounted.target.ResourceRetrievalRunner"
+ android:targetPackage="com.android.overlaytest.remounted.target"
+ android:label="Remounted system RRO tests" />
+</manifest>
diff --git a/core/tests/overlaytests/remount/target/res/values/values.xml b/core/tests/overlaytests/remount/target/res/values/values.xml
new file mode 100644
index 0000000..b5f444a
--- /dev/null
+++ b/core/tests/overlaytests/remount/target/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:sharedlib="http://schemas.android.com/apk/res/com.android.overlaytest.remounted.shared_library">
+ <bool name="uses_shared_library_overlaid">@sharedlib:bool/shared_library_overlaid</bool>
+</resources>
diff --git a/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java b/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java
new file mode 100644
index 0000000..2e4c211
--- /dev/null
+++ b/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java
@@ -0,0 +1,140 @@
+/*
+ * 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.overlaytest.remounted.target;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.TypedValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An {@link Instrumentation} that retrieves the value of specified resources within the
+ * application.
+ **/
+public class ResourceRetrievalRunner extends Instrumentation {
+ private static final String TAG = ResourceRetrievalRunner.class.getSimpleName();
+
+ // A list of whitespace separated resource names of which to retrieve the resource values.
+ private static final String RESOURCE_LIST_TAG = "res";
+
+ // A list of whitespace separated overlay package paths that must be present before retrieving
+ // resource values.
+ private static final String REQUIRED_OVERLAYS_LIST_TAG = "overlays";
+
+ // The suffixes of the keys returned from the instrumentation. To retrieve the type of a
+ // resource looked up with the instrumentation, append the {@link #RESOURCES_TYPE_SUFFIX} suffix
+ // to the end of the name of the resource. For the value of a resource, use
+ // {@link #RESOURCES_DATA_SUFFIX} instead.
+ private static final String RESOURCES_TYPE_SUFFIX = "_type";
+ private static final String RESOURCES_DATA_SUFFIX = "_data";
+
+ // The amount of time in seconds to wait for the overlays to be present in the AssetManager.
+ private static final int OVERLAY_PATH_TIMEOUT = 60;
+
+ private final ArrayList<String> mResourceNames = new ArrayList<>();
+ private final ArrayList<String> mOverlayPaths = new ArrayList<>();
+ private final Bundle mResult = new Bundle();
+
+ /**
+ * Receives the instrumentation arguments and runs the resource retrieval.
+ * The entry with key {@link #RESOURCE_LIST_TAG} in the {@link Bundle} arguments is a
+ * whitespace separated string of resource names of which to retrieve the resource values.
+ * The entry with key {@link #REQUIRED_OVERLAYS_LIST_TAG} in the {@link Bundle} arguments is a
+ * whitespace separated string of overlay package paths prefixes that must be present before
+ * retrieving the resource values.
+ */
+ @Override
+ public void onCreate(Bundle arguments) {
+ super.onCreate(arguments);
+ mResourceNames.addAll(Arrays.asList(arguments.getString(RESOURCE_LIST_TAG).split(" ")));
+ if (arguments.containsKey(REQUIRED_OVERLAYS_LIST_TAG)) {
+ mOverlayPaths.addAll(Arrays.asList(
+ arguments.getString(REQUIRED_OVERLAYS_LIST_TAG).split(" ")));
+ }
+ start();
+ }
+
+ @Override
+ public void onStart() {
+ final Resources res = getContext().getResources();
+ res.getAssets().setResourceResolutionLoggingEnabled(true);
+
+ if (!mOverlayPaths.isEmpty()) {
+ Log.d(TAG, String.format("Waiting for overlay paths [%s]",
+ String.join(",", mOverlayPaths)));
+
+ // Wait for all required overlays to be present in the AssetManager.
+ final FutureTask<Boolean> overlayListener = new FutureTask<>(() -> {
+ while (!mOverlayPaths.isEmpty()) {
+ final String[] apkPaths = res.getAssets().getApkPaths();
+ for (String path : apkPaths) {
+ for (String overlayPath : mOverlayPaths) {
+ if (path.startsWith(overlayPath)) {
+ mOverlayPaths.remove(overlayPath);
+ break;
+ }
+ }
+ }
+ }
+ return true;
+ });
+
+ try {
+ final Executor executor = (t) -> new Thread(t).start();
+ executor.execute(overlayListener);
+ overlayListener.get(OVERLAY_PATH_TIMEOUT, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ Log.e(TAG, String.format("Failed to wait for required overlays [%s]",
+ String.join(",", mOverlayPaths)), e);
+ finish(Activity.RESULT_CANCELED, mResult);
+ }
+ }
+
+ // Retrieve the values for each resource passed in.
+ final TypedValue typedValue = new TypedValue();
+ for (final String resourceName : mResourceNames) {
+ try {
+ final int resId = res.getIdentifier(resourceName, null, null);
+ res.getValue(resId, typedValue, true);
+ Log.d(TAG, String.format("Resolution for 0x%s: %s", Integer.toHexString(resId),
+ res.getAssets().getLastResourceResolution()));
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Failed to retrieve value for resource " + resourceName, e);
+ finish(Activity.RESULT_CANCELED, mResult);
+ }
+
+ putValue(resourceName, typedValue);
+ }
+
+ finish(Activity.RESULT_OK, mResult);
+ }
+
+ private void putValue(String resourceName, TypedValue value) {
+ mResult.putInt(resourceName + RESOURCES_TYPE_SUFFIX, value.type);
+ final CharSequence textValue = value.coerceToString();
+ mResult.putString(resourceName + RESOURCES_DATA_SUFFIX,
+ textValue == null ? "null" : textValue.toString());
+ }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java b/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java
new file mode 100644
index 0000000..0809f69
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.util;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+
+/** Unit tests for {@link ConnectivityUtil}. */
+public class ConnectivityUtilTest {
+
+ public static final String TAG = "ConnectivityUtilTest";
+
+ // Mock objects for testing
+ @Mock private Context mMockContext;
+ @Mock private PackageManager mMockPkgMgr;
+ @Mock private ApplicationInfo mMockApplInfo;
+ @Mock private AppOpsManager mMockAppOps;
+ @Mock private UserManager mMockUserManager;
+ @Mock private LocationManager mLocationManager;
+
+ private static final String TEST_PKG_NAME = "com.google.somePackage";
+ private static final String TEST_FEATURE_ID = "com.google.someFeature";
+ private static final int MANAGED_PROFILE_UID = 1100000;
+ private static final int OTHER_USER_UID = 1200000;
+
+ private final String mInteractAcrossUsersFullPermission =
+ "android.permission.INTERACT_ACROSS_USERS_FULL";
+ private final String mManifestStringCoarse =
+ Manifest.permission.ACCESS_COARSE_LOCATION;
+ private final String mManifestStringFine =
+ Manifest.permission.ACCESS_FINE_LOCATION;
+
+ // Test variables
+ private int mWifiScanAllowApps;
+ private int mUid;
+ private int mCoarseLocationPermission;
+ private int mAllowCoarseLocationApps;
+ private int mFineLocationPermission;
+ private int mAllowFineLocationApps;
+ private int mCurrentUser;
+ private boolean mIsLocationEnabled;
+ private boolean mThrowSecurityException;
+ private Answer<Integer> mReturnPermission;
+ private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>();
+
+ private class TestConnectivityUtil extends ConnectivityUtil {
+
+ TestConnectivityUtil(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected int getCurrentUser() {
+ return mCurrentUser;
+ }
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ initTestVars();
+ }
+
+ private void setupMocks() throws Exception {
+ when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PKG_NAME), eq(0), any()))
+ .thenReturn(mMockApplInfo);
+ when(mMockContext.getPackageManager()).thenReturn(mMockPkgMgr);
+ when(mMockAppOps.noteOp(AppOpsManager.OPSTR_WIFI_SCAN, mUid, TEST_PKG_NAME,
+ TEST_FEATURE_ID, null)).thenReturn(mWifiScanAllowApps);
+ when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_COARSE_LOCATION), eq(mUid),
+ eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
+ .thenReturn(mAllowCoarseLocationApps);
+ when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(mUid),
+ eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
+ .thenReturn(mAllowFineLocationApps);
+ if (mThrowSecurityException) {
+ doThrow(new SecurityException("Package " + TEST_PKG_NAME + " doesn't belong"
+ + " to application bound to user " + mUid))
+ .when(mMockAppOps).checkPackage(mUid, TEST_PKG_NAME);
+ }
+ when(mMockContext.getSystemService(Context.APP_OPS_SERVICE))
+ .thenReturn(mMockAppOps);
+ when(mMockContext.getSystemService(Context.USER_SERVICE))
+ .thenReturn(mMockUserManager);
+ when(mMockContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
+ }
+
+ private void setupTestCase() throws Exception {
+ setupMocks();
+ setupMockInterface();
+ }
+
+ private void initTestVars() {
+ mPermissionsList.clear();
+ mReturnPermission = createPermissionAnswer();
+ mWifiScanAllowApps = AppOpsManager.MODE_ERRORED;
+ mUid = OTHER_USER_UID;
+ mThrowSecurityException = true;
+ mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M;
+ mIsLocationEnabled = false;
+ mCurrentUser = UserHandle.USER_SYSTEM;
+ mCoarseLocationPermission = PackageManager.PERMISSION_DENIED;
+ mFineLocationPermission = PackageManager.PERMISSION_DENIED;
+ mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
+ mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
+ }
+
+ private void setupMockInterface() {
+ Binder.restoreCallingIdentity((((long) mUid) << 32) | Binder.getCallingPid());
+ doAnswer(mReturnPermission).when(mMockContext).checkPermission(
+ anyString(), anyInt(), anyInt());
+ when(mMockUserManager.isSameProfileGroup(UserHandle.SYSTEM,
+ UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID)))
+ .thenReturn(true);
+ when(mMockContext.checkPermission(mManifestStringCoarse, -1, mUid))
+ .thenReturn(mCoarseLocationPermission);
+ when(mMockContext.checkPermission(mManifestStringFine, -1, mUid))
+ .thenReturn(mFineLocationPermission);
+ when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled);
+ }
+
+ private Answer<Integer> createPermissionAnswer() {
+ return new Answer<Integer>() {
+ @Override
+ public Integer answer(InvocationOnMock invocation) {
+ int myUid = (int) invocation.getArguments()[1];
+ String myPermission = (String) invocation.getArguments()[0];
+ mPermissionsList.get(myPermission);
+ if (mPermissionsList.containsKey(myPermission)) {
+ int uid = mPermissionsList.get(myPermission);
+ if (myUid == uid) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+ };
+ }
+
+ @Test
+ public void testEnforceLocationPermission_HasAllPermissions_BeforeQ() throws Exception {
+ mIsLocationEnabled = true;
+ mThrowSecurityException = false;
+ mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
+ mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ mUid = mCurrentUser;
+ setupTestCase();
+ new TestConnectivityUtil(mMockContext)
+ .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
+ }
+
+ @Test
+ public void testEnforceLocationPermission_HasAllPermissions_AfterQ() throws Exception {
+ mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+ mIsLocationEnabled = true;
+ mThrowSecurityException = false;
+ mUid = mCurrentUser;
+ mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
+ mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ setupTestCase();
+ new TestConnectivityUtil(mMockContext)
+ .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
+ }
+
+ @Test
+ public void testEnforceLocationPermission_PkgNameAndUidMismatch() throws Exception {
+ mThrowSecurityException = true;
+ mIsLocationEnabled = true;
+ mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
+ mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ setupTestCase();
+
+ assertThrows(SecurityException.class,
+ () -> new TestConnectivityUtil(mMockContext)
+ .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+ }
+
+ @Test
+ public void testenforceCanAccessScanResults_UserOrProfileNotCurrent() throws Exception {
+ mIsLocationEnabled = true;
+ mThrowSecurityException = false;
+ mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
+ mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ setupTestCase();
+
+ assertThrows(SecurityException.class,
+ () -> new TestConnectivityUtil(mMockContext)
+ .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+ }
+
+ @Test
+ public void testenforceCanAccessScanResults_NoCoarseLocationPermission() throws Exception {
+ mThrowSecurityException = false;
+ mIsLocationEnabled = true;
+ setupTestCase();
+ assertThrows(SecurityException.class,
+ () -> new TestConnectivityUtil(mMockContext)
+ .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+ }
+
+ @Test
+ public void testenforceCanAccessScanResults_NoFineLocationPermission() throws Exception {
+ mThrowSecurityException = false;
+ mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+ mIsLocationEnabled = true;
+ mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
+ mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
+ mUid = MANAGED_PROFILE_UID;
+ setupTestCase();
+
+ assertThrows(SecurityException.class,
+ () -> new TestConnectivityUtil(mMockContext)
+ .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+ verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString());
+ }
+
+ @Test
+ public void testenforceCanAccessScanResults_LocationModeDisabled() throws Exception {
+ mThrowSecurityException = false;
+ mUid = MANAGED_PROFILE_UID;
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid);
+ mIsLocationEnabled = false;
+
+ setupTestCase();
+
+ assertThrows(SecurityException.class,
+ () -> new TestConnectivityUtil(mMockContext)
+ .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+ }
+
+ private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
+ try {
+ r.run();
+ Assert.fail("Expected " + exceptionClass + " to be thrown.");
+ } catch (Exception exception) {
+ assertTrue(exceptionClass.isInstance(exception));
+ }
+ }
+}
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 26a40d3..dfb7a16 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -135,3 +135,10 @@
src: "com.android.car.floatingcardslauncher.xml",
filename_from_src: true,
}
+
+prebuilt_etc {
+ name: "privapp_whitelist_com.android.car.ui.paintbooth",
+ sub_dir: "permissions",
+ src: "com.android.car.ui.paintbooth.xml",
+ filename_from_src: true,
+}
diff --git a/data/etc/car/com.android.car.ui.paintbooth.xml b/data/etc/car/com.android.car.ui.paintbooth.xml
new file mode 100644
index 0000000..11bf304
--- /dev/null
+++ b/data/etc/car/com.android.car.ui.paintbooth.xml
@@ -0,0 +1,29 @@
+<?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
+ -->
+<permissions>
+
+ <privapp-permissions package="com.android.car.ui.paintbooth">
+ <!-- For enabling/disabling, and getting list of RROs -->
+ <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
+ <!-- For showing the current active activity -->
+ <permission name="android.permission.REAL_GET_TASKS"/>
+ <!-- For getting list of RROs for current user -->
+ <permission name="android.permission.MANAGE_USERS"/>
+ <!-- For getting list of RROs for current user-->
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9930ea2..ad99ab3 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -245,6 +245,7 @@
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
<permission name="android.permission.TETHER_PRIVILEGED"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+ <permission name="android.permission.UPDATE_DEVICE_STATS"/>
</privapp-permissions>
<privapp-permissions package="com.android.server.telecom">
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 8765719..3f2f349 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -44,6 +44,7 @@
"AttributeResolution.cpp",
"ChunkIterator.cpp",
"ConfigDescription.cpp",
+ "DynamicLibManager.cpp",
"Idmap.cpp",
"LoadedArsc.cpp",
"Locale.cpp",
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index ca4143f..8cfd2d8 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -25,6 +25,7 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "androidfw/DynamicLibManager.h"
#include "androidfw/ResourceUtils.h"
#include "androidfw/Util.h"
#include "utils/ByteOrder.h"
@@ -66,7 +67,12 @@
StringPoolRef entry_string_ref;
};
-AssetManager2::AssetManager2() {
+AssetManager2::AssetManager2() : dynamic_lib_manager_(std::make_unique<DynamicLibManager>()) {
+ memset(&configuration_, 0, sizeof(configuration_));
+}
+
+AssetManager2::AssetManager2(DynamicLibManager* dynamic_lib_manager)
+ : dynamic_lib_manager_(dynamic_lib_manager) {
memset(&configuration_, 0, sizeof(configuration_));
}
@@ -85,25 +91,45 @@
package_groups_.clear();
package_ids_.fill(0xff);
- // A mapping from apk assets path to the runtime package id of its first loaded package.
+ // Overlay resources are not directly referenced by an application so their resource ids
+ // can change throughout the application's lifetime. Assign overlay package ids last.
+ std::vector<const ApkAssets*> sorted_apk_assets(apk_assets_);
+ std::stable_partition(sorted_apk_assets.begin(), sorted_apk_assets.end(), [](const ApkAssets* a) {
+ return !a->IsOverlay();
+ });
+
std::unordered_map<std::string, uint8_t> apk_assets_package_ids;
+ std::unordered_map<std::string, uint8_t> package_name_package_ids;
- // 0x01 is reserved for the android package.
- int next_package_id = 0x02;
- const size_t apk_assets_count = apk_assets_.size();
- for (size_t i = 0; i < apk_assets_count; i++) {
- const ApkAssets* apk_assets = apk_assets_[i];
- const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
-
- for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
- // Get the package ID or assign one if a shared library.
- int package_id;
- if (package->IsDynamic()) {
- package_id = next_package_id++;
- } else {
- package_id = package->GetPackageId();
+ // Assign stable package ids to application packages.
+ uint8_t next_available_package_id = 0U;
+ for (const auto& apk_assets : sorted_apk_assets) {
+ for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) {
+ uint8_t package_id = package->GetPackageId();
+ if (package->IsOverlay()) {
+ package_id = GetDynamicLibManager()->FindUnassignedId(next_available_package_id);
+ next_available_package_id = package_id + 1;
+ } else if (package->IsDynamic()) {
+ package_id = GetDynamicLibManager()->GetAssignedId(package->GetPackageName());
}
+ // Map the path of the apk assets to the package id of its first loaded package.
+ apk_assets_package_ids[apk_assets->GetPath()] = package_id;
+
+ // Map the package name of the package to the first loaded package with that package id.
+ package_name_package_ids[package->GetPackageName()] = package_id;
+ }
+ }
+
+ const int apk_assets_count = apk_assets_.size();
+ for (int i = 0; i < apk_assets_count; i++) {
+ const auto& apk_assets = apk_assets_[i];
+ for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) {
+ const auto package_id_entry = package_name_package_ids.find(package->GetPackageName());
+ CHECK(package_id_entry != package_name_package_ids.end())
+ << "no package id assgined to package " << package->GetPackageName();
+ const uint8_t package_id = package_id_entry->second;
+
// Add the mapping for package ID to index if not present.
uint8_t idx = package_ids_[package_id];
if (idx == 0xff) {
@@ -115,7 +141,10 @@
// to take effect.
const auto& loaded_idmap = apk_assets->GetLoadedIdmap();
auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath());
- if (target_package_iter != apk_assets_package_ids.end()) {
+ if (target_package_iter == apk_assets_package_ids.end()) {
+ LOG(INFO) << "failed to find target package for overlay "
+ << loaded_idmap->OverlayApkPath();
+ } else {
const uint8_t target_package_id = target_package_iter->second;
const uint8_t target_idx = package_ids_[target_package_id];
CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not"
@@ -123,7 +152,7 @@
PackageGroup& target_package_group = package_groups_[target_idx];
- // Create a special dynamic reference table for the overlay to rewite references to
+ // Create a special dynamic reference table for the overlay to rewrite references to
// overlay resources as references to the target resources they overlay.
auto overlay_table = std::make_shared<OverlayDynamicRefTable>(
loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
@@ -153,8 +182,6 @@
package_group->dynamic_ref_table->mEntries.replaceValueFor(
package_name, static_cast<uint8_t>(entry.package_id));
}
-
- apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id));
}
}
@@ -567,7 +594,7 @@
if (resource_resolution_logging_enabled_) {
last_resolution_.steps.push_back(
Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(),
- &package_group.packages_[0].loaded_package_->GetPackageName()});
+ overlay_result.package_name});
}
}
}
@@ -1279,6 +1306,16 @@
return 0;
}
+DynamicLibManager* AssetManager2::GetDynamicLibManager() const {
+ auto dynamic_lib_manager =
+ std::get_if<std::unique_ptr<DynamicLibManager>>(&dynamic_lib_manager_);
+ if (dynamic_lib_manager) {
+ return (*dynamic_lib_manager).get();
+ } else {
+ return *std::get_if<DynamicLibManager*>(&dynamic_lib_manager_);
+ }
+}
+
std::unique_ptr<Theme> AssetManager2::NewTheme() {
return std::unique_ptr<Theme>(new Theme(this));
}
diff --git a/libs/androidfw/DynamicLibManager.cpp b/libs/androidfw/DynamicLibManager.cpp
new file mode 100644
index 0000000..895b769
--- /dev/null
+++ b/libs/androidfw/DynamicLibManager.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#include "androidfw/DynamicLibManager.h"
+
+namespace android {
+
+uint8_t DynamicLibManager::GetAssignedId(const std::string& library_package_name) {
+ auto lib_entry = shared_lib_package_ids_.find(library_package_name);
+ if (lib_entry != shared_lib_package_ids_.end()) {
+ return lib_entry->second;
+ }
+
+ return shared_lib_package_ids_[library_package_name] = next_package_id_++;
+}
+
+uint8_t DynamicLibManager::FindUnassignedId(uint8_t start_package_id) {
+ return (start_package_id < next_package_id_) ? next_package_id_ : start_package_id;
+}
+
+} // namespace android
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 00cbbca..b2cec2a 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -27,6 +27,7 @@
#include "androidfw/ApkAssets.h"
#include "androidfw/Asset.h"
#include "androidfw/AssetManager.h"
+#include "androidfw/DynamicLibManager.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/Util.h"
@@ -94,6 +95,7 @@
};
AssetManager2();
+ explicit AssetManager2(DynamicLibManager* dynamic_lib_manager);
// Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
// are not owned by the AssetManager, and must have a longer lifetime.
@@ -371,6 +373,8 @@
// Retrieve the assigned package id of the package if loaded into this AssetManager
uint8_t GetAssignedPackageId(const LoadedPackage* package) const;
+ DynamicLibManager* GetDynamicLibManager() const;
+
// The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
// have a longer lifetime.
std::vector<const ApkAssets*> apk_assets_;
@@ -389,6 +393,9 @@
// may need to be purged.
ResTable_config configuration_;
+ // Component responsible for assigning package ids to shared libraries.
+ std::variant<std::unique_ptr<DynamicLibManager>, DynamicLibManager*> dynamic_lib_manager_;
+
// Cached set of bags. These are cached because they can inherit keys from parent bags,
// which involves some calculation.
std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
diff --git a/libs/androidfw/include/androidfw/DynamicLibManager.h b/libs/androidfw/include/androidfw/DynamicLibManager.h
new file mode 100644
index 0000000..1ff7079
--- /dev/null
+++ b/libs/androidfw/include/androidfw/DynamicLibManager.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROIDFW_DYNAMICLIBMANAGER_H
+#define ANDROIDFW_DYNAMICLIBMANAGER_H
+
+#include <string>
+#include <unordered_map>
+
+#include "android-base/macros.h"
+
+namespace android {
+
+// Manages assigning resource ids for dynamic resources.
+class DynamicLibManager {
+ public:
+ DynamicLibManager() = default;
+
+ // Retrieves the assigned package id for the library.
+ uint8_t GetAssignedId(const std::string& library_package_name);
+
+ // Queries in ascending order for the first available package id that is not currently assigned to
+ // a library.
+ uint8_t FindUnassignedId(uint8_t start_package_id);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DynamicLibManager);
+
+ uint8_t next_package_id_ = 0x02;
+ std::unordered_map<std::string, uint8_t> shared_lib_package_ids_;
+};
+
+} // namespace android
+
+#endif //ANDROIDFW_DYNAMICLIBMANAGER_H
diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h
index 64924f4..8891512 100644
--- a/libs/androidfw/include/androidfw/MutexGuard.h
+++ b/libs/androidfw/include/androidfw/MutexGuard.h
@@ -47,7 +47,8 @@
static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
public:
- explicit Guarded() : guarded_() {
+ template <typename ...Args>
+ explicit Guarded(Args&& ...args) : guarded_(std::forward<Args>(args)...) {
}
template <typename U = T>
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index b3190be..2f6f3df 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -214,6 +214,25 @@
EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data);
}
+TEST_F(AssetManager2Test, AssignsUnchangingPackageIdToSharedLibrary) {
+ DynamicLibManager lib_manager;
+ AssetManager2 assetmanager(&lib_manager);
+ assetmanager.SetApkAssets(
+ {lib_one_assets_.get(), lib_two_assets_.get(), libclient_assets_.get()});
+
+ AssetManager2 assetmanager2(&lib_manager);
+ assetmanager2.SetApkAssets(
+ {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
+ uint32_t res_id = assetmanager.GetResourceId("com.android.lib_one:string/foo");
+ ASSERT_NE(0U, res_id);
+
+ uint32_t res_id_2 = assetmanager2.GetResourceId("com.android.lib_one:string/foo");
+ ASSERT_NE(0U, res_id_2);
+
+ ASSERT_EQ(res_id, res_id_2);
+}
+
TEST_F(AssetManager2Test, GetSharedLibraryResourceName) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({lib_one_assets_.get()});
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 2314072..12681ae 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -746,7 +746,10 @@
glyphFunc(buffer.glyphs, buffer.pos);
sk_sp<SkTextBlob> textBlob(builder.make());
- mCanvas->drawTextBlob(textBlob, 0, 0, paintCopy);
+
+ apply_looper(&paintCopy, [&](const SkPaint& p) {
+ mCanvas->drawTextBlob(textBlob, 0, 0, p);
+ });
drawTextDecorations(x, y, totalAdvance, paintCopy);
}
@@ -783,8 +786,10 @@
xform[i - start].fTx = pos.x() - tan.y() * y - halfWidth * tan.x();
xform[i - start].fTy = pos.y() + tan.x() * y - halfWidth * tan.y();
}
-
- this->asSkCanvas()->drawTextBlob(builder.make(), 0, 0, paintCopy);
+ auto* finalCanvas = this->asSkCanvas();
+ apply_looper(&paintCopy, [&](const SkPaint& p) {
+ finalCanvas->drawTextBlob(builder.make(), 0, 0, paintCopy);
+ });
}
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index 0a54aca..e075d80 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -25,16 +25,16 @@
static const int IDENT_DISPLAYEVENT = 1;
static android::DisplayInfo DUMMY_DISPLAY{
- 1080, // w
- 1920, // h
- 320.0, // xdpi
- 320.0, // ydpi
- 60.0, // fps
- 2.0, // density
- 0, // orientation
- false, // secure?
- 0, // appVsyncOffset
- 0, // presentationDeadline
+ 1080, // w
+ 1920, // h
+ 320.0, // xdpi
+ 320.0, // ydpi
+ 60.0, // fps
+ 2.0, // density
+ ui::ROTATION_0, // orientation
+ false, // secure?
+ 0, // appVsyncOffset
+ 0, // presentationDeadline
};
DisplayInfo getInternalDisplay() {
diff --git a/media/OWNERS b/media/OWNERS
index 8bd037a..be60583 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -5,6 +5,7 @@
etalvala@google.com
gkasten@google.com
hdmoon@google.com
+hkuang@google.com
hunga@google.com
insun@google.com
jaewan@google.com
diff --git a/media/java/android/media/IMediaRoute2ProviderClient.aidl b/media/java/android/media/IMediaRoute2ProviderClient.aidl
index bcb2336..0fccb3a 100644
--- a/media/java/android/media/IMediaRoute2ProviderClient.aidl
+++ b/media/java/android/media/IMediaRoute2ProviderClient.aidl
@@ -18,15 +18,17 @@
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2Info;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
import android.os.Bundle;
/**
* @hide
*/
oneway interface IMediaRoute2ProviderClient {
- void updateState(in MediaRoute2ProviderInfo providerInfo,
- in List<RouteSessionInfo> sessionInfos);
- void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, long requestId);
- void notifySessionInfoChanged(in RouteSessionInfo sessionInfo);
+ // TODO: Change it to updateRoutes?
+ void updateState(in MediaRoute2ProviderInfo providerInfo);
+ void notifySessionCreated(in RoutingSessionInfo sessionInfo, long requestId);
+ void notifySessionCreationFailed(long requestId);
+ void notifySessionUpdated(in RoutingSessionInfo sessionInfo);
+ void notifySessionReleased(in RoutingSessionInfo sessionInfo);
}
diff --git a/media/java/android/media/IMediaRouter2Client.aidl b/media/java/android/media/IMediaRouter2Client.aidl
index f90c7c5..bc7ebea 100644
--- a/media/java/android/media/IMediaRouter2Client.aidl
+++ b/media/java/android/media/IMediaRouter2Client.aidl
@@ -17,7 +17,7 @@
package android.media;
import android.media.MediaRoute2Info;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
import android.os.Bundle;
/**
@@ -28,7 +28,7 @@
void notifyRoutesAdded(in List<MediaRoute2Info> routes);
void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
void notifyRoutesChanged(in List<MediaRoute2Info> routes);
- void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, int requestId);
- void notifySessionInfoChanged(in RouteSessionInfo sessionInfo);
- void notifySessionReleased(in RouteSessionInfo sessionInfo);
+ void notifySessionCreated(in @nullable RoutingSessionInfo sessionInfo, int requestId);
+ void notifySessionInfoChanged(in RoutingSessionInfo sessionInfo);
+ void notifySessionReleased(in RoutingSessionInfo sessionInfo);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 06abdc1..3cdaa07 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -23,7 +23,7 @@
import android.media.MediaRoute2Info;
import android.media.MediaRouterClientState;
import android.media.RouteDiscoveryPreference;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
/**
* {@hide}
@@ -70,5 +70,5 @@
void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
in MediaRoute2Info route, int direction);
- List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager);
+ List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager);
}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 8333b0f..6d9aea5 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -58,6 +58,16 @@
public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
+ /**
+ * The request ID to pass {@link #notifySessionCreated(RoutingSessionInfo, long)}
+ * when {@link MediaRoute2ProviderService} created a session although there was no creation
+ * request.
+ *
+ * @see #notifySessionCreated(RoutingSessionInfo, long)
+ * @hide
+ */
+ public static final long REQUEST_ID_UNKNOWN = 0;
+
private final Handler mHandler;
private final Object mSessionLock = new Object();
private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false);
@@ -66,7 +76,7 @@
private MediaRoute2ProviderInfo mProviderInfo;
@GuardedBy("mSessionLock")
- private ArrayMap<String, RouteSessionInfo> mSessionInfo = new ArrayMap<>();
+ private ArrayMap<String, RoutingSessionInfo> mSessionInfo = new ArrayMap<>();
public MediaRoute2ProviderService() {
mHandler = new Handler(Looper.getMainLooper());
@@ -118,11 +128,11 @@
*
* @param sessionId id of the session
* @return information of the session with the given id.
- * null if the session is destroyed or id is not valid.
+ * null if the session is released or ID is not valid.
* @hide
*/
@Nullable
- public final RouteSessionInfo getSessionInfo(@NonNull String sessionId) {
+ public final RoutingSessionInfo getSessionInfo(@NonNull String sessionId) {
if (TextUtils.isEmpty(sessionId)) {
throw new IllegalArgumentException("sessionId must not be empty");
}
@@ -132,100 +142,50 @@
}
/**
- * Gets the list of {@link RouteSessionInfo session info} that the provider service maintains.
+ * Gets the list of {@link RoutingSessionInfo session info} that the provider service maintains.
* @hide
*/
@NonNull
- public final List<RouteSessionInfo> getAllSessionInfo() {
+ public final List<RoutingSessionInfo> getAllSessionInfo() {
synchronized (mSessionLock) {
return new ArrayList<>(mSessionInfo.values());
}
}
/**
- * Updates the information of a session.
- * If the session is destroyed or not created before, it will be ignored.
- * Call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify clients of
- * session info changes.
- *
- * @param sessionInfo new session information
- * @see #notifySessionCreated(RouteSessionInfo, long)
- * @hide
- */
- public final void updateSessionInfo(@NonNull RouteSessionInfo sessionInfo) {
- Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
- String sessionId = sessionInfo.getId();
-
- synchronized (mSessionLock) {
- if (mSessionInfo.containsKey(sessionId)) {
- mSessionInfo.put(sessionId, sessionInfo);
- schedulePublishState();
- } else {
- Log.w(TAG, "Ignoring unknown session info.");
- return;
- }
- }
- }
-
- /**
- * Notifies the session is changed.
- *
- * TODO: This method is temporary, only created for tests. Remove when the alternative is ready.
- * @hide
- */
- public final void notifySessionInfoChanged(@NonNull RouteSessionInfo sessionInfo) {
- Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
-
- String sessionId = sessionInfo.getId();
- synchronized (mSessionLock) {
- if (mSessionInfo.containsKey(sessionId)) {
- mSessionInfo.put(sessionId, sessionInfo);
- } else {
- Log.w(TAG, "Ignoring unknown session info.");
- return;
- }
- }
-
- if (mClient == null) {
- return;
- }
- try {
- mClient.notifySessionInfoChanged(sessionInfo);
- } catch (RemoteException ex) {
- Log.w(TAG, "Failed to notify session info changed.");
- }
- }
-
- /**
- * Notifies clients of that the session is created and ready for use. If the session can be
- * controlled, pass a {@link Bundle} that contains how to control it.
+ * Notifies clients of that the session is created and ready for use.
+ * <p>
+ * If this session is created without any creation request, use {@link #REQUEST_ID_UNKNOWN}
+ * as the request ID.
*
* @param sessionInfo information of the new session.
- * The {@link RouteSessionInfo#getId() id} of the session must be
- * unique. Pass {@code null} to reject the request or inform clients that
- * session creation is failed.
- * @param requestId id of the previous request to create this 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)
* @hide
*/
- // TODO: fail reason?
- // TODO: Maybe better to create notifySessionCreationFailed?
- public final void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) {
- if (sessionInfo != null) {
- String sessionId = sessionInfo.getId();
- synchronized (mSessionLock) {
- if (mSessionInfo.containsKey(sessionId)) {
- Log.w(TAG, "Ignoring duplicate session id.");
- return;
- }
- mSessionInfo.put(sessionInfo.getId(), sessionInfo);
+ public final void notifySessionCreated(@NonNull RoutingSessionInfo sessionInfo,
+ long requestId) {
+ Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+
+ String sessionId = sessionInfo.getId();
+ synchronized (mSessionLock) {
+ if (mSessionInfo.containsKey(sessionId)) {
+ Log.w(TAG, "Ignoring duplicate session id.");
+ return;
}
- schedulePublishState();
+ mSessionInfo.put(sessionInfo.getId(), sessionInfo);
}
+ schedulePublishState();
if (mClient == null) {
return;
}
try {
+ // TODO: Calling binder calls in multiple thread may cause timing issue.
+ // Consider to change implementations to avoid the problems.
+ // For example, post binder calls, always send all sessions at once, etc.
mClient.notifySessionCreated(sessionInfo, requestId);
} catch (RemoteException ex) {
Log.w(TAG, "Failed to notify session created.");
@@ -233,70 +193,138 @@
}
/**
- * Releases a session with the given id.
- * {@link #onDestroySession} is called if the session is released.
+ * Notifies clients of that the session could not be created.
*
- * @param sessionId id of the session to be released
- * @see #onDestroySession(String, RouteSessionInfo)
+ * @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)
* @hide
*/
- public final void releaseSession(@NonNull String sessionId) {
- if (TextUtils.isEmpty(sessionId)) {
- throw new IllegalArgumentException("sessionId must not be empty");
+ public final void notifySessionCreationFailed(long requestId) {
+ if (mClient == null) {
+ return;
}
- //TODO: notify media router service of release.
- RouteSessionInfo sessionInfo;
- synchronized (mSessionLock) {
- sessionInfo = mSessionInfo.remove(sessionId);
- }
- if (sessionInfo != null) {
- mHandler.sendMessage(obtainMessage(
- MediaRoute2ProviderService::onDestroySession, this, sessionId, sessionInfo));
- schedulePublishState();
+ try {
+ mClient.notifySessionCreationFailed(requestId);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session creation failed.");
}
}
/**
- * Called when a session should be created.
+ * Notifies the existing session is updated. For example, when
+ * {@link RoutingSessionInfo#getSelectedRoutes() selected routes} are changed.
+ *
+ * @hide
+ */
+ public final void notifySessionUpdated(@NonNull RoutingSessionInfo sessionInfo) {
+ Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+
+ String sessionId = sessionInfo.getId();
+ synchronized (mSessionLock) {
+ if (mSessionInfo.containsKey(sessionId)) {
+ mSessionInfo.put(sessionId, sessionInfo);
+ } else {
+ Log.w(TAG, "Ignoring unknown session info.");
+ return;
+ }
+ }
+
+ if (mClient == null) {
+ return;
+ }
+ try {
+ mClient.notifySessionUpdated(sessionInfo);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session info changed.");
+ }
+ }
+
+ /**
+ * Notifies that the session is released.
+ *
+ * @param sessionId id of the released session.
+ * @see #onReleaseSession(String)
+ * @hide
+ */
+ public final void notifySessionReleased(@NonNull String sessionId) {
+ if (TextUtils.isEmpty(sessionId)) {
+ throw new IllegalArgumentException("sessionId must not be empty");
+ }
+ RoutingSessionInfo sessionInfo;
+ synchronized (mSessionLock) {
+ sessionInfo = mSessionInfo.remove(sessionId);
+ }
+
+ if (sessionInfo == null) {
+ Log.w(TAG, "Ignoring unknown session info.");
+ return;
+ }
+
+ if (mClient == null) {
+ return;
+ }
+ try {
+ mClient.notifySessionReleased(sessionInfo);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session info changed.");
+ }
+ }
+
+ /**
+ * Called when the service receives a request to create a session.
+ * <p>
* You should create and maintain your own session and notifies the client of
- * session info. Call {@link #notifySessionCreated(RouteSessionInfo, long)}
+ * session info. Call {@link #notifySessionCreated(RoutingSessionInfo, long)}
* with the given {@code requestId} to notify the information of a new session.
- * If you can't create the session or want to reject the request, pass {@code null}
- * as session info in {@link #notifySessionCreated(RouteSessionInfo, long)}
- * with the given {@code requestId}.
+ * The created session must have the same route feature and must include the given route
+ * specified by {@code routeId}.
+ * <p>
+ * If the session can be controlled, you can optionally pass the control hints to
+ * {@link RoutingSessionInfo.Builder#setControlHints(Bundle)}. Control hints is a
+ * {@link Bundle} which contains how to control the session.
+ * <p>
+ * If you can't create the session or want to reject the request, call
+ * {@link #notifySessionCreationFailed(long)} with the given {@code requestId}.
*
* @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
+ *
+ * @see RoutingSessionInfo.Builder#Builder(String, 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);
/**
- * Called when a session is about to be destroyed.
- * You can clean up your session here. This can happen by the
- * client or provider itself.
+ * Called when the session should be released. A client of the session or system can request
+ * a session to be released.
+ * <p>
+ * After releasing the session, call {@link #notifySessionReleased(String)}
+ * with the ID of the released session.
*
- * @param sessionId id of the session being destroyed.
- * @param lastSessionInfo information of the session being destroyed.
- * @see #releaseSession(String)
+ * Note: Calling {@link #notifySessionReleased(String)} will <em>NOT</em> trigger
+ * this method to be called.
+ *
+ * @param sessionId id of the session being released.
+ * @see #notifySessionReleased(String)
+ * @see #getSessionInfo(String)
* @hide
*/
- public abstract void onDestroySession(@NonNull String sessionId,
- @NonNull RouteSessionInfo lastSessionInfo);
+ public abstract void onReleaseSession(@NonNull String sessionId);
//TODO: make a way to reject the request
/**
* Called when a client requests selecting a route for the session.
- * After the route is selected, call {@link #updateSessionInfo(RouteSessionInfo)} to update
- * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify
- * clients of updated session info.
+ * After the route is selected, call {@link #notifySessionUpdated(RoutingSessionInfo)}
+ * to update session info.
*
* @param sessionId id of the session
* @param routeId id of the route
- * @see #updateSessionInfo(RouteSessionInfo)
* @hide
*/
public abstract void onSelectRoute(@NonNull String sessionId, @NonNull String routeId);
@@ -304,9 +332,8 @@
//TODO: make a way to reject the request
/**
* Called when a client requests deselecting a route from the session.
- * After the route is deselected, call {@link #updateSessionInfo(RouteSessionInfo)} to update
- * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify
- * clients of updated session info.
+ * After the route is deselected, call {@link #notifySessionUpdated(RoutingSessionInfo)}
+ * to update session info.
*
* @param sessionId id of the session
* @param routeId id of the route
@@ -317,9 +344,8 @@
//TODO: make a way to reject the request
/**
* Called when a client requests transferring a session to a route.
- * After the transfer is finished, call {@link #updateSessionInfo(RouteSessionInfo)} to update
- * session info and call {@link #updateProviderInfo(MediaRoute2ProviderInfo)} to notify
- * clients of updated session info.
+ * After the transfer is finished, call {@link #notifySessionUpdated(RoutingSessionInfo)}
+ * to update session info.
*
* @param sessionId id of the session
* @param routeId id of the route
@@ -343,6 +369,8 @@
* </p>
*
* @param preference the new discovery preference
+ *
+ * TODO: This method needs tests.
*/
public void onDiscoveryPreferenceChanged(@NonNull RouteDiscoveryPreference preference) {}
@@ -377,12 +405,12 @@
return;
}
- List<RouteSessionInfo> sessionInfos;
+ List<RoutingSessionInfo> sessionInfos;
synchronized (mSessionLock) {
sessionInfos = new ArrayList<>(mSessionInfo.values());
}
try {
- mClient.updateState(mProviderInfo, sessionInfos);
+ mClient.updateState(mProviderInfo);
} catch (RemoteException ex) {
Log.w(TAG, "Failed to send onProviderInfoUpdated");
}
@@ -414,6 +442,7 @@
MediaRoute2ProviderService.this, packageName, routeId, routeFeature,
requestId));
}
+
@Override
public void releaseSession(@NonNull String sessionId) {
if (!checkCallerisSystem()) {
@@ -423,7 +452,7 @@
Log.w(TAG, "releaseSession: Ignoring empty sessionId from system service.");
return;
}
- mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::releaseSession,
+ mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession,
MediaRoute2ProviderService.this, sessionId));
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index a89dece..bc4da10 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -57,7 +57,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final Object sRouterLock = new Object();
- @GuardedBy("sLock")
+ @GuardedBy("sRouterLock")
private static MediaRouter2 sInstance;
private final Context mContext;
@@ -73,23 +73,23 @@
new CopyOnWriteArrayList<>();
private final String mPackageName;
- @GuardedBy("sLock")
+ @GuardedBy("sRouterLock")
final Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
- @GuardedBy("sLock")
+ @GuardedBy("sRouterLock")
private RouteDiscoveryPreference mDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
// TODO: Make MediaRouter2 is always connected to the MediaRouterService.
- @GuardedBy("sLock")
+ @GuardedBy("sRouterLock")
Client2 mClient;
- @GuardedBy("sLock")
- private Map<String, RouteSessionController> mSessionControllers = new ArrayMap<>();
+ @GuardedBy("sRouterLock")
+ private Map<String, RoutingController> mRoutingControllers = new ArrayMap<>();
private AtomicInteger mSessionCreationRequestCnt = new AtomicInteger(1);
final Handler mHandler;
- @GuardedBy("sLock")
+ @GuardedBy("sRouterLock")
private boolean mShouldUpdateRoutes;
private volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList();
@@ -241,7 +241,7 @@
}
/**
- * Registers a callback to get updates on creations and changes of route sessions.
+ * Registers a callback to get updates on creations and changes of routing sessions.
* If you register the same callback twice or more, it will be ignored.
*
* @param executor the executor to execute the callback on
@@ -455,7 +455,7 @@
* <p>
* Pass {@code null} to sessionInfo for the failure case.
*/
- void createControllerOnHandler(@Nullable RouteSessionInfo sessionInfo, int requestId) {
+ void createControllerOnHandler(@Nullable RoutingSessionInfo sessionInfo, int requestId) {
SessionCreationRequest matchingRequest = null;
for (SessionCreationRequest request : mSessionCreationRequests) {
if (request.mRequestId == requestId) {
@@ -502,23 +502,23 @@
}
if (sessionInfo != null) {
- RouteSessionController controller = new RouteSessionController(sessionInfo);
+ RoutingController controller = new RoutingController(sessionInfo);
synchronized (sRouterLock) {
- mSessionControllers.put(controller.getSessionId(), controller);
+ mRoutingControllers.put(controller.getSessionId(), controller);
}
notifySessionCreated(controller);
}
}
- void changeSessionInfoOnHandler(RouteSessionInfo sessionInfo) {
+ void changeSessionInfoOnHandler(RoutingSessionInfo sessionInfo) {
if (sessionInfo == null) {
Log.w(TAG, "changeSessionInfoOnHandler: Ignoring null sessionInfo.");
return;
}
- RouteSessionController matchingController;
+ RoutingController matchingController;
synchronized (sRouterLock) {
- matchingController = mSessionControllers.get(sessionInfo.getId());
+ matchingController = mRoutingControllers.get(sessionInfo.getId());
}
if (matchingController == null) {
@@ -527,27 +527,27 @@
return;
}
- RouteSessionInfo oldInfo = matchingController.getRouteSessionInfo();
+ RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo();
if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) {
Log.w(TAG, "changeSessionInfoOnHandler: Provider IDs are not matched. old="
+ oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId());
return;
}
- matchingController.setRouteSessionInfo(sessionInfo);
+ matchingController.setRoutingSessionInfo(sessionInfo);
notifySessionInfoChanged(matchingController, oldInfo, sessionInfo);
}
- void releaseControllerOnHandler(RouteSessionInfo sessionInfo) {
+ void releaseControllerOnHandler(RoutingSessionInfo sessionInfo) {
if (sessionInfo == null) {
Log.w(TAG, "releaseControllerOnHandler: Ignoring null sessionInfo.");
return;
}
final String uniqueSessionId = sessionInfo.getId();
- RouteSessionController matchingController;
+ RoutingController matchingController;
synchronized (sRouterLock) {
- matchingController = mSessionControllers.get(uniqueSessionId);
+ matchingController = mRoutingControllers.get(uniqueSessionId);
}
if (matchingController == null) {
@@ -558,7 +558,7 @@
return;
}
- RouteSessionInfo oldInfo = matchingController.getRouteSessionInfo();
+ RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo();
if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) {
Log.w(TAG, "releaseControllerOnHandler: Provider IDs are not matched. old="
+ oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId());
@@ -566,7 +566,7 @@
}
synchronized (sRouterLock) {
- mSessionControllers.remove(uniqueSessionId, matchingController);
+ mRoutingControllers.remove(uniqueSessionId, matchingController);
}
matchingController.release();
notifyControllerReleased(matchingController);
@@ -610,7 +610,7 @@
}
}
- private void notifySessionCreated(RouteSessionController controller) {
+ private void notifySessionCreated(RoutingController controller) {
for (SessionCallbackRecord record: mSessionCallbackRecords) {
record.mExecutor.execute(
() -> record.mSessionCallback.onSessionCreated(controller));
@@ -624,8 +624,8 @@
}
}
- private void notifySessionInfoChanged(RouteSessionController controller,
- RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
+ private void notifySessionInfoChanged(RoutingController controller,
+ RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) {
for (SessionCallbackRecord record: mSessionCallbackRecords) {
record.mExecutor.execute(
() -> record.mSessionCallback.onSessionInfoChanged(
@@ -633,7 +633,7 @@
}
}
- private void notifyControllerReleased(RouteSessionController controller) {
+ private void notifyControllerReleased(RoutingController controller) {
for (SessionCallbackRecord record: mSessionCallbackRecords) {
record.mExecutor.execute(
() -> record.mSessionCallback.onSessionReleased(controller));
@@ -678,11 +678,11 @@
*/
public static class SessionCallback {
/**
- * Called when the route session is created by the route provider.
+ * Called when the routing session is created by the route provider.
*
* @param controller the controller to control the created session
*/
- public void onSessionCreated(@NonNull RouteSessionController controller) {}
+ public void onSessionCreated(@NonNull RoutingController controller) {}
/**
* Called when the session creation request failed.
@@ -702,45 +702,44 @@
* TODO: (Discussion) Do we really need newInfo? The controller has the newInfo.
* However. there can be timing issue if there is no newInfo.
*/
- public void onSessionInfoChanged(@NonNull RouteSessionController controller,
- @NonNull RouteSessionInfo oldInfo,
- @NonNull RouteSessionInfo newInfo) {}
+ public void onSessionInfoChanged(@NonNull RoutingController controller,
+ @NonNull RoutingSessionInfo oldInfo,
+ @NonNull RoutingSessionInfo newInfo) {}
/**
* Called when the session is released by {@link MediaRoute2ProviderService}.
* Before this method is called, the controller would be released by the system,
- * which means the {@link RouteSessionController#isReleased()} will always return true
+ * which means the {@link RoutingController#isReleased()} will always return true
* for the {@code controller} here.
* <p>
- * Note: Calling {@link RouteSessionController#release()} will <em>NOT</em> trigger
+ * Note: Calling {@link RoutingController#release()} will <em>NOT</em> trigger
* this method to be called.
*
* TODO: Add tests for checking whether this method is called.
* TODO: When service process dies, this should be called.
*
- * @see RouteSessionController#isReleased()
+ * @see RoutingController#isReleased()
*/
- public void onSessionReleased(@NonNull RouteSessionController controller) {}
+ public void onSessionReleased(@NonNull RoutingController controller) {}
}
/**
- * A class to control media route session in media route provider.
+ * 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}.
*
- * TODO: Need to add toString()
* @hide
*/
- public final class RouteSessionController {
+ public final class RoutingController {
private final Object mControllerLock = new Object();
- @GuardedBy("mLock")
- private RouteSessionInfo mSessionInfo;
+ @GuardedBy("mControllerLock")
+ private RoutingSessionInfo mSessionInfo;
- @GuardedBy("mLock")
+ @GuardedBy("mControllerLock")
private volatile boolean mIsReleased;
- RouteSessionController(@NonNull RouteSessionInfo sessionInfo) {
+ RoutingController(@NonNull RoutingSessionInfo sessionInfo) {
mSessionInfo = sessionInfo;
}
@@ -764,7 +763,7 @@
}
/**
- * @return the control hints used to control route session if available.
+ * @return the control hints used to control routing session if available.
*/
@Nullable
public Bundle getControlHints() {
@@ -986,7 +985,7 @@
Client2 client;
synchronized (sRouterLock) {
- mSessionControllers.remove(getSessionId(), this);
+ mRoutingControllers.remove(getSessionId(), this);
client = mClient;
}
if (client != null) {
@@ -998,18 +997,50 @@
}
}
+ @Override
+ public String toString() {
+ // To prevent logging spam, we only print the ID of each route.
+ List<String> selectedRoutes = getSelectedRoutes().stream()
+ .map(MediaRoute2Info::getId).collect(Collectors.toList());
+ List<String> selectableRoutes = getSelectableRoutes().stream()
+ .map(MediaRoute2Info::getId).collect(Collectors.toList());
+ List<String> deselectableRoutes = getDeselectableRoutes().stream()
+ .map(MediaRoute2Info::getId).collect(Collectors.toList());
+ List<String> transferrableRoutes = getTransferrableRoutes().stream()
+ .map(MediaRoute2Info::getId).collect(Collectors.toList());
+
+ StringBuilder result = new StringBuilder()
+ .append("RoutingController{ ")
+ .append("sessionId=").append(getSessionId())
+ .append(", routeFeature=").append(getRouteFeature())
+ .append(", selectedRoutes={")
+ .append(selectedRoutes)
+ .append("}")
+ .append(", selectableRoutes={")
+ .append(selectableRoutes)
+ .append("}")
+ .append(", deselectableRoutes={")
+ .append(deselectableRoutes)
+ .append("}")
+ .append(", transferrableRoutes={")
+ .append(transferrableRoutes)
+ .append("}")
+ .append(" }");
+ return result.toString();
+ }
+
/**
* TODO: Change this to package private. (Hidden for debugging purposes)
* @hide
*/
@NonNull
- public RouteSessionInfo getRouteSessionInfo() {
+ public RoutingSessionInfo getRoutingSessionInfo() {
synchronized (mControllerLock) {
return mSessionInfo;
}
}
- void setRouteSessionInfo(@NonNull RouteSessionInfo info) {
+ void setRoutingSessionInfo(@NonNull RoutingSessionInfo info) {
synchronized (mControllerLock) {
mSessionInfo = info;
}
@@ -1125,19 +1156,19 @@
}
@Override
- public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, int requestId) {
+ public void notifySessionCreated(@Nullable RoutingSessionInfo sessionInfo, int requestId) {
mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
MediaRouter2.this, sessionInfo, requestId));
}
@Override
- public void notifySessionInfoChanged(@Nullable RouteSessionInfo sessionInfo) {
+ public void notifySessionInfoChanged(@Nullable RoutingSessionInfo sessionInfo) {
mHandler.sendMessage(obtainMessage(MediaRouter2::changeSessionInfoOnHandler,
MediaRouter2.this, sessionInfo));
}
@Override
- public void notifySessionReleased(RouteSessionInfo sessionInfo) {
+ public void notifySessionReleased(RoutingSessionInfo sessionInfo) {
mHandler.sendMessage(obtainMessage(MediaRouter2::releaseControllerOnHandler,
MediaRouter2.this, sessionInfo));
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index f445cdb..7022933 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -176,7 +176,7 @@
}
@NonNull
- public List<RouteSessionInfo> getActiveSessions() {
+ public List<RoutingSessionInfo> getActiveSessions() {
Client client;
synchronized (sLock) {
client = mClient;
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
new file mode 100644
index 0000000..2c431b9
--- /dev/null
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -0,0 +1,403 @@
+/*
+ * 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.media;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * MediaTranscodeManager provides an interface to the system's media transcode service.
+ * Transcode requests are put in a queue and processed in order. When a transcode operation is
+ * completed the caller is notified via its OnTranscodingFinishedListener. In the meantime the
+ * caller may use the returned TranscodingJob object to cancel or check the status of a specific
+ * transcode operation.
+ * The currently supported media types are video and still images.
+ *
+ * TODO(lnilsson): Add sample code when API is settled.
+ *
+ * @hide
+ */
+public final class MediaTranscodeManager {
+ private static final String TAG = "MediaTranscodeManager";
+
+ // Invalid ID passed from native means the request was never enqueued.
+ private static final long ID_INVALID = -1;
+
+ // Events passed from native.
+ private static final int EVENT_JOB_STARTED = 1;
+ private static final int EVENT_JOB_PROGRESSED = 2;
+ private static final int EVENT_JOB_FINISHED = 3;
+
+ @IntDef(prefix = { "EVENT_" }, value = {
+ EVENT_JOB_STARTED,
+ EVENT_JOB_PROGRESSED,
+ EVENT_JOB_FINISHED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Event {}
+
+ private static MediaTranscodeManager sMediaTranscodeManager;
+ private final ConcurrentMap<Long, TranscodingJob> mPendingTranscodingJobs =
+ new ConcurrentHashMap<>();
+ private final Context mContext;
+
+ /**
+ * Listener that gets notified when a transcoding operation has finished.
+ * This listener gets notified regardless of how the operation finished. It is up to the
+ * listener implementation to check the result and take appropriate action.
+ */
+ @FunctionalInterface
+ public interface OnTranscodingFinishedListener {
+ /**
+ * Called when the transcoding operation has finished. The receiver may use the
+ * TranscodingJob to check the result, i.e. whether the operation succeeded, was canceled or
+ * if an error occurred.
+ * @param transcodingJob The TranscodingJob instance for the finished transcoding operation.
+ */
+ void onTranscodingFinished(@NonNull TranscodingJob transcodingJob);
+ }
+
+ /**
+ * Class describing a transcode operation to be performed. The caller uses this class to
+ * configure a transcoding operation that can then be enqueued using MediaTranscodeManager.
+ */
+ public static final class TranscodingRequest {
+ private Uri mSrcUri;
+ private Uri mDstUri;
+ private MediaFormat mDstFormat;
+
+ private TranscodingRequest(Builder b) {
+ mSrcUri = b.mSrcUri;
+ mDstUri = b.mDstUri;
+ mDstFormat = b.mDstFormat;
+ }
+
+ /** TranscodingRequest builder class. */
+ public static class Builder {
+ private Uri mSrcUri;
+ private Uri mDstUri;
+ private MediaFormat mDstFormat;
+
+ /**
+ * Specifies the source media file.
+ * @param uri Content uri for the source media file.
+ * @return The builder instance.
+ */
+ public Builder setSourceUri(Uri uri) {
+ mSrcUri = uri;
+ return this;
+ }
+
+ /**
+ * Specifies the destination media file.
+ * @param uri Content uri for the destination media file.
+ * @return The builder instance.
+ */
+ public Builder setDestinationUri(Uri uri) {
+ mDstUri = uri;
+ return this;
+ }
+
+ /**
+ * Specifies the media format of the transcoded media file.
+ * @param dstFormat MediaFormat containing the desired destination format.
+ * @return The builder instance.
+ */
+ public Builder setDestinationFormat(MediaFormat dstFormat) {
+ mDstFormat = dstFormat;
+ return this;
+ }
+
+ /**
+ * Builds a new TranscodingRequest with the configuration set on this builder.
+ * @return A new TranscodingRequest.
+ */
+ public TranscodingRequest build() {
+ return new TranscodingRequest(this);
+ }
+ }
+ }
+
+ /**
+ * Handle to an enqueued transcoding operation. An instance of this class represents a single
+ * enqueued transcoding operation. The caller can use that instance to query the status or
+ * progress, and to get the result once the operation has completed.
+ */
+ public static final class TranscodingJob {
+ /** The job is enqueued but not yet running. */
+ public static final int STATUS_PENDING = 1;
+ /** The job is currently running. */
+ public static final int STATUS_RUNNING = 2;
+ /** The job is finished. */
+ public static final int STATUS_FINISHED = 3;
+
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_PENDING,
+ STATUS_RUNNING,
+ STATUS_FINISHED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Status {}
+
+ /** The job does not have a result yet. */
+ public static final int RESULT_NONE = 1;
+ /** The job completed successfully. */
+ public static final int RESULT_SUCCESS = 2;
+ /** The job encountered an error while running. */
+ public static final int RESULT_ERROR = 3;
+ /** The job was canceled by the caller. */
+ public static final int RESULT_CANCELED = 4;
+
+ @IntDef(prefix = { "RESULT_" }, value = {
+ RESULT_NONE,
+ RESULT_SUCCESS,
+ RESULT_ERROR,
+ RESULT_CANCELED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Result {}
+
+ /** Listener that gets notified when the progress changes. */
+ @FunctionalInterface
+ public interface OnProgressChangedListener {
+
+ /**
+ * Called when the progress changes. The progress is between 0 and 1, where 0 means
+ * that the job has not yet started and 1 means that it has finished.
+ * @param progress The new progress.
+ */
+ void onProgressChanged(float progress);
+ }
+
+ private final Executor mExecutor;
+ private final OnTranscodingFinishedListener mListener;
+ private final ReentrantLock mStatusChangeLock = new ReentrantLock();
+ private Executor mProgressChangedExecutor;
+ private OnProgressChangedListener mProgressChangedListener;
+ private long mID;
+ private float mProgress = 0.0f;
+ private @Status int mStatus = STATUS_PENDING;
+ private @Result int mResult = RESULT_NONE;
+
+ private TranscodingJob(long id, @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnTranscodingFinishedListener listener) {
+ mID = id;
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ /**
+ * Set a progress listener.
+ * @param listener The progress listener.
+ */
+ public void setOnProgressChangedListener(@NonNull @CallbackExecutor Executor executor,
+ @Nullable OnProgressChangedListener listener) {
+ mProgressChangedExecutor = executor;
+ mProgressChangedListener = listener;
+ }
+
+ /**
+ * Cancels the transcoding job and notify the listener. If the job happened to finish before
+ * being canceled this call is effectively a no-op and will not update the result in that
+ * case.
+ */
+ public void cancel() {
+ setJobFinished(RESULT_CANCELED);
+ sMediaTranscodeManager.native_cancelTranscodingRequest(mID);
+ }
+
+ /**
+ * Gets the progress of the transcoding job. The progress is between 0 and 1, where 0 means
+ * that the job has not yet started and 1 means that it is finished.
+ * @return The progress.
+ */
+ public float getProgress() {
+ return mProgress;
+ }
+
+ /**
+ * Gets the status of the transcoding job.
+ * @return The status.
+ */
+ public @Status int getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * Gets the result of the transcoding job.
+ * @return The result.
+ */
+ public @Result int getResult() {
+ return mResult;
+ }
+
+ private void setJobStarted() {
+ mStatus = STATUS_RUNNING;
+ }
+
+ private void setJobProgress(float newProgress) {
+ mProgress = newProgress;
+
+ // Notify listener.
+ OnProgressChangedListener onProgressChangedListener = mProgressChangedListener;
+ if (onProgressChangedListener != null) {
+ mProgressChangedExecutor.execute(
+ () -> onProgressChangedListener.onProgressChanged(mProgress));
+ }
+ }
+
+ private void setJobFinished(int result) {
+ boolean doNotifyListener = false;
+
+ // Prevent conflicting simultaneous status updates from native (finished) and from the
+ // caller (cancel).
+ try {
+ mStatusChangeLock.lock();
+ if (mStatus != STATUS_FINISHED) {
+ mStatus = STATUS_FINISHED;
+ mResult = result;
+ doNotifyListener = true;
+ }
+ } finally {
+ mStatusChangeLock.unlock();
+ }
+
+ if (doNotifyListener) {
+ mExecutor.execute(() -> mListener.onTranscodingFinished(this));
+ }
+ }
+
+ private void processJobEvent(@Event int event, int arg) {
+ switch (event) {
+ case EVENT_JOB_STARTED:
+ setJobStarted();
+ break;
+ case EVENT_JOB_PROGRESSED:
+ setJobProgress((float) arg / 100);
+ break;
+ case EVENT_JOB_FINISHED:
+ setJobFinished(arg);
+ break;
+ default:
+ Log.e(TAG, "Unsupported event: " + event);
+ break;
+ }
+ }
+ }
+
+ // Initializes the native library.
+ private static native void native_init();
+ // Requests a new job ID from the native service.
+ private native long native_requestUniqueJobID();
+ // Enqueues a transcoding request to the native service.
+ private native boolean native_enqueueTranscodingRequest(
+ long id, @NonNull TranscodingRequest transcodingRequest, @NonNull Context context);
+ // Cancels an enqueued transcoding request.
+ private native void native_cancelTranscodingRequest(long id);
+
+ // Private constructor.
+ private MediaTranscodeManager(@NonNull Context context) {
+ mContext = context;
+ }
+
+ // Events posted from the native service.
+ @SuppressWarnings("unused")
+ private void postEventFromNative(@Event int event, long id, int arg) {
+ Log.d(TAG, String.format("postEventFromNative. Event %d, ID %d, arg %d", event, id, arg));
+
+ TranscodingJob transcodingJob = mPendingTranscodingJobs.get(id);
+
+ // Job IDs are added to the tracking set before the job is enqueued so it should never
+ // be null unless the service misbehaves.
+ if (transcodingJob == null) {
+ Log.e(TAG, "No matching transcode job found for id " + id);
+ return;
+ }
+
+ transcodingJob.processJobEvent(event, arg);
+ }
+
+ /**
+ * Gets the MediaTranscodeManager singleton instance.
+ * @param context The application context.
+ * @return the {@link MediaTranscodeManager} singleton instance.
+ */
+ public static MediaTranscodeManager getInstance(@NonNull Context context) {
+ Preconditions.checkNotNull(context);
+ synchronized (MediaTranscodeManager.class) {
+ if (sMediaTranscodeManager == null) {
+ sMediaTranscodeManager = new MediaTranscodeManager(context.getApplicationContext());
+ }
+ return sMediaTranscodeManager;
+ }
+ }
+
+ /**
+ * Enqueues a TranscodingRequest for execution.
+ * @param transcodingRequest The TranscodingRequest to enqueue.
+ * @param listenerExecutor Executor on which the listener is notified.
+ * @param listener Listener to get notified when the transcoding job is finished.
+ * @return A TranscodingJob for this operation.
+ */
+ public @Nullable TranscodingJob enqueueTranscodingRequest(
+ @NonNull TranscodingRequest transcodingRequest,
+ @NonNull @CallbackExecutor Executor listenerExecutor,
+ @NonNull OnTranscodingFinishedListener listener) {
+ Log.i(TAG, "enqueueTranscodingRequest called.");
+ Preconditions.checkNotNull(transcodingRequest);
+ Preconditions.checkNotNull(listenerExecutor);
+ Preconditions.checkNotNull(listener);
+
+ // Reserve a job ID.
+ long jobID = native_requestUniqueJobID();
+ if (jobID == ID_INVALID) {
+ return null;
+ }
+
+ // Add the job to the tracking set.
+ TranscodingJob transcodingJob = new TranscodingJob(jobID, listenerExecutor, listener);
+ mPendingTranscodingJobs.put(jobID, transcodingJob);
+
+ // Enqueue the request with the native service.
+ boolean enqueued = native_enqueueTranscodingRequest(jobID, transcodingRequest, mContext);
+ if (!enqueued) {
+ mPendingTranscodingJobs.remove(jobID);
+ return null;
+ }
+
+ return transcodingJob;
+ }
+
+ static {
+ System.loadLibrary("media_jni");
+ native_init();
+ }
+}
diff --git a/media/java/android/media/RouteSessionInfo.aidl b/media/java/android/media/RoutingSessionInfo.aidl
similarity index 95%
rename from media/java/android/media/RouteSessionInfo.aidl
rename to media/java/android/media/RoutingSessionInfo.aidl
index fb5d836..7b8e3d9 100644
--- a/media/java/android/media/RouteSessionInfo.aidl
+++ b/media/java/android/media/RoutingSessionInfo.aidl
@@ -16,4 +16,4 @@
package android.media;
-parcelable RouteSessionInfo;
+parcelable RoutingSessionInfo;
diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
similarity index 92%
rename from media/java/android/media/RouteSessionInfo.java
rename to media/java/android/media/RoutingSessionInfo.java
index def5197..96acf6c 100644
--- a/media/java/android/media/RouteSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -30,24 +30,24 @@
import java.util.Objects;
/**
- * Describes a route session that is made when a media route is selected.
+ * Describes a routing session which is created when a media route is selected.
* @hide
*/
-public final class RouteSessionInfo implements Parcelable {
+public final class RoutingSessionInfo implements Parcelable {
@NonNull
- public static final Creator<RouteSessionInfo> CREATOR =
- new Creator<RouteSessionInfo>() {
+ public static final Creator<RoutingSessionInfo> CREATOR =
+ new Creator<RoutingSessionInfo>() {
@Override
- public RouteSessionInfo createFromParcel(Parcel in) {
- return new RouteSessionInfo(in);
+ public RoutingSessionInfo createFromParcel(Parcel in) {
+ return new RoutingSessionInfo(in);
}
@Override
- public RouteSessionInfo[] newArray(int size) {
- return new RouteSessionInfo[size];
+ public RoutingSessionInfo[] newArray(int size) {
+ return new RoutingSessionInfo[size];
}
};
- public static final String TAG = "RouteSessionInfo";
+ private static final String TAG = "RoutingSessionInfo";
final String mId;
final String mClientPackageName;
@@ -61,7 +61,7 @@
@Nullable
final Bundle mControlHints;
- RouteSessionInfo(@NonNull Builder builder) {
+ RoutingSessionInfo(@NonNull Builder builder) {
Objects.requireNonNull(builder, "builder must not be null.");
mId = builder.mId;
@@ -82,7 +82,7 @@
mControlHints = builder.mControlHints;
}
- RouteSessionInfo(@NonNull Parcel src) {
+ RoutingSessionInfo(@NonNull Parcel src) {
Objects.requireNonNull(src, "src must not be null.");
mId = ensureString(src.readString());
@@ -228,11 +228,11 @@
if (this == obj) {
return true;
}
- if (!(obj instanceof RouteSessionInfo)) {
+ if (!(obj instanceof RoutingSessionInfo)) {
return false;
}
- RouteSessionInfo other = (RouteSessionInfo) obj;
+ RoutingSessionInfo other = (RoutingSessionInfo) obj;
return Objects.equals(mId, other.mId)
&& Objects.equals(mClientPackageName, other.mClientPackageName)
&& Objects.equals(mRouteFeature, other.mRouteFeature)
@@ -252,7 +252,7 @@
@Override
public String toString() {
StringBuilder result = new StringBuilder()
- .append("RouteSessionInfo{ ")
+ .append("RoutingSessionInfo{ ")
.append("sessionId=").append(mId)
.append(", routeFeature=").append(mRouteFeature)
.append(", selectedRoutes={")
@@ -290,7 +290,7 @@
}
/**
- * Builder class for {@link RouteSessionInfo}.
+ * Builder class for {@link RoutingSessionInfo}.
*/
public static final class Builder {
final String mId;
@@ -304,10 +304,10 @@
Bundle mControlHints;
/**
- * Constructor for builder to create {@link RouteSessionInfo}.
+ * Constructor for builder to create {@link RoutingSessionInfo}.
* <p>
* In order to ensure ID uniqueness in {@link MediaRouter2} side, the value of
- * {@link RouteSessionInfo#getId()} can be different from what was set in
+ * {@link RoutingSessionInfo#getId()} can be different from what was set in
* {@link MediaRoute2ProviderService}.
* </p>
*
@@ -337,12 +337,12 @@
}
/**
- * Constructor for builder to create {@link RouteSessionInfo} with
- * existing {@link RouteSessionInfo} instance.
+ * Constructor for builder to create {@link RoutingSessionInfo} with
+ * existing {@link RoutingSessionInfo} instance.
*
* @param sessionInfo the existing instance to copy data from.
*/
- public Builder(@NonNull RouteSessionInfo sessionInfo) {
+ public Builder(@NonNull RoutingSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
mId = sessionInfo.mId;
@@ -514,16 +514,16 @@
}
/**
- * Builds a route session info.
+ * Builds a routing session info.
*
* @throws IllegalArgumentException if no selected routes are added.
*/
@NonNull
- public RouteSessionInfo build() {
+ public RoutingSessionInfo build() {
if (mSelectedRoutes.isEmpty()) {
throw new IllegalArgumentException("selectedRoutes must not be empty");
}
- return new RouteSessionInfo(this);
+ return new RoutingSessionInfo(this);
}
}
}
diff --git a/media/java/android/media/VolumeProvider.java b/media/java/android/media/VolumeProvider.java
index 8f68cbd..ed272d5 100644
--- a/media/java/android/media/VolumeProvider.java
+++ b/media/java/android/media/VolumeProvider.java
@@ -16,6 +16,7 @@
package android.media;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.media.session.MediaSession;
import java.lang.annotation.Retention;
@@ -60,6 +61,7 @@
private final int mControlType;
private final int mMaxVolume;
+ private final String mControlId;
private int mCurrentVolume;
private Callback mCallback;
@@ -73,10 +75,28 @@
* @param maxVolume The maximum allowed volume.
* @param currentVolume The current volume on the output.
*/
+
public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume) {
+ this(volumeControl, maxVolume, currentVolume, null);
+ }
+
+ /**
+ * Create a new volume provider for handling volume events. You must specify
+ * the type of volume control, the maximum volume that can be used, and the
+ * current volume on the output.
+ *
+ * @param volumeControl The method for controlling volume that is used by
+ * this provider.
+ * @param maxVolume The maximum allowed volume.
+ * @param currentVolume The current volume on the output.
+ * @param volumeControlId The volume control id of this provider.
+ */
+ public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume,
+ @Nullable String volumeControlId) {
mControlType = volumeControl;
mMaxVolume = maxVolume;
mCurrentVolume = currentVolume;
+ mControlId = volumeControlId;
}
/**
@@ -122,6 +142,17 @@
}
/**
+ * Gets the volume control id. It can be used to identify which volume provider is
+ * used by the session.
+ *
+ * @return the volume control id or {@code null} if it isn't set.
+ */
+ @Nullable
+ public final String getVolumeControlId() {
+ return mControlId;
+ }
+
+ /**
* Override to handle requests to set the volume of the current output.
* After the volume has been modified {@link #setCurrentVolume} must be
* called to notify the system.
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 01f1250..27f02fe 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -719,9 +719,14 @@
if (track == null) {
break;
}
- // TODO: add synchronous versions
- track.stop();
- track.flush();
+ try {
+ // TODO: add synchronous versions
+ track.stop();
+ track.flush();
+ } catch (IllegalStateException e) {
+ // ignore exception, AudioTrack could have already been stopped or
+ // released by the user of the AudioPolicy
+ }
}
}
if (mCaptors != null) {
@@ -730,8 +735,13 @@
if (record == null) {
break;
}
- // TODO: if needed: implement an invalidate method
- record.stop();
+ try {
+ // TODO: if needed: implement an invalidate method
+ record.stop();
+ } catch (IllegalStateException e) {
+ // ignore exception, AudioRecord could have already been stopped or
+ // released by the user of the AudioPolicy
+ }
}
}
}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 4d68a6a..21378c8 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -48,6 +48,6 @@
// These commands relate to volume handling
void setPlaybackToLocal(in AudioAttributes attributes);
- void setPlaybackToRemote(int control, int max);
+ void setPlaybackToRemote(int control, int max, @nullable String controlId);
void setCurrentVolume(int currentVolume);
}
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 1812d9c..c2f6206 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -964,16 +964,26 @@
private final int mMaxVolume;
private final int mCurrentVolume;
private final AudioAttributes mAudioAttrs;
+ private final String mVolumeControlId;
/**
* @hide
*/
public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs) {
+ this(type, control, max, current, attrs, null);
+ }
+
+ /**
+ * @hide
+ */
+ public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs,
+ String volumeControlId) {
mVolumeType = type;
mVolumeControl = control;
mMaxVolume = max;
mCurrentVolume = current;
mAudioAttrs = attrs;
+ mVolumeControlId = volumeControlId;
}
PlaybackInfo(Parcel in) {
@@ -982,6 +992,7 @@
mMaxVolume = in.readInt();
mCurrentVolume = in.readInt();
mAudioAttrs = in.readParcelable(null);
+ mVolumeControlId = in.readString();
}
/**
@@ -1042,11 +1053,24 @@
return mAudioAttrs;
}
+ /**
+ * Gets the volume control ID for this session. It can be used to identify which
+ * volume provider is used by the session.
+ *
+ * @return the volume control ID for this session or {@code null} if it's local playback
+ * or not set.
+ * @see VolumeProvider#getVolumeControlId()
+ */
+ @Nullable
+ public String getVolumeControlId() {
+ return mVolumeControlId;
+ }
+
@Override
public String toString() {
return "volumeType=" + mVolumeType + ", volumeControl=" + mVolumeControl
+ ", maxVolume=" + mMaxVolume + ", currentVolume=" + mCurrentVolume
- + ", audioAttrs=" + mAudioAttrs;
+ + ", audioAttrs=" + mAudioAttrs + ", volumeControlId=" + mVolumeControlId;
}
@Override
@@ -1061,6 +1085,7 @@
dest.writeInt(mMaxVolume);
dest.writeInt(mCurrentVolume);
dest.writeParcelable(mAudioAttrs, flags);
+ dest.writeString(mVolumeControlId);
}
public static final @android.annotation.NonNull Parcelable.Creator<PlaybackInfo> CREATOR =
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 3adee59..9953626 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -335,7 +335,7 @@
try {
mBinder.setPlaybackToRemote(volumeProvider.getVolumeControl(),
- volumeProvider.getMaxVolume());
+ volumeProvider.getMaxVolume(), volumeProvider.getVolumeControlId());
mBinder.setCurrentVolume(volumeProvider.getCurrentVolume());
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 938ffcd..dd4dac2 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -44,6 +44,7 @@
import com.android.internal.util.Preconditions;
import java.util.HashMap;
+import java.util.Objects;
import java.util.UUID;
/**
@@ -175,19 +176,40 @@
* Factory constructor to create a SoundModel instance for use with methods in this
* class.
*/
- public static Model create(UUID modelUuid, UUID vendorUuid, byte[] data) {
- return new Model(new SoundTrigger.GenericSoundModel(modelUuid,
- vendorUuid, data));
+ @NonNull
+ public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid,
+ @Nullable byte[] data, int version) {
+ Objects.requireNonNull(modelUuid);
+ Objects.requireNonNull(vendorUuid);
+ return new Model(new SoundTrigger.GenericSoundModel(modelUuid, vendorUuid, data,
+ version));
}
+ /**
+ * Factory constructor to create a SoundModel instance for use with methods in this
+ * class.
+ */
+ @NonNull
+ public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid,
+ @Nullable byte[] data) {
+ return create(modelUuid, vendorUuid, data, -1);
+ }
+
+ @NonNull
public UUID getModelUuid() {
return mGenericSoundModel.uuid;
}
+ @NonNull
public UUID getVendorUuid() {
return mGenericSoundModel.vendorUuid;
}
+ public int getVersion() {
+ return mGenericSoundModel.version;
+ }
+
+ @Nullable
public byte[] getModelData() {
return mGenericSoundModel.data;
}
diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
index 149c1cd..726af76 100644
--- a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
+++ b/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
@@ -44,4 +44,11 @@
* and this event will be sent in addition to the abort event.
*/
void onRecognitionAvailabilityChange(boolean available);
+ /**
+ * Notifies the client that the associated module has crashed and restarted. The module instance
+ * is no longer usable and will throw a ServiceSpecificException with a Status.DEAD_OBJECT code
+ * for every call. The client should detach, then re-attach to the module in order to get a new,
+ * usable instance. All state for this module has been lost.
+ */
+ void onModuleDied();
}
diff --git a/media/java/android/media/soundtrigger_middleware/Status.aidl b/media/java/android/media/soundtrigger_middleware/Status.aidl
index 5d082e2..85ccacf 100644
--- a/media/java/android/media/soundtrigger_middleware/Status.aidl
+++ b/media/java/android/media/soundtrigger_middleware/Status.aidl
@@ -28,4 +28,6 @@
OPERATION_NOT_SUPPORTED = 2,
/** Temporary lack of permission. */
TEMPORARY_PERMISSION_DENIED = 3,
+ /** The object on which this method is called is dead and all of its state is lost. */
+ DEAD_OBJECT = 4,
}
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index b076bb6..b5e9d1b 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -60,6 +60,7 @@
void createSession(in ITvInputClient client, in String inputId, boolean isRecordingSession,
int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
+ int getClientPid(in String sessionId);
void setMainSession(in IBinder sessionToken, int userId);
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
index f90c504..8ccf13a 100755
--- a/media/java/android/media/tv/ITvInputService.aidl
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -30,8 +30,9 @@
void registerCallback(in ITvInputServiceCallback callback);
void unregisterCallback(in ITvInputServiceCallback callback);
void createSession(in InputChannel channel, in ITvInputSessionCallback callback,
- in String inputId);
- void createRecordingSession(in ITvInputSessionCallback callback, in String inputId);
+ in String inputId, in String sessionId);
+ void createRecordingSession(in ITvInputSessionCallback callback, in String inputId,
+ in String sessionId);
// For hardware TvInputService
void notifyHardwareAdded(in TvInputHardwareInfo hardwareInfo);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 854ea43..630d819 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -266,6 +266,15 @@
public static final int INPUT_STATE_DISCONNECTED = 2;
/**
+ * An unknown state of the client pid gets from the TvInputManager. Client gets this value when
+ * query through {@link getClientPid(String sessionId)} fails.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int UNKNOWN_CLIENT_PID = -1;
+
+ /**
* Broadcast intent action when the user blocked content ratings change. For use with the
* {@link #isRatingBlocked}.
*/
@@ -1484,6 +1493,21 @@
}
/**
+ * Get a the client pid when creating the session with the session id provided.
+ *
+ * @param sessionId a String of session id that is used to query the client pid.
+ * @return the client pid when created the session. Returns {@link #UNKNOWN_CLIENT_PID}
+ * if the call fails.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
+ public int getClientPid(@NonNull String sessionId) {
+ return getClientPidInternal(sessionId);
+ };
+
+ /**
* Creates a recording {@link Session} for a given TV input.
*
* <p>The number of sessions that can be created at the same time is limited by the capability
@@ -1516,6 +1540,17 @@
}
}
+ private int getClientPidInternal(String sessionId) {
+ Preconditions.checkNotNull(sessionId);
+ int clientPid = UNKNOWN_CLIENT_PID;
+ try {
+ clientPid = mService.getClientPid(sessionId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return clientPid;
+ }
+
/**
* Returns the TvStreamConfig list of the given TV input.
*
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 7fbb337..629dc7c 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -124,7 +124,7 @@
@Override
public void createSession(InputChannel channel, ITvInputSessionCallback cb,
- String inputId) {
+ String inputId, String sessionId) {
if (channel == null) {
Log.w(TAG, "Creating session without input channel");
}
@@ -135,17 +135,20 @@
args.arg1 = channel;
args.arg2 = cb;
args.arg3 = inputId;
+ args.arg4 = sessionId;
mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget();
}
@Override
- public void createRecordingSession(ITvInputSessionCallback cb, String inputId) {
+ public void createRecordingSession(ITvInputSessionCallback cb, String inputId,
+ String sessionId) {
if (cb == null) {
return;
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = cb;
args.arg2 = inputId;
+ args.arg3 = sessionId;
mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_RECORDING_SESSION, args)
.sendToTarget();
}
@@ -208,6 +211,37 @@
}
/**
+ * Returns a concrete implementation of {@link Session}.
+ *
+ * <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager,
+ * it needs to override this method to get the sessionId passed. When no overriding, this method
+ * calls {@link #onCreateSession(String)} defaultly.
+ *
+ * @param inputId The ID of the TV input associated with the session.
+ * @param sessionId the unique sessionId created by TIF when session is created.
+ */
+ @Nullable
+ public Session onCreateSession(@NonNull String inputId, @NonNull String sessionId) {
+ return onCreateSession(inputId);
+ }
+
+ /**
+ * Returns a concrete implementation of {@link RecordingSession}.
+ *
+ * <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager,
+ * it needs to override this method to get the sessionId passed. When no overriding, this method
+ * calls {@link #onCreateRecordingSession(String)} defaultly.
+ *
+ * @param inputId The ID of the TV input associated with the recording session.
+ * @param sessionId the unique sessionId created by TIF when session is created.
+ */
+ @Nullable
+ public RecordingSession onCreateRecordingSession(
+ @NonNull String inputId, @NonNull String sessionId) {
+ return onCreateRecordingSession(inputId);
+ }
+
+ /**
* Returns a new {@link TvInputInfo} object if this service is responsible for
* {@code hardwareInfo}; otherwise, return {@code null}. Override to modify default behavior of
* ignoring all hardware input.
@@ -2032,8 +2066,9 @@
InputChannel channel = (InputChannel) args.arg1;
ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
String inputId = (String) args.arg3;
+ String sessionId = (String) args.arg4;
args.recycle();
- Session sessionImpl = onCreateSession(inputId);
+ Session sessionImpl = onCreateSession(inputId, sessionId);
if (sessionImpl == null) {
try {
// Failed to create a session.
@@ -2103,8 +2138,10 @@
SomeArgs args = (SomeArgs) msg.obj;
ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg1;
String inputId = (String) args.arg2;
+ String sessionId = (String) args.arg3;
args.recycle();
- RecordingSession recordingSessionImpl = onCreateRecordingSession(inputId);
+ RecordingSession recordingSessionImpl =
+ onCreateRecordingSession(inputId, sessionId);
if (recordingSessionImpl == null) {
try {
// Failed to create a recording session.
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index f181b49..c7cc9e6d 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
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.Tuner.LnbCallback;
@@ -37,6 +38,7 @@
*
* @hide
*/
+@SystemApi
public class Lnb implements AutoCloseable {
/** @hide */
@IntDef({VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V,
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 862489f..e3dfaeb 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -16,6 +16,7 @@
package android.media.tv.tuner;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -32,11 +33,13 @@
import android.media.tv.tuner.frontend.FrontendCallback;
import android.media.tv.tuner.frontend.FrontendInfo;
import android.media.tv.tuner.frontend.FrontendStatus;
+import android.media.tv.tuner.frontend.ScanCallback;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* This class is used to interact with hardware tuners devices.
@@ -70,6 +73,10 @@
private List<Integer> mLnbIds;
private Lnb mLnb;
+ @Nullable
+ private ScanCallback mScanCallback;
+ @Nullable
+ private Executor mScanCallbackExecutor;
/**
* Constructs a Tuner instance.
@@ -231,29 +238,6 @@
private Frontend(int id) {
mId = id;
}
-
- public void setCallback(@Nullable FrontendCallback callback, @Nullable Handler handler) {
- mCallback = callback;
-
- if (mCallback == null) {
- return;
- }
-
- if (handler == null) {
- // use default looper if handler is null
- if (mHandler == null) {
- mHandler = createEventHandler();
- }
- return;
- }
-
- Looper looper = handler.getLooper();
- if (mHandler != null && mHandler.getLooper() == looper) {
- // the same looper. reuse mHandler
- return;
- }
- mHandler = new EventHandler(looper);
- }
}
/**
@@ -286,18 +270,32 @@
* Scan channels.
* @hide
*/
- public int scan(@NonNull FrontendSettings settings, @FrontendScanType int scanType) {
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ public int scan(@NonNull FrontendSettings settings, @FrontendScanType int scanType,
+ @NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
+ mScanCallback = scanCallback;
+ mScanCallbackExecutor = executor;
return nativeScan(settings.getType(), settings, scanType);
}
/**
* Stops a previous scanning.
*
- * If the method completes successfully, the frontend stop previous scanning.
+ * <p>
+ * The {@link ScanCallback} and it's {@link Executor} will be removed.
+ *
+ * <p>
+ * If the method completes successfully, the frontend stopped previous scanning.
+ *
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public int stopScan() {
- return nativeStopScan();
+ TunerUtils.checkTunerPermission(mContext);
+ int retVal = nativeStopScan();
+ mScanCallback = null;
+ mScanCallbackExecutor = null;
+ return retVal;
}
/**
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index 944ed8c..4532122 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -1015,10 +1015,13 @@
Constants.FrontendAtsc3DemodOutputFormat.BASEBAND_PACKET;
/** @hide */
- @IntDef({FRONTEND_DVBS_STANDARD_AUTO, FRONTEND_DVBS_STANDARD_S, FRONTEND_DVBS_STANDARD_S2,
- FRONTEND_DVBS_STANDARD_S2X})
+ @IntDef(prefix = "FRONTEND_DVBS_STANDARD",
+ value = {FRONTEND_DVBS_STANDARD_AUTO, FRONTEND_DVBS_STANDARD_S,
+ FRONTEND_DVBS_STANDARD_S2,
+ FRONTEND_DVBS_STANDARD_S2X})
@Retention(RetentionPolicy.SOURCE)
- public @interface FrontendDvbsStandard {}
+ public @interface FrontendDvbsStandard {
+ }
/** @hide */
public static final int FRONTEND_DVBS_STANDARD_AUTO = Constants.FrontendDvbsStandard.AUTO;
/** @hide */
@@ -1210,6 +1213,20 @@
Constants.FrontendDvbtGuardInterval.INTERVAL_19_256;
/** @hide */
+ @IntDef(prefix = "FRONTEND_DVBT_STANDARD",
+ value = {FRONTEND_DVBT_STANDARD_AUTO, FRONTEND_DVBT_STANDARD_T,
+ FRONTEND_DVBT_STANDARD_T2}
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendDvbtStandard {}
+ /** @hide */
+ public static final int FRONTEND_DVBT_STANDARD_AUTO = Constants.FrontendDvbtStandard.AUTO;
+ /** @hide */
+ public static final int FRONTEND_DVBT_STANDARD_T = Constants.FrontendDvbtStandard.T;
+ /** @hide */
+ public static final int FRONTEND_DVBT_STANDARD_T2 = Constants.FrontendDvbtStandard.T2;
+
+ /** @hide */
@IntDef({FRONTEND_ISDBS_CODERATE_UNDEFINED, FRONTEND_ISDBS_CODERATE_AUTO,
FRONTEND_ISDBS_CODERATE_1_2, FRONTEND_ISDBS_CODERATE_2_3, FRONTEND_ISDBS_CODERATE_3_4,
FRONTEND_ISDBS_CODERATE_5_6, FRONTEND_ISDBS_CODERATE_7_8})
@@ -1298,37 +1315,30 @@
/**
* Operation succeeded.
- * @hide
*/
public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
/**
* Operation failed because the corresponding resources are not available.
- * @hide
*/
public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
/**
* Operation failed because the corresponding resources are not initialized.
- * @hide
*/
public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
/**
* Operation failed because it's not in a valid state.
- * @hide
*/
public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
/**
* Operation failed because there are invalid arguments.
- * @hide
*/
public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
/**
* Memory allocation failed.
- * @hide
*/
public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
/**
* Operation failed due to unknown errors.
- * @hide
*/
public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
index 99b10cd..6496627 100644
--- a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
import java.lang.annotation.Retention;
@@ -28,6 +29,7 @@
*
* @hide
*/
+@SystemApi
public abstract class FilterConfiguration {
/** @hide */
@@ -57,7 +59,7 @@
public static final int FILTER_TYPE_ALP = Constants.DemuxFilterMainType.ALP;
@Nullable
- private final Settings mSettings;
+ /* package */ final Settings mSettings;
/* package */ FilterConfiguration(Settings settings) {
mSettings = settings;
diff --git a/media/java/android/media/tv/tuner/filter/PesSettings.java b/media/java/android/media/tv/tuner/filter/PesSettings.java
index f38abf1..bfa1f8c 100644
--- a/media/java/android/media/tv/tuner/filter/PesSettings.java
+++ b/media/java/android/media/tv/tuner/filter/PesSettings.java
@@ -17,6 +17,9 @@
package android.media.tv.tuner.filter;
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;
@@ -26,6 +29,7 @@
*
* @hide
*/
+@SystemApi
public class PesSettings extends Settings {
private final int mStreamId;
private final boolean mIsRaw;
@@ -37,12 +41,32 @@
}
/**
+ * Gets stream ID.
+ */
+ public int getStreamId() {
+ return mStreamId;
+ }
+
+ /**
+ * Returns whether the data is raw.
+ *
+ * @return {@code true} if the data is raw. Filter sends onFilterStatus callback
+ * instead of onFilterEvent for raw data. {@code false} otherwise.
+ */
+ public boolean isRaw() {
+ return mIsRaw;
+ }
+
+ /**
* Creates a builder for {@link PesSettings}.
*
* @param mainType the filter main type of the settings.
+ * @param context the context of the caller.
*/
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@NonNull
- public static Builder newBuilder(@FilterType int mainType) {
+ public static Builder builder(@NonNull Context context, @FilterType int mainType) {
+ TunerUtils.checkTunerPermission(context);
return new Builder(mainType);
}
@@ -70,13 +94,13 @@
}
/**
- * Sets whether it's raw.
+ * Sets whether the data is raw.
*
* @param isRaw {@code true} if the data is raw. Filter sends onFilterStatus callback
* instead of onFilterEvent for raw data. {@code false} otherwise.
*/
@NonNull
- public Builder setIsRaw(boolean isRaw) {
+ public Builder setRaw(boolean isRaw) {
mIsRaw = isRaw;
return this;
}
diff --git a/media/java/android/media/tv/tuner/filter/Settings.java b/media/java/android/media/tv/tuner/filter/Settings.java
index 146aca7..9155926 100644
--- a/media/java/android/media/tv/tuner/filter/Settings.java
+++ b/media/java/android/media/tv/tuner/filter/Settings.java
@@ -16,11 +16,14 @@
package android.media.tv.tuner.filter;
+import android.annotation.SystemApi;
+
/**
* Settings for filters of different subtypes.
*
* @hide
*/
+@SystemApi
public abstract class Settings {
private final int mType;
diff --git a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
index d0241b6..5c38cfa 100644
--- a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
@@ -17,12 +17,18 @@
package android.media.tv.tuner.filter;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
/**
* Filter configuration for a TS filter.
*
* @hide
*/
+@SystemApi
public class TsFilterConfiguration extends FilterConfiguration {
private final int mTpid;
@@ -37,10 +43,28 @@
}
/**
- * Creates a builder for {@link TsFilterConfiguration}.
+ * Gets the {@link Settings} object of this filter configuration.
*/
+ @Nullable
+ public Settings getSettings() {
+ return mSettings;
+ }
+ /**
+ * Gets Tag Protocol ID.
+ */
+ public int getTpid() {
+ return mTpid;
+ }
+
+ /**
+ * Creates a builder for {@link TsFilterConfiguration}.
+ *
+ * @param context 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();
}
@@ -51,6 +75,9 @@
private Settings mSettings;
private int mTpid;
+ private Builder() {
+ }
+
/**
* Sets filter settings.
*
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java b/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
index 0992eb6..9c4f460 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
@@ -27,10 +27,4 @@
* Invoked when there is a frontend event.
*/
void onEvent(int frontendEventType);
-
- /**
- * Invoked when there is a scan message.
- * @param msg
- */
- void onScanMessage(ScanMessage msg);
}
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
new file mode 100644
index 0000000..8118fcc
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -0,0 +1,80 @@
+/*
+ * 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.media.tv.tuner.TunerConstants;
+
+/**
+ * Scan callback.
+ *
+ * @hide
+ */
+public interface ScanCallback {
+ /** Scan locked the signal. */
+ void onLocked(boolean isLocked);
+
+ /** Scan stopped. */
+ void onEnd(boolean isEnd);
+
+ /** scan progress percent (0..100) */
+ void onProgress(int percent);
+
+ /** Signal frequency in Hertz */
+ void onFrequencyReport(int frequency);
+
+ /** Symbols per second */
+ void onSymbolRate(int rate);
+
+ /** Locked Plp Ids for DVBT2 frontend. */
+ void onPlpIds(int[] plpIds);
+
+ /** Locked group Ids for DVBT2 frontend. */
+ void onGroupIds(int[] groupIds);
+
+ /** Stream Ids. */
+ void onInputStreamIds(int[] inputStreamIds);
+
+ /** Locked signal standard. */
+ void onDvbsStandard(@TunerConstants.FrontendDvbsStandard int dvbsStandandard);
+
+ /** Locked signal standard. */
+ void onDvbtStandard(@TunerConstants.FrontendDvbtStandard int dvbtStandard);
+
+ /** PLP status in a tuned frequency band for ATSC3 frontend. */
+ void onAtsc3PlpInfos(Atsc3PlpInfo[] atsc3PlpInfos);
+
+ /** PLP information for ATSC3. */
+ class Atsc3PlpInfo {
+ private final int mPlpId;
+ private final boolean mLlsFlag;
+
+ private Atsc3PlpInfo(int plpId, boolean llsFlag) {
+ mPlpId = plpId;
+ mLlsFlag = llsFlag;
+ }
+
+ /** Gets PLP IDs. */
+ public int getPlpId() {
+ return mPlpId;
+ }
+
+ /** Gets LLS flag. */
+ public boolean getLlsFlag() {
+ return mLlsFlag;
+ }
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/ScanMessage.java b/media/java/android/media/tv/tuner/frontend/ScanMessage.java
deleted file mode 100644
index dd687dd..0000000
--- a/media/java/android/media/tv/tuner/frontend/ScanMessage.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.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;
-
-/**
- * Message from frontend during scan operations.
- *
- * @hide
- */
-public class ScanMessage {
-
- /** @hide */
- @IntDef({
- LOCKED,
- END,
- PROGRESS_PERCENT,
- FREQUENCY,
- SYMBOL_RATE,
- PLP_IDS,
- GROUP_IDS,
- INPUT_STREAM_IDS,
- STANDARD,
- ATSC3_PLP_INFO
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Type {}
- /** @hide */
- public static final int LOCKED = Constants.FrontendScanMessageType.LOCKED;
- /** @hide */
- public static final int END = Constants.FrontendScanMessageType.END;
- /** @hide */
- public static final int PROGRESS_PERCENT = Constants.FrontendScanMessageType.PROGRESS_PERCENT;
- /** @hide */
- public static final int FREQUENCY = Constants.FrontendScanMessageType.FREQUENCY;
- /** @hide */
- public static final int SYMBOL_RATE = Constants.FrontendScanMessageType.SYMBOL_RATE;
- /** @hide */
- public static final int PLP_IDS = Constants.FrontendScanMessageType.PLP_IDS;
- /** @hide */
- public static final int GROUP_IDS = Constants.FrontendScanMessageType.GROUP_IDS;
- /** @hide */
- public static final int INPUT_STREAM_IDS = Constants.FrontendScanMessageType.INPUT_STREAM_IDS;
- /** @hide */
- public static final int STANDARD = Constants.FrontendScanMessageType.STANDARD;
- /** @hide */
- public static final int ATSC3_PLP_INFO = Constants.FrontendScanMessageType.ATSC3_PLP_INFO;
-
- private final int mType;
- private final Object mValue;
-
- private ScanMessage(int type, Object value) {
- mType = type;
- mValue = value;
- }
-
- /** Gets scan message type. */
- @Type
- public int getMessageType() {
- return mType;
- }
- /** Message indicates whether frontend is locked or not. */
- public boolean getIsLocked() {
- if (mType != LOCKED) {
- throw new IllegalStateException();
- }
- return (Boolean) mValue;
- }
- /** Message indicates whether the scan has reached the end or not. */
- public boolean getIsEnd() {
- if (mType != END) {
- throw new IllegalStateException();
- }
- return (Boolean) mValue;
- }
- /** Progress message in percent. */
- public int getProgressPercent() {
- if (mType != PROGRESS_PERCENT) {
- throw new IllegalStateException();
- }
- return (Integer) mValue;
- }
- /** Gets frequency. */
- public int getFrequency() {
- if (mType != FREQUENCY) {
- throw new IllegalStateException();
- }
- return (Integer) mValue;
- }
- /** Gets symbol rate. */
- public int getSymbolRate() {
- if (mType != SYMBOL_RATE) {
- throw new IllegalStateException();
- }
- return (Integer) mValue;
- }
- /** Gets PLP IDs. */
- public int[] getPlpIds() {
- if (mType != PLP_IDS) {
- throw new IllegalStateException();
- }
- return (int[]) mValue;
- }
- /** Gets group IDs. */
- public int[] getGroupIds() {
- if (mType != GROUP_IDS) {
- throw new IllegalStateException();
- }
- return (int[]) mValue;
- }
- /** Gets Input stream IDs. */
- public int[] getInputStreamIds() {
- if (mType != INPUT_STREAM_IDS) {
- throw new IllegalStateException();
- }
- return (int[]) mValue;
- }
- /** Gets the DVB-T or DVB-S standard. */
- public int getStandard() {
- if (mType != STANDARD) {
- throw new IllegalStateException();
- }
- return (int) mValue;
- }
-
- /** Gets PLP information for ATSC3. */
- public Atsc3PlpInfo[] getAtsc3PlpInfos() {
- if (mType != ATSC3_PLP_INFO) {
- throw new IllegalStateException();
- }
- return (Atsc3PlpInfo[]) mValue;
- }
-
- /** PLP information for ATSC3. */
- public static class Atsc3PlpInfo {
- private final int mPlpId;
- private final boolean mLlsFlag;
-
- private Atsc3PlpInfo(int plpId, boolean llsFlag) {
- mPlpId = plpId;
- mLlsFlag = llsFlag;
- }
-
- /** Gets PLP IDs. */
- public int getPlpId() {
- return mPlpId;
- }
- /** Gets LLS flag. */
- public boolean getLlsFlag() {
- return mLlsFlag;
- }
- }
-}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 536a061..aeacd8f 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -19,6 +19,7 @@
"android_media_MediaProfiles.cpp",
"android_media_MediaRecorder.cpp",
"android_media_MediaSync.cpp",
+ "android_media_MediaTranscodeManager.cpp",
"android_media_ResampleInputStream.cpp",
"android_media_Streams.cpp",
"android_media_SyncParams.cpp",
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 963b650..5cb42a9a 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1453,6 +1453,7 @@
extern int register_android_mtp_MtpDatabase(JNIEnv *env);
extern int register_android_mtp_MtpDevice(JNIEnv *env);
extern int register_android_mtp_MtpServer(JNIEnv *env);
+extern int register_android_media_MediaTranscodeManager(JNIEnv *env);
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
@@ -1565,6 +1566,11 @@
goto bail;
}
+ if (register_android_media_MediaTranscodeManager(env) < 0) {
+ ALOGE("ERROR: MediaTranscodeManager native registration failed");
+ goto bail;
+ }
+
/* success -- return valid version number */
result = JNI_VERSION_1_4;
diff --git a/media/jni/android_media_MediaTranscodeManager.cpp b/media/jni/android_media_MediaTranscodeManager.cpp
new file mode 100644
index 0000000..0b4048c
--- /dev/null
+++ b/media/jni/android_media_MediaTranscodeManager.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 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 LOG_NDEBUG 0
+#define LOG_TAG "MediaTranscodeManager_JNI"
+
+#include "android_runtime/AndroidRuntime.h"
+#include "jni.h"
+
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+
+namespace {
+
+// NOTE: Keep these enums in sync with their equivalents in MediaTranscodeManager.java.
+enum {
+ ID_INVALID = -1
+};
+
+enum {
+ EVENT_JOB_STARTED = 1,
+ EVENT_JOB_PROGRESSED = 2,
+ EVENT_JOB_FINISHED = 3,
+};
+
+enum {
+ RESULT_NONE = 1,
+ RESULT_SUCCESS = 2,
+ RESULT_ERROR = 3,
+ RESULT_CANCELED = 4,
+};
+
+struct {
+ jmethodID postEventFromNative;
+} gMediaTranscodeManagerClassInfo;
+
+using namespace android;
+
+void android_media_MediaTranscodeManager_native_init(JNIEnv *env, jclass clazz) {
+ ALOGV("android_media_MediaTranscodeManager_native_init");
+
+ gMediaTranscodeManagerClassInfo.postEventFromNative = env->GetMethodID(
+ clazz, "postEventFromNative", "(IJI)V");
+ LOG_ALWAYS_FATAL_IF(gMediaTranscodeManagerClassInfo.postEventFromNative == NULL,
+ "can't find android/media/MediaTranscodeManager.postEventFromNative");
+}
+
+jlong android_media_MediaTranscodeManager_requestUniqueJobID(
+ JNIEnv *env __unused, jobject thiz __unused) {
+ ALOGV("android_media_MediaTranscodeManager_reserveUniqueJobID");
+ static std::atomic_int32_t sJobIDCounter{0};
+ jlong id = (jlong)++sJobIDCounter;
+ return id;
+}
+
+jboolean android_media_MediaTranscodeManager_enqueueTranscodingRequest(
+ JNIEnv *env, jobject thiz, jlong id, jobject request, jobject context __unused) {
+ ALOGV("android_media_MediaTranscodeManager_enqueueTranscodingRequest");
+ if (!request) {
+ return ID_INVALID;
+ }
+
+ env->CallVoidMethod(thiz, gMediaTranscodeManagerClassInfo.postEventFromNative,
+ EVENT_JOB_FINISHED, id, RESULT_ERROR);
+ return true;
+}
+
+void android_media_MediaTranscodeManager_cancelTranscodingRequest(
+ JNIEnv *env __unused, jobject thiz __unused, jlong jobID __unused) {
+ ALOGV("android_media_MediaTranscodeManager_cancelTranscodingRequest");
+}
+
+const JNINativeMethod gMethods[] = {
+ { "native_init", "()V",
+ (void *)android_media_MediaTranscodeManager_native_init },
+ { "native_requestUniqueJobID", "()J",
+ (void *)android_media_MediaTranscodeManager_requestUniqueJobID },
+ { "native_enqueueTranscodingRequest",
+ "(JLandroid/media/MediaTranscodeManager$TranscodingRequest;Landroid/content/Context;)Z",
+ (void *)android_media_MediaTranscodeManager_enqueueTranscodingRequest },
+ { "native_cancelTranscodingRequest", "(J)V",
+ (void *)android_media_MediaTranscodeManager_cancelTranscodingRequest },
+};
+
+} // namespace anonymous
+
+int register_android_media_MediaTranscodeManager(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/media/MediaTranscodeManager", gMethods, NELEM(gMethods));
+}
diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp
index f0fbc50..ecbe2b3 100644
--- a/media/tests/MediaFrameworkTest/Android.bp
+++ b/media/tests/MediaFrameworkTest/Android.bp
@@ -7,6 +7,7 @@
],
static_libs: [
"mockito-target-minus-junit4",
+ "androidx.test.ext.junit",
"androidx.test.rules",
"android-ex-camera2",
],
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediatranscodemanager/MediaTranscodeManagerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediatranscodemanager/MediaTranscodeManagerTest.java
new file mode 100644
index 0000000..eeda50e
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediatranscodemanager/MediaTranscodeManagerTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.mediaframeworktest.functional.mediatranscodemanager;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.media.MediaTranscodeManager;
+import android.util.Log;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class MediaTranscodeManagerTest {
+ private static final String TAG = "MediaTranscodeManagerTest";
+
+ /** The time to wait for the transcode operation to complete before failing the test. */
+ private static final int TRANSCODE_TIMEOUT_SECONDS = 2;
+
+ @Test
+ public void testMediaTranscodeManager() throws InterruptedException {
+ Log.d(TAG, "Starting: testMediaTranscodeManager");
+
+ Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+ MediaTranscodeManager.TranscodingRequest request =
+ new MediaTranscodeManager.TranscodingRequest.Builder().build();
+ Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+ MediaTranscodeManager mediaTranscodeManager =
+ MediaTranscodeManager.getInstance(ApplicationProvider.getApplicationContext());
+ assertNotNull(mediaTranscodeManager);
+
+ MediaTranscodeManager.TranscodingJob job;
+ job = mediaTranscodeManager.enqueueTranscodingRequest(request, listenerExecutor,
+ transcodingJob -> {
+ Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
+ transcodeCompleteSemaphore.release();
+ });
+ assertNotNull(job);
+
+ job.setOnProgressChangedListener(
+ listenerExecutor, progress -> Log.d(TAG, "Progress: " + progress));
+
+ if (job != null) {
+ Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
+ boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+ TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertTrue("Transcode failed to complete in time.", finishedOnTime);
+ }
+ }
+}
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 b81c378..ed93112 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -22,7 +22,7 @@
import android.content.Intent;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderService;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
import android.os.IBinder;
import android.text.TextUtils;
@@ -63,7 +63,7 @@
"com.android.mediarouteprovider.FEATURE_SPECIAL";
Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
- Map<String, String> mRouteSessionMap = new HashMap<>();
+ Map<String, String> mRouteIdToSessionId = new HashMap<>();
private int mNextSessionId = 1000;
private void initializeRoutes() {
@@ -172,7 +172,7 @@
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.
- notifySessionCreated(/* sessionInfo= */ null, requestId);
+ notifySessionCreationFailed(requestId);
return;
}
maybeDeselectRoute(routeId);
@@ -183,9 +183,9 @@
mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
.setClientPackageName(packageName)
.build());
- mRouteSessionMap.put(routeId, sessionId);
+ mRouteIdToSessionId.put(routeId, sessionId);
- RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
sessionId, packageName, routeFeature)
.addSelectedRoute(routeId)
.addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
@@ -196,9 +196,14 @@
}
@Override
- public void onDestroySession(String sessionId, RouteSessionInfo lastSessionInfo) {
- for (String routeId : lastSessionInfo.getSelectedRoutes()) {
- mRouteSessionMap.remove(routeId);
+ public void onReleaseSession(String sessionId) {
+ RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
+ if (sessionInfo == null) {
+ return;
+ }
+
+ for (String routeId : sessionInfo.getSelectedRoutes()) {
+ mRouteIdToSessionId.remove(routeId);
MediaRoute2Info route = mRoutes.get(routeId);
if (route != null) {
mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
@@ -206,11 +211,12 @@
.build());
}
}
+ notifySessionReleased(sessionId);
}
@Override
public void onSelectRoute(String sessionId, String routeId) {
- RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
+ RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
MediaRoute2Info route = mRoutes.get(routeId);
if (route == null || sessionInfo == null) {
return;
@@ -220,65 +226,62 @@
mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
.setClientPackageName(sessionInfo.getClientPackageName())
.build());
- mRouteSessionMap.put(routeId, sessionId);
+ mRouteIdToSessionId.put(routeId, sessionId);
- RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+ RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
.addSelectedRoute(routeId)
.removeSelectableRoute(routeId)
.addDeselectableRoute(routeId)
.build();
- updateSessionInfo(newSessionInfo);
- notifySessionInfoChanged(newSessionInfo);
+ notifySessionUpdated(newSessionInfo);
}
@Override
public void onDeselectRoute(String sessionId, String routeId) {
- RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
+ RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
MediaRoute2Info route = mRoutes.get(routeId);
- mRouteSessionMap.remove(routeId);
if (sessionInfo == null || route == null
|| !sessionInfo.getSelectedRoutes().contains(routeId)) {
return;
}
+ mRouteIdToSessionId.remove(routeId);
mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
.setClientPackageName(null)
.build());
if (sessionInfo.getSelectedRoutes().size() == 1) {
- releaseSession(sessionId);
+ notifySessionReleased(sessionId);
return;
}
- RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+ RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
.removeSelectedRoute(routeId)
.addSelectableRoute(routeId)
.removeDeselectableRoute(routeId)
.build();
- updateSessionInfo(newSessionInfo);
- notifySessionInfoChanged(newSessionInfo);
+ notifySessionUpdated(newSessionInfo);
}
@Override
public void onTransferToRoute(String sessionId, String routeId) {
- RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
- RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+ RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
+ RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
.clearSelectedRoutes()
.addSelectedRoute(routeId)
.removeDeselectableRoute(routeId)
.removeTransferrableRoute(routeId)
.build();
- updateSessionInfo(newSessionInfo);
- notifySessionInfoChanged(newSessionInfo);
+ notifySessionUpdated(newSessionInfo);
}
void maybeDeselectRoute(String routeId) {
- if (!mRouteSessionMap.containsKey(routeId)) {
+ if (!mRouteIdToSessionId.containsKey(routeId)) {
return;
}
- String sessionId = mRouteSessionMap.get(routeId);
+ String sessionId = mRouteIdToSessionId.get(routeId);
onDeselectRoute(sessionId, routeId);
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index 908d7bf..007229a 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -48,10 +48,10 @@
import android.media.MediaRoute2Info;
import android.media.MediaRouter2;
import android.media.MediaRouter2.RouteCallback;
-import android.media.MediaRouter2.RouteSessionController;
+import android.media.MediaRouter2.RoutingController;
import android.media.MediaRouter2.SessionCallback;
import android.media.RouteDiscoveryPreference;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
import android.net.Uri;
import android.os.Parcel;
import android.support.test.InstrumentationRegistry;
@@ -253,12 +253,12 @@
final CountDownLatch successLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
- final List<RouteSessionController> controllers = new ArrayList<>();
+ final List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
SessionCallback sessionCallback = new SessionCallback() {
@Override
- public void onSessionCreated(RouteSessionController controller) {
+ public void onSessionCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
@@ -302,12 +302,12 @@
final CountDownLatch successLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
- final List<RouteSessionController> controllers = new ArrayList<>();
+ final List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
SessionCallback sessionCallback = new SessionCallback() {
@Override
- public void onSessionCreated(RouteSessionController controller) {
+ public void onSessionCreated(RoutingController controller) {
controllers.add(controller);
successLatch.countDown();
}
@@ -346,12 +346,12 @@
final CountDownLatch successLatch = new CountDownLatch(2);
final CountDownLatch failureLatch = new CountDownLatch(1);
- final List<RouteSessionController> createdControllers = new ArrayList<>();
+ final List<RoutingController> createdControllers = new ArrayList<>();
// Create session with this route
SessionCallback sessionCallback = new SessionCallback() {
@Override
- public void onSessionCreated(RouteSessionController controller) {
+ public void onSessionCreated(RoutingController controller) {
createdControllers.add(controller);
successLatch.countDown();
}
@@ -384,8 +384,8 @@
// Created controllers should have proper info
assertEquals(2, createdControllers.size());
- RouteSessionController controller1 = createdControllers.get(0);
- RouteSessionController controller2 = createdControllers.get(1);
+ RoutingController controller1 = createdControllers.get(0);
+ RoutingController controller2 = createdControllers.get(1);
assertNotEquals(controller1.getSessionId(), controller2.getSessionId());
assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(ROUTE_ID1));
@@ -411,12 +411,12 @@
final CountDownLatch successLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
- final List<RouteSessionController> controllers = new ArrayList<>();
+ final List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
SessionCallback sessionCallback = new SessionCallback() {
@Override
- public void onSessionCreated(RouteSessionController controller) {
+ public void onSessionCreated(RoutingController controller) {
controllers.add(controller);
successLatch.countDown();
}
@@ -451,7 +451,7 @@
// TODO: Add tests for illegal inputs if needed (e.g. selecting already selected route)
@Test
- public void testRouteSessionControllerSelectAndDeselectRoute() throws Exception {
+ public void testRoutingControllerSelectAndDeselectRoute() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
@@ -462,12 +462,12 @@
final CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
final CountDownLatch onSessionInfoChangedLatchForSelect = new CountDownLatch(1);
final CountDownLatch onSessionInfoChangedLatchForDeselect = new CountDownLatch(1);
- final List<RouteSessionController> controllers = new ArrayList<>();
+ final List<RoutingController> controllers = new ArrayList<>();
// Create session with ROUTE_ID1
SessionCallback sessionCallback = new SessionCallback() {
@Override
- public void onSessionCreated(RouteSessionController controller) {
+ public void onSessionCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
@@ -476,8 +476,8 @@
}
@Override
- public void onSessionInfoChanged(RouteSessionController controller,
- RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
+ public void onSessionInfoChanged(RoutingController controller,
+ RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) {
if (onSessionCreatedLatch.getCount() != 0
|| !TextUtils.equals(
controllers.get(0).getSessionId(), controller.getSessionId())) {
@@ -527,7 +527,7 @@
assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
- RouteSessionController controller = controllers.get(0);
+ RoutingController controller = controllers.get(0);
assertTrue(getRouteIds(controller.getSelectableRoutes())
.contains(ROUTE_ID4_TO_SELECT_AND_DESELECT));
@@ -550,7 +550,7 @@
}
@Test
- public void testRouteSessionControllerTransferToRoute() throws Exception {
+ public void testRoutingControllerTransferToRoute() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
@@ -560,12 +560,12 @@
final CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
final CountDownLatch onSessionInfoChangedLatch = new CountDownLatch(1);
- final List<RouteSessionController> controllers = new ArrayList<>();
+ final List<RoutingController> controllers = new ArrayList<>();
// Create session with ROUTE_ID1
SessionCallback sessionCallback = new SessionCallback() {
@Override
- public void onSessionCreated(RouteSessionController controller) {
+ public void onSessionCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
@@ -574,8 +574,8 @@
}
@Override
- public void onSessionInfoChanged(RouteSessionController controller,
- RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
+ public void onSessionInfoChanged(RoutingController controller,
+ RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) {
if (onSessionCreatedLatch.getCount() != 0
|| !TextUtils.equals(
controllers.get(0).getSessionId(), controller.getSessionId())) {
@@ -609,7 +609,7 @@
assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
- RouteSessionController controller = controllers.get(0);
+ RoutingController controller = controllers.get(0);
assertTrue(getRouteIds(controller.getTransferrableRoutes())
.contains(ROUTE_ID5_TO_TRANSFER_TO));
@@ -629,7 +629,7 @@
// TODO: Add tests for onSessionReleased() call.
@Test
- public void testRouteSessionControllerReleaseShouldIgnoreTransferTo() throws Exception {
+ public void testRoutingControllerReleaseShouldIgnoreTransferTo() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
@@ -639,12 +639,12 @@
final CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
final CountDownLatch onSessionInfoChangedLatch = new CountDownLatch(1);
- final List<RouteSessionController> controllers = new ArrayList<>();
+ final List<RoutingController> controllers = new ArrayList<>();
// Create session with ROUTE_ID1
SessionCallback sessionCallback = new SessionCallback() {
@Override
- public void onSessionCreated(RouteSessionController controller) {
+ public void onSessionCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
@@ -653,8 +653,8 @@
}
@Override
- public void onSessionInfoChanged(RouteSessionController controller,
- RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
+ public void onSessionInfoChanged(RoutingController controller,
+ RoutingSessionInfo oldInfo, RoutingSessionInfo newInfo) {
if (onSessionCreatedLatch.getCount() != 0
|| !TextUtils.equals(
controllers.get(0).getSessionId(), controller.getSessionId())) {
@@ -674,7 +674,7 @@
assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
- RouteSessionController controller = controllers.get(0);
+ RoutingController controller = controllers.get(0);
assertTrue(getRouteIds(controller.getTransferrableRoutes())
.contains(ROUTE_ID5_TO_TRANSFER_TO));
@@ -732,8 +732,8 @@
}
}
- static void releaseControllers(@NonNull List<RouteSessionController> controllers) {
- for (RouteSessionController controller : controllers) {
+ static void releaseControllers(@NonNull List<RoutingController> controllers) {
+ for (RoutingController controller : controllers) {
controller.release();
}
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 5dabb7d..ae15b91 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -228,7 +228,7 @@
addRouterCallback(new MediaRouter2.RouteCallback());
addSessionCallback(new SessionCallback() {
@Override
- public void onSessionCreated(MediaRouter2.RouteSessionController controller) {
+ public void onSessionCreated(MediaRouter2.RoutingController controller) {
if (createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)) {
latch.countDown();
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
similarity index 86%
rename from media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionInfoTest.java
rename to media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
index 6d21b33..3f59736 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionInfoTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
@@ -22,7 +22,7 @@
import static org.junit.Assert.assertTrue;
import static org.testng.Assert.assertThrows;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
import android.os.Bundle;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
@@ -32,11 +32,11 @@
import org.junit.runner.RunWith;
/**
- * Tests {@link RouteSessionInfo} and its {@link RouteSessionInfo.Builder builder}.
+ * Tests {@link RoutingSessionInfo} and its {@link RoutingSessionInfo.Builder builder}.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class RouteSessionInfoTest {
+public class RoutingSessionInfoTest {
public static final String TEST_ID = "test_id";
public static final String TEST_CLIENT_PACKAGE_NAME = "com.test.client.package.name";
public static final String TEST_ROUTE_FEATURE = "test_route_feature";
@@ -68,71 +68,71 @@
final String validRouteFeature = TEST_ROUTE_FEATURE;
// ID is invalid
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
nullId, validClientPackageName, validRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
emptyId, validClientPackageName, validRouteFeature));
// client package name is invalid (null)
- assertThrows(NullPointerException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
validId, nullClientPackageName, validRouteFeature));
// route feature is invalid
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
validId, validClientPackageName, nullRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
validId, validClientPackageName, emptyRouteFeature));
// Two arguments are invalid - (1) ID and clientPackageName
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
nullId, nullClientPackageName, validRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
emptyId, nullClientPackageName, validRouteFeature));
// Two arguments are invalid - (2) ID and routeFeature
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
nullId, validClientPackageName, nullRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
nullId, validClientPackageName, emptyRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
emptyId, validClientPackageName, nullRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ 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 RouteSessionInfo.Builder(
+ assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
validId, nullClientPackageName, nullRouteFeature));
- assertThrows(NullPointerException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
validId, nullClientPackageName, emptyRouteFeature));
// All arguments are invalid
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
nullId, nullClientPackageName, nullRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
nullId, nullClientPackageName, emptyRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
emptyId, nullClientPackageName, nullRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RouteSessionInfo.Builder(
+ assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
emptyId, nullClientPackageName, emptyRouteFeature));
// Null RouteInfo (1-argument constructor)
- final RouteSessionInfo nullRouteSessionInfo = null;
+ final RoutingSessionInfo nullRoutingSessionInfo = null;
assertThrows(NullPointerException.class,
- () -> new RouteSessionInfo.Builder(nullRouteSessionInfo));
+ () -> new RoutingSessionInfo.Builder(nullRoutingSessionInfo));
}
@Test
public void testBuilderConstructorWithEmptyClientPackageName() {
// An empty string for client package name is valid. (for unknown cases)
// Creating builder with it should not throw any exception.
- RouteSessionInfo.Builder builder = new RouteSessionInfo.Builder(
+ RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
TEST_ID, "" /* clientPackageName*/, TEST_ROUTE_FEATURE);
}
@Test
public void testBuilderBuildWithEmptySelectedRoutesThrowsIAE() {
- RouteSessionInfo.Builder builder = new RouteSessionInfo.Builder(
+ RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
// Note: Calling build() without adding any selected routes.
assertThrows(IllegalArgumentException.class, () -> builder.build());
@@ -140,7 +140,7 @@
@Test
public void testBuilderAddRouteMethodsWithIllegalArgumentsThrowsIAE() {
- RouteSessionInfo.Builder builder = new RouteSessionInfo.Builder(
+ RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
final String nullRouteId = null;
@@ -167,7 +167,7 @@
@Test
public void testBuilderRemoveRouteMethodsWithIllegalArgumentsThrowsIAE() {
- RouteSessionInfo.Builder builder = new RouteSessionInfo.Builder(
+ RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
final String nullRouteId = null;
@@ -193,11 +193,11 @@
}
@Test
- public void testBuilderAndGettersOfRouteSessionInfo() {
+ public void testBuilderAndGettersOfRoutingSessionInfo() {
Bundle controlHints = new Bundle();
controlHints.putString(TEST_KEY, TEST_VALUE);
- RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
@@ -238,7 +238,7 @@
@Test
public void testBuilderAddRouteMethodsWithBuilderCopyConstructor() {
- RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectableRoute(TEST_ROUTE_ID_2)
@@ -246,7 +246,7 @@
.addTransferrableRoute(TEST_ROUTE_ID_6)
.build();
- RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+ RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
.addSelectedRoute(TEST_ROUTE_ID_1)
.addSelectableRoute(TEST_ROUTE_ID_3)
.addDeselectableRoute(TEST_ROUTE_ID_5)
@@ -272,7 +272,7 @@
@Test
public void testBuilderRemoveRouteMethods() {
- RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
@@ -307,7 +307,7 @@
@Test
public void testBuilderRemoveRouteMethodsWithBuilderCopyConstructor() {
- RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
@@ -319,7 +319,7 @@
.addTransferrableRoute(TEST_ROUTE_ID_7)
.build();
- RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+ RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
.removeSelectedRoute(TEST_ROUTE_ID_1)
.removeSelectableRoute(TEST_ROUTE_ID_3)
.removeDeselectableRoute(TEST_ROUTE_ID_5)
@@ -341,7 +341,7 @@
@Test
public void testBuilderClearRouteMethods() {
- RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
@@ -373,7 +373,7 @@
@Test
public void testBuilderClearRouteMethodsWithBuilderCopyConstructor() {
- RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
@@ -385,7 +385,7 @@
.addTransferrableRoute(TEST_ROUTE_ID_7)
.build();
- RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+ RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
.clearSelectedRoutes()
.clearSelectableRoutes()
.clearDeselectableRoutes()
@@ -407,7 +407,7 @@
Bundle controlHints = new Bundle();
controlHints.putString(TEST_KEY, TEST_VALUE);
- RouteSessionInfo sessionInfo1 = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
@@ -420,7 +420,7 @@
.setControlHints(controlHints)
.build();
- RouteSessionInfo sessionInfo2 = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo2 = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
@@ -442,7 +442,7 @@
Bundle controlHints = new Bundle();
controlHints.putString(TEST_KEY, TEST_VALUE);
- RouteSessionInfo sessionInfo1 = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
@@ -455,7 +455,7 @@
.setControlHints(controlHints)
.build();
- RouteSessionInfo sessionInfo2 = new RouteSessionInfo.Builder(sessionInfo1).build();
+ RoutingSessionInfo sessionInfo2 = new RoutingSessionInfo.Builder(sessionInfo1).build();
assertEquals(sessionInfo1, sessionInfo2);
assertEquals(sessionInfo1.hashCode(), sessionInfo2.hashCode());
@@ -466,7 +466,7 @@
Bundle controlHints = new Bundle();
controlHints.putString(TEST_KEY, TEST_VALUE);
- RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
@@ -480,44 +480,44 @@
.build();
// Now, we will use copy constructor
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.addSelectedRoute("randomRoute")
.build());
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.addSelectableRoute("randomRoute")
.build());
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.addDeselectableRoute("randomRoute")
.build());
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.addTransferrableRoute("randomRoute")
.build());
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.removeSelectedRoute(TEST_ROUTE_ID_1)
.build());
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.removeSelectableRoute(TEST_ROUTE_ID_3)
.build());
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.removeDeselectableRoute(TEST_ROUTE_ID_5)
.build());
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.removeTransferrableRoute(TEST_ROUTE_ID_7)
.build());
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.clearSelectedRoutes()
// Note: Calling build() with empty selected routes will throw IAE.
.addSelectedRoute(TEST_ROUTE_ID_0)
.build());
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.clearSelectableRoutes()
.build());
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.clearDeselectableRoutes()
.build());
- assertNotEquals(sessionInfo, new RouteSessionInfo.Builder(sessionInfo)
+ assertNotEquals(sessionInfo, new RoutingSessionInfo.Builder(sessionInfo)
.clearTransferrableRoutes()
.build());
@@ -529,7 +529,7 @@
Bundle controlHints = new Bundle();
controlHints.putString(TEST_KEY, TEST_VALUE);
- RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
+ RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
@@ -546,7 +546,8 @@
sessionInfo.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
- RouteSessionInfo sessionInfoFromParcel = RouteSessionInfo.CREATOR.createFromParcel(parcel);
+ RoutingSessionInfo sessionInfoFromParcel =
+ RoutingSessionInfo.CREATOR.createFromParcel(parcel);
assertEquals(sessionInfo, sessionInfoFromParcel);
assertEquals(sessionInfo.hashCode(), sessionInfoFromParcel.hashCode());
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index 6554267..cf55eba 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -16,8 +16,12 @@
package android.telephony;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
import android.app.ActivityThread;
import android.app.PendingIntent;
+import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
@@ -27,22 +31,18 @@
/**
* Manages MMS operations such as sending multimedia messages.
- * Get this object by calling the static method {@link #getInstance()}.
- * @hide
+ * Get this object by calling Context#getSystemService(Context#MMS_SERVICE).
*/
+@SystemService(Context.MMS_SERVICE)
public class MmsManager {
private static final String TAG = "MmsManager";
-
- /** Singleton object constructed during class initialization. */
- private static final MmsManager sInstance = new MmsManager();
+ private final Context mContext;
/**
- * Get the MmsManager singleton instance.
- *
- * @return the {@link MmsManager} singleton instance.
+ * @hide
*/
- public static MmsManager getInstance() {
- return sInstance;
+ public MmsManager(@NonNull Context context) {
+ mContext = context;
}
/**
@@ -56,8 +56,9 @@
* @param sentIntent if not NULL this <code>PendingIntent</code> is broadcast when the message
* is successfully sent, or failed
*/
- public void sendMultimediaMessage(int subId, Uri contentUri, String locationUrl,
- Bundle configOverrides, PendingIntent sentIntent) {
+ public void sendMultimediaMessage(int subId, @NonNull Uri contentUri,
+ @Nullable String locationUrl, @Nullable Bundle configOverrides,
+ @Nullable PendingIntent sentIntent) {
try {
final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
if (iMms == null) {
@@ -84,8 +85,9 @@
* broadcast when the message is downloaded, or the download is failed
* @throws IllegalArgumentException if locationUrl or contentUri is empty
*/
- public void downloadMultimediaMessage(int subId, String locationUrl, Uri contentUri,
- Bundle configOverrides, PendingIntent downloadedIntent) {
+ public void downloadMultimediaMessage(int subId, @NonNull String locationUrl,
+ @NonNull Uri contentUri, @Nullable Bundle configOverrides,
+ @Nullable PendingIntent downloadedIntent) {
try {
final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
if (iMms == null) {
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index 1aebeaf..26c7f8d 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -16,6 +16,7 @@
#include <android/bitmap.h>
#include <android/graphics/bitmap.h>
+#include <android/data_space.h>
int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
AndroidBitmapInfo* info) {
@@ -29,6 +30,15 @@
return ANDROID_BITMAP_RESULT_SUCCESS;
}
+int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) {
+ if (NULL == env || NULL == jbitmap) {
+ return ADATASPACE_UNKNOWN; // Or return a real error?
+ }
+
+ android::graphics::Bitmap bitmap(env, jbitmap);
+ return bitmap.getDataSpace();
+}
+
int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr) {
if (NULL == env || NULL == jbitmap) {
return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index bdd7f63..832770f 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -18,6 +18,7 @@
AImageDecoderHeaderInfo_isAnimated;
AImageDecoderHeaderInfo_getAndroidBitmapFormat;
AndroidBitmap_getInfo;
+ AndroidBitmap_getDataSpace;
AndroidBitmap_lockPixels;
AndroidBitmap_unlockPixels;
local:
diff --git a/packages/CarSystemUI/res/layout/sysui_primary_window.xml b/packages/CarSystemUI/res/layout/sysui_primary_window.xml
new file mode 100644
index 0000000..cc36e87
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/sysui_primary_window.xml
@@ -0,0 +1,30 @@
+<?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.
+ -->
+
+<!-- Fullscreen views in sysui should be listed here in increasing Z order. -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="@android:color/transparent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout="@layout/car_fullscreen_user_switcher"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 6ec3854..cfe1c70 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -22,7 +22,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.util.leak.LeakDetector;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java
new file mode 100644
index 0000000..c7e14d6
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java
@@ -0,0 +1,153 @@
+/*
+ * 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.car;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.os.Binder;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Controls the expansion state of the primary window which will contain all of the fullscreen sysui
+ * behavior. This window still has a collapsed state in order to watch for swipe events to expand
+ * this window for the notification panel.
+ */
+@Singleton
+public class SystemUIPrimaryWindowController implements
+ ConfigurationController.ConfigurationListener {
+
+ private final Context mContext;
+ private final Resources mResources;
+ private final WindowManager mWindowManager;
+
+ private final int mStatusBarHeight;
+ private final int mNavBarHeight;
+ private final int mDisplayHeight;
+ private ViewGroup mBaseLayout;
+ private WindowManager.LayoutParams mLp;
+ private WindowManager.LayoutParams mLpChanged;
+ private boolean mIsAttached = false;
+
+ @Inject
+ public SystemUIPrimaryWindowController(
+ Context context,
+ @Main Resources resources,
+ WindowManager windowManager,
+ ConfigurationController configurationController
+ ) {
+ mContext = context;
+ mResources = resources;
+ mWindowManager = windowManager;
+
+ Point display = new Point();
+ mWindowManager.getDefaultDisplay().getSize(display);
+ mDisplayHeight = display.y;
+
+ mStatusBarHeight = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+ mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.navigation_bar_height);
+
+ mLpChanged = new WindowManager.LayoutParams();
+ mBaseLayout = (ViewGroup) LayoutInflater.from(context)
+ .inflate(R.layout.sysui_primary_window, /* root= */ null, false);
+
+ configurationController.addCallback(this);
+ }
+
+ /** Returns the base view of the primary window. */
+ public ViewGroup getBaseLayout() {
+ return mBaseLayout;
+ }
+
+ /** Returns {@code true} if the window is already attached. */
+ public boolean isAttached() {
+ return mIsAttached;
+ }
+
+ /** Attaches the window to the window manager. */
+ public void attach() {
+ if (mIsAttached) {
+ return;
+ }
+ mIsAttached = true;
+ // Now that the status bar window encompasses the sliding panel and its
+ // translucent backdrop, the entire thing is made TRANSLUCENT and is
+ // hardware-accelerated.
+ mLp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ mStatusBarHeight,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
+ PixelFormat.TRANSLUCENT);
+ mLp.token = new Binder();
+ mLp.gravity = Gravity.TOP;
+ mLp.setFitWindowInsetsTypes(/* types= */ 0);
+ mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+ mLp.setTitle("SystemUIPrimaryWindow");
+ mLp.packageName = mContext.getPackageName();
+ mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+ mWindowManager.addView(mBaseLayout, mLp);
+ mLpChanged.copyFrom(mLp);
+ }
+
+ /** Sets the window to the expanded state. */
+ public void setWindowExpanded(boolean expanded) {
+ if (expanded) {
+ // TODO: Update this so that the windowing type gets the full height of the display
+ // when we use MATCH_PARENT.
+ mLpChanged.height = mDisplayHeight + mNavBarHeight;
+ mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ } else {
+ mLpChanged.height = mStatusBarHeight;
+ // TODO: Allow touches to go through to the status bar to handle notification panel.
+ mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ }
+ updateWindow();
+ }
+
+ /** Returns {@code true} if the window is expanded */
+ public boolean isWindowExpanded() {
+ return mLp.height != mStatusBarHeight;
+ }
+
+ private void updateWindow() {
+ if (mLp != null && mLp.copyFrom(mLpChanged) != 0) {
+ if (isAttached()) {
+ mWindowManager.updateViewLayout(mBaseLayout, mLp);
+ }
+ }
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 4521f5f..18485dc 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -67,6 +67,7 @@
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarDeviceProvisionedListener;
import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.SystemUIPrimaryWindowController;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -106,8 +107,8 @@
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -168,6 +169,7 @@
// acceleration rate for the fling animation
private static final float FLING_SPEED_UP_FACTOR = 0.6f;
+ private final UserSwitcherController mUserSwitcherController;
private final ScrimController mScrimController;
private final LockscreenLockIconController mLockscreenLockIconController;
@@ -177,17 +179,16 @@
private float mBackgroundAlphaDiff;
private float mInitialBackgroundAlpha;
- private final Lazy<FullscreenUserSwitcher> mFullscreenUserSwitcherLazy;
- private FullscreenUserSwitcher mFullscreenUserSwitcher;
-
private CarBatteryController mCarBatteryController;
private BatteryMeterView mBatteryMeterView;
private Drawable mNotificationPanelBackground;
private final Object mQueueLock = new Object();
+ private final SystemUIPrimaryWindowController mSystemUIPrimaryWindowController;
private final CarNavigationBarController mCarNavigationBarController;
private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
private final Lazy<PowerManagerHelper> mPowerManagerHelperLazy;
+ private final FullscreenUserSwitcher mFullscreenUserSwitcher;
private final ShadeController mShadeController;
private final CarServiceProvider mCarServiceProvider;
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
@@ -268,7 +269,7 @@
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- Lazy<NewNotifPipeline> newNotifPipeline,
+ Lazy<NotifPipelineInitializer> notifPipelineInitializer,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
@@ -338,7 +339,8 @@
/* Car Settings injected components. */
CarServiceProvider carServiceProvider,
Lazy<PowerManagerHelper> powerManagerHelperLazy,
- Lazy<FullscreenUserSwitcher> fullscreenUserSwitcherLazy,
+ FullscreenUserSwitcher fullscreenUserSwitcher,
+ SystemUIPrimaryWindowController systemUIPrimaryWindowController,
CarNavigationBarController carNavigationBarController,
FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
super(
@@ -355,7 +357,7 @@
headsUpManagerPhone,
dynamicPrivacyController,
bypassHeadsUpNotifier,
- newNotifPipeline,
+ notifPipelineInitializer,
falsingManager,
broadcastDispatcher,
remoteInputQuickSettingsDisabler,
@@ -422,6 +424,7 @@
userInfoControllerImpl,
notificationRowBinder,
dismissCallbackRegistry);
+ mUserSwitcherController = userSwitcherController;
mScrimController = scrimController;
mLockscreenLockIconController = lockscreenLockIconController;
mCarDeviceProvisionedController =
@@ -429,7 +432,8 @@
mShadeController = shadeController;
mCarServiceProvider = carServiceProvider;
mPowerManagerHelperLazy = powerManagerHelperLazy;
- mFullscreenUserSwitcherLazy = fullscreenUserSwitcherLazy;
+ mFullscreenUserSwitcher = fullscreenUserSwitcher;
+ mSystemUIPrimaryWindowController = systemUIPrimaryWindowController;
mCarNavigationBarController = carNavigationBarController;
mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
}
@@ -444,6 +448,13 @@
mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
mScreenLifecycle.addObserver(mScreenObserver);
+ // TODO: Remove the setup of user switcher from Car Status Bar.
+ mSystemUIPrimaryWindowController.attach();
+ mFullscreenUserSwitcher.setStatusBar(this);
+ mFullscreenUserSwitcher.setContainer(
+ mSystemUIPrimaryWindowController.getBaseLayout().findViewById(
+ R.id.fullscreen_user_switcher_stub));
+
// Notification bar related setup.
mInitialBackgroundAlpha = (float) mContext.getResources().getInteger(
R.integer.config_initialNotificationBackgroundAlpha) / 100;
@@ -510,16 +521,6 @@
});
}
- /**
- * Allows for showing or hiding just the navigation bars. This is indented to be used when
- * the full screen user selector is shown.
- */
- void setNavBarVisibility(@View.Visibility int visibility) {
- mCarNavigationBarController.setBottomWindowVisibility(visibility);
- mCarNavigationBarController.setLeftWindowVisibility(visibility);
- mCarNavigationBarController.setRightWindowVisibility(visibility);
- }
-
@Override
public boolean hideKeyguard() {
boolean result = super.hideKeyguard();
@@ -924,9 +925,6 @@
+ " scroll " + mStackScroller.getScrollX()
+ "," + mStackScroller.getScrollY());
}
-
- pw.print(" mFullscreenUserSwitcher=");
- pw.println(mFullscreenUserSwitcher);
pw.print(" mCarBatteryController=");
pw.println(mCarBatteryController);
pw.print(" mBatteryMeterView=");
@@ -972,14 +970,7 @@
@Override
protected void createUserSwitcher() {
- UserSwitcherController userSwitcherController =
- Dependency.get(UserSwitcherController.class);
- if (userSwitcherController.useFullscreenUserSwitcher()) {
- mFullscreenUserSwitcher = mFullscreenUserSwitcherLazy.get();
- mFullscreenUserSwitcher.setStatusBar(this);
- mFullscreenUserSwitcher.setContainer(
- mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub));
- } else {
+ if (!mUserSwitcherController.useFullscreenUserSwitcher()) {
super.createUserSwitcher();
}
}
@@ -996,25 +987,12 @@
super.onStateChanged(newState);
if (newState != StatusBarState.FULLSCREEN_USER_SWITCHER) {
- hideUserSwitcher();
+ mFullscreenUserSwitcher.hide();
} else {
dismissKeyguardWhenUserSwitcherNotDisplayed();
}
}
- /** Makes the full screen user switcher visible, if applicable. */
- public void showUserSwitcher() {
- if (mFullscreenUserSwitcher != null && mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
- mFullscreenUserSwitcher.show(); // Makes the switcher visible.
- }
- }
-
- private void hideUserSwitcher() {
- if (mFullscreenUserSwitcher != null) {
- mFullscreenUserSwitcher.hide();
- }
- }
-
final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
public void onScreenTurnedOn() {
@@ -1024,7 +1002,7 @@
// We automatically dismiss keyguard unless user switcher is being shown on the keyguard.
private void dismissKeyguardWhenUserSwitcherNotDisplayed() {
- if (mFullscreenUserSwitcher == null) {
+ if (!mUserSwitcherController.useFullscreenUserSwitcher()) {
return; // Not using the full screen user switcher.
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
index 0ad0992..2a2eb69 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
@@ -24,6 +24,7 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.R;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.navigationbar.car.CarNavigationBarController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.NavigationModeController;
@@ -40,6 +41,8 @@
public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
protected boolean mShouldHideNavBar;
+ private final CarNavigationBarController mCarNavigationBarController;
+ private final FullscreenUserSwitcher mFullscreenUserSwitcher;
@Inject
public CarStatusBarKeyguardViewManager(Context context,
@@ -52,13 +55,17 @@
DockManager dockManager,
StatusBarWindowController statusBarWindowController,
KeyguardStateController keyguardStateController,
- NotificationMediaManager notificationMediaManager) {
+ NotificationMediaManager notificationMediaManager,
+ CarNavigationBarController carNavigationBarController,
+ FullscreenUserSwitcher fullscreenUserSwitcher) {
super(context, callback, lockPatternUtils, sysuiStatusBarStateController,
configurationController, keyguardUpdateMonitor, navigationModeController,
dockManager, statusBarWindowController, keyguardStateController,
notificationMediaManager);
mShouldHideNavBar = context.getResources()
.getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
+ mCarNavigationBarController = carNavigationBarController;
+ mFullscreenUserSwitcher = fullscreenUserSwitcher;
}
@Override
@@ -66,8 +73,10 @@
if (!mShouldHideNavBar) {
return;
}
- CarStatusBar statusBar = (CarStatusBar) mStatusBar;
- statusBar.setNavBarVisibility(navBarVisible ? View.VISIBLE : View.GONE);
+ int visibility = navBarVisible ? View.VISIBLE : View.GONE;
+ mCarNavigationBarController.setBottomWindowVisibility(visibility);
+ mCarNavigationBarController.setLeftWindowVisibility(visibility);
+ mCarNavigationBarController.setRightWindowVisibility(visibility);
}
/**
@@ -86,8 +95,7 @@
*/
@Override
public void onCancelClicked() {
- CarStatusBar statusBar = (CarStatusBar) mStatusBar;
- statusBar.showUserSwitcher();
+ mFullscreenUserSwitcher.show();
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index e5a091f..3abbe32 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -31,6 +31,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.SystemUIPrimaryWindowController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -66,8 +67,8 @@
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -138,7 +139,7 @@
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- Lazy<NewNotifPipeline> newNotifPipeline,
+ Lazy<NotifPipelineInitializer> notifPipelineInitializer,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
@@ -207,7 +208,8 @@
DismissCallbackRegistry dismissCallbackRegistry,
CarServiceProvider carServiceProvider,
Lazy<PowerManagerHelper> powerManagerHelperLazy,
- Lazy<FullscreenUserSwitcher> fullscreenUserSwitcherLazy,
+ FullscreenUserSwitcher fullscreenUserSwitcher,
+ SystemUIPrimaryWindowController systemUIPrimaryWindowController,
CarNavigationBarController carNavigationBarController,
FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
return new CarStatusBar(
@@ -224,7 +226,7 @@
headsUpManagerPhone,
dynamicPrivacyController,
bypassHeadsUpNotifier,
- newNotifPipeline,
+ notifPipelineInitializer,
falsingManager,
broadcastDispatcher,
remoteInputQuickSettingsDisabler,
@@ -292,7 +294,8 @@
dismissCallbackRegistry,
carServiceProvider,
powerManagerHelperLazy,
- fullscreenUserSwitcherLazy,
+ fullscreenUserSwitcher,
+ systemUIPrimaryWindowController,
carNavigationBarController,
flingAnimationUtilsBuilder);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index f8fc3bb..3cd66c2 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -38,6 +38,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.SystemUIPrimaryWindowController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.car.CarTrustAgentUnlockDialogHelper.OnHideListener;
import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord;
@@ -56,9 +57,10 @@
private final UserManager mUserManager;
private final CarServiceProvider mCarServiceProvider;
private final CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
+ private final SystemUIPrimaryWindowController mSystemUIPrimaryWindowController;
+ private CarStatusBar mCarStatusBar;
private final int mShortAnimDuration;
- private CarStatusBar mStatusBar;
private View mParent;
private UserGridRecyclerView mUserGridView;
private CarTrustAgentEnrollmentManager mEnrollmentManager;
@@ -81,23 +83,35 @@
@Main Resources resources,
UserManager userManager,
CarServiceProvider carServiceProvider,
- CarTrustAgentUnlockDialogHelper carTrustAgentUnlockDialogHelper) {
+ CarTrustAgentUnlockDialogHelper carTrustAgentUnlockDialogHelper,
+ SystemUIPrimaryWindowController systemUIPrimaryWindowController) {
mContext = context;
mResources = resources;
mUserManager = userManager;
mCarServiceProvider = carServiceProvider;
mUnlockDialogHelper = carTrustAgentUnlockDialogHelper;
+ mSystemUIPrimaryWindowController = systemUIPrimaryWindowController;
mShortAnimDuration = mResources.getInteger(android.R.integer.config_shortAnimTime);
}
- /** Sets the status bar which controls the keyguard. */
+ /** Sets the status bar which gives an entry point to dismiss the keyguard. */
+ // TODO: Remove this in favor of a keyguard controller.
public void setStatusBar(CarStatusBar statusBar) {
- mStatusBar = statusBar;
+ mCarStatusBar = statusBar;
+ }
+
+ /** Returns {@code true} if the user switcher already has a parent view. */
+ public boolean isAttached() {
+ return mParent != null;
}
/** Sets the {@link ViewStub} to show the user switcher. */
public void setContainer(ViewStub containerStub) {
+ if (isAttached()) {
+ return;
+ }
+
mParent = containerStub.inflate();
View container = mParent.findViewById(R.id.container);
@@ -148,20 +162,31 @@
* Makes user grid visible.
*/
public void show() {
+ if (!isAttached()) {
+ return;
+ }
mParent.setVisibility(View.VISIBLE);
+ mSystemUIPrimaryWindowController.setWindowExpanded(true);
}
/**
* Hides the user grid.
*/
public void hide() {
+ if (!isAttached()) {
+ return;
+ }
mParent.setVisibility(View.INVISIBLE);
+ mSystemUIPrimaryWindowController.setWindowExpanded(false);
}
/**
* @return {@code true} if user grid is visible, {@code false} otherwise.
*/
public boolean isVisible() {
+ if (!isAttached()) {
+ return false;
+ }
return mParent.getVisibility() == View.VISIBLE;
}
@@ -196,7 +221,7 @@
}
if (mSelectedUser.mType == UserRecord.FOREGROUND_USER) {
hide();
- mStatusBar.dismissKeyguard();
+ mCarStatusBar.dismissKeyguard();
return;
}
// Switching is about to happen, since it takes time, fade out the switcher gradually.
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index cdabeeb..8c756ec 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -43,6 +43,9 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
@@ -53,7 +56,6 @@
import com.android.internal.util.UserIcons;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -337,7 +339,7 @@
.setPositiveButton(android.R.string.ok, null)
.create();
// Sets window flags for the SysUI dialog
- SystemUIDialog.applyFlags(maxUsersDialog);
+ applyCarSysUIDialogFlags(maxUsersDialog);
maxUsersDialog.show();
}
@@ -356,10 +358,19 @@
.setOnCancelListener(this)
.create();
// Sets window flags for the SysUI dialog
- SystemUIDialog.applyFlags(addUserDialog);
+ applyCarSysUIDialogFlags(addUserDialog);
addUserDialog.show();
}
+ private void applyCarSysUIDialogFlags(AlertDialog dialog) {
+ final Window window = dialog.getWindow();
+ window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ window.setFitWindowInsetsTypes(
+ window.getFitWindowInsetsTypes() & ~WindowInsets.Type.statusBars());
+ }
+
private void notifyUserSelected(UserRecord userRecord) {
// Notify the listener which user was selected
if (mUserSelectionListener != null) {
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 642dc82..41a9b24 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -36,6 +36,7 @@
import android.os.Bundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -49,7 +50,6 @@
import android.widget.TextView;
import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.TrafficStatsConstants;
@@ -203,7 +203,7 @@
}
private URL getUrlForCaptivePortal() {
- String url = getIntent().getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY);
+ String url = getIntent().getStringExtra(TelephonyManager.EXTRA_REDIRECTION_URL);
if (TextUtils.isEmpty(url)) url = mCm.getCaptivePortalServerUrl();
final CarrierConfigManager configManager = getApplicationContext()
.getSystemService(CarrierConfigManager.class);
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index 2697a10..cb062a6 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -30,8 +30,6 @@
import android.text.TextUtils;
import android.util.Log;
-import com.android.internal.telephony.PhoneConstants;
-
/**
* This util class provides common logic for carrier actions
*/
@@ -103,7 +101,7 @@
}
private static void onDisableAllMeteredApns(Intent intent, Context context) {
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.getDefaultVoiceSubscriptionId());
logd("onDisableAllMeteredApns subId: " + subId);
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
@@ -111,7 +109,7 @@
}
private static void onEnableAllMeteredApns(Intent intent, Context context) {
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.getDefaultVoiceSubscriptionId());
logd("onEnableAllMeteredApns subId: " + subId);
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
@@ -135,7 +133,7 @@
}
private static void onRegisterDefaultNetworkAvail(Intent intent, Context context) {
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.getDefaultVoiceSubscriptionId());
logd("onRegisterDefaultNetworkAvail subId: " + subId);
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
@@ -143,7 +141,7 @@
}
private static void onDeregisterDefaultNetworkAvail(Intent intent, Context context) {
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.getDefaultVoiceSubscriptionId());
logd("onDeregisterDefaultNetworkAvail subId: " + subId);
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
@@ -151,7 +149,7 @@
}
private static void onDisableRadio(Intent intent, Context context) {
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.getDefaultVoiceSubscriptionId());
logd("onDisableRadio subId: " + subId);
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
@@ -159,7 +157,7 @@
}
private static void onEnableRadio(Intent intent, Context context) {
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.getDefaultVoiceSubscriptionId());
logd("onEnableRadio subId: " + subId);
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
@@ -202,7 +200,7 @@
}
private static void onResetAllCarrierActions(Intent intent, Context context) {
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.getDefaultVoiceSubscriptionId());
logd("onResetAllCarrierActions subId: " + subId);
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
index 46b1d5f..c7f5e9a 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
@@ -19,10 +19,10 @@
import android.content.Intent;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
@@ -50,7 +50,7 @@
* @param intent passing signal for config match
* @return a list of carrier action for the given signal based on the carrier config.
*
- * Example: input intent TelephonyIntent.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+ * Example: input intent TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
* This intent allows fined-grained matching based on both intent type & extra values:
* apnType and errorCode.
* apnType read from passing intent is "default" and errorCode is 0x26 for example and
@@ -78,25 +78,25 @@
String arg1 = null;
String arg2 = null;
switch (intent.getAction()) {
- case TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED:
+ case TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED:
configs = b.getStringArray(CarrierConfigManager
.KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY);
break;
- case TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED:
+ case TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED:
configs = b.getStringArray(CarrierConfigManager
.KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY);
- arg1 = intent.getStringExtra(TelephonyIntents.EXTRA_APN_TYPE_KEY);
- arg2 = intent.getStringExtra(TelephonyIntents.EXTRA_ERROR_CODE_KEY);
+ arg1 = intent.getStringExtra(TelephonyManager.EXTRA_APN_TYPE);
+ arg2 = intent.getStringExtra(TelephonyManager.EXTRA_ERROR_CODE);
break;
- case TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET:
+ case TelephonyManager.ACTION_CARRIER_SIGNAL_RESET:
configs = b.getStringArray(CarrierConfigManager
.KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET);
break;
- case TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE:
+ case TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE:
configs = b.getStringArray(CarrierConfigManager
.KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE);
- arg1 = String.valueOf(intent.getBooleanExtra(TelephonyIntents
- .EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY, false));
+ arg1 = String.valueOf(intent.getBooleanExtra(TelephonyManager
+ .EXTRA_DEFAULT_NETWORK_AVAILABLE, false));
break;
default:
Log.e(TAG, "load carrier config failure with un-configured key: "
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
index 3e34f0a..78a02d7 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
@@ -27,10 +27,9 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.provider.Settings;
+import android.telephony.TelephonyManager;
import android.util.Log;
-import com.android.internal.telephony.TelephonyIntents;
-
/**
* Service to run {@link android.app.job.JobScheduler} job.
* Service to monitor when there is a change to conent URI
@@ -93,7 +92,7 @@
}
int jobId;
switch(intent.getAction()) {
- case TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED:
+ case TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED:
jobId = PROVISION_OBSERVER_REEVALUATION_JOB_ID;
break;
default:
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
index 1928ad9..6229434 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
@@ -15,18 +15,22 @@
*/
package com.android.carrierdefaultapp;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.test.InstrumentationTestCase;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -34,10 +38,6 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
public class CarrierDefaultReceiverTest extends InstrumentationTestCase {
@Mock
@@ -69,6 +69,7 @@
mContext.injectSystemService(NotificationManager.class, mNotificationMgr);
mContext.injectSystemService(TelephonyManager.class, mTelephonyMgr);
mContext.injectSystemService(CarrierConfigManager.class, mCarrierConfigMgr);
+ doReturn(mTelephonyMgr).when(mTelephonyMgr).createForSubscriptionId(anyInt());
mReceiver = new CarrierDefaultBroadcastReceiver();
}
@@ -87,8 +88,8 @@
.KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY, new String[]{"4,1"});
doReturn(b).when(mCarrierConfigMgr).getConfig();
- Intent intent = new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED);
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ Intent intent = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED);
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
mReceiver.onReceive(mContext, intent);
mContext.waitForMs(100);
diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
index 69bd0ed..ff00fb3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
@@ -16,8 +16,6 @@
package com.android.settingslib;
-import static android.content.Context.TELEPHONY_SERVICE;
-
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -172,36 +170,38 @@
}
}
- public static String getFormattedPhoneNumber(Context context, SubscriptionInfo subscriptionInfo) {
+ /**
+ * Format a phone number.
+ * @param subscriptionInfo {@link SubscriptionInfo} subscription information.
+ * @return Returns formatted phone number.
+ */
+ public static String getFormattedPhoneNumber(Context context,
+ SubscriptionInfo subscriptionInfo) {
String formattedNumber = null;
if (subscriptionInfo != null) {
- final TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
- final String rawNumber =
- telephonyManager.getLine1Number(subscriptionInfo.getSubscriptionId());
+ final TelephonyManager telephonyManager = context.getSystemService(
+ TelephonyManager.class);
+ final String rawNumber = telephonyManager.createForSubscriptionId(
+ subscriptionInfo.getSubscriptionId()).getLine1Number();
if (!TextUtils.isEmpty(rawNumber)) {
formattedNumber = PhoneNumberUtils.formatNumber(rawNumber);
}
-
}
return formattedNumber;
}
public static String getFormattedPhoneNumbers(Context context,
- List<SubscriptionInfo> subscriptionInfo) {
+ List<SubscriptionInfo> subscriptionInfoList) {
StringBuilder sb = new StringBuilder();
- if (subscriptionInfo != null) {
- final TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
- final int count = subscriptionInfo.size();
- for (int i = 0; i < count; i++) {
- final String rawNumber = telephonyManager.getLine1Number(
- subscriptionInfo.get(i).getSubscriptionId());
+ if (subscriptionInfoList != null) {
+ final TelephonyManager telephonyManager = context.getSystemService(
+ TelephonyManager.class);
+ final int count = subscriptionInfoList.size();
+ for (SubscriptionInfo subscriptionInfo : subscriptionInfoList) {
+ final String rawNumber = telephonyManager.createForSubscriptionId(
+ subscriptionInfo.getSubscriptionId()).getLine1Number();
if (!TextUtils.isEmpty(rawNumber)) {
- sb.append(PhoneNumberUtils.formatNumber(rawNumber));
- if (i < count - 1) {
- sb.append("\n");
- }
+ sb.append(PhoneNumberUtils.formatNumber(rawNumber)).append("\n");
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 81739e0..9d4c24e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -211,13 +211,6 @@
private static final int EAP_WPA = 1; // WPA-EAP
private static final int EAP_WPA2_WPA3 = 2; // RSN-EAP
- /**
- * The number of distinct wifi levels.
- *
- * <p>Must keep in sync with {@link R.array.wifi_signal} and {@link WifiManager#RSSI_LEVELS}.
- */
- public static final int SIGNAL_LEVELS = 5;
-
public static final int UNREACHABLE_RSSI = Integer.MIN_VALUE;
public static final String KEY_PREFIX_AP = "AP:";
@@ -453,9 +446,10 @@
return other.getSpeed() - getSpeed();
}
+ WifiManager wifiManager = getWifiManager();
// Sort by signal strength, bucketed by level
- int difference = WifiManager.calculateSignalLevel(other.mRssi, SIGNAL_LEVELS)
- - WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS);
+ int difference = wifiManager.calculateSignalLevel(other.mRssi)
+ - wifiManager.calculateSignalLevel(mRssi);
if (difference != 0) {
return difference;
}
@@ -869,13 +863,14 @@
}
/**
- * Returns the number of levels to show for a Wifi icon, from 0 to {@link #SIGNAL_LEVELS}-1.
+ * Returns the number of levels to show for a Wifi icon, from 0 to
+ * {@link WifiManager#getMaxSignalLevel()}.
*
- * <p>Use {@#isReachable()} to determine if an AccessPoint is in range, as this method will
+ * <p>Use {@link #isReachable()} to determine if an AccessPoint is in range, as this method will
* always return at least 0.
*/
public int getLevel() {
- return WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS);
+ return getWifiManager().calculateSignalLevel(mRssi);
}
public int getRssi() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
index 4a4f0e6..f21e466 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
@@ -22,6 +22,7 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Parcelable;
@@ -126,13 +127,15 @@
@Keep
public TestAccessPointBuilder setLevel(int level) {
// Reversal of WifiManager.calculateSignalLevels
+ WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
+ int maxSignalLevel = wifiManager.getMaxSignalLevel();
if (level == 0) {
mRssi = MIN_RSSI;
- } else if (level >= AccessPoint.SIGNAL_LEVELS) {
+ } else if (level > maxSignalLevel) {
mRssi = MAX_RSSI;
} else {
float inputRange = MAX_RSSI - MIN_RSSI;
- float outputRange = AccessPoint.SIGNAL_LEVELS - 1;
+ float outputRange = maxSignalLevel;
mRssi = (int) (level * inputRange / outputRange + MIN_RSSI);
}
return this;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 8591116..3f55cea 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -89,7 +89,7 @@
public void setListening(boolean listening) {
if (listening) {
mNetworkScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
- mWifiNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK);
+ mWifiNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK);
mWifiNetworkScoreCache.registerListener(mCacheListener);
mConnectivityManager.registerNetworkCallback(
mNetworkRequest, mNetworkCallback, mHandler);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index ba6a8ea..ed4ff08 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -361,7 +361,7 @@
mNetworkScoreManager.registerNetworkScoreCache(
NetworkKey.TYPE_WIFI,
mScoreCache,
- NetworkScoreManager.CACHE_FILTER_SCAN_RESULTS);
+ NetworkScoreManager.SCORE_FILTER_SCAN_RESULTS);
}
private void requestScoresForNetworkKeys(Collection<NetworkKey> keys) {
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index 4600793..2ccff1e 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -14,7 +14,10 @@
android_test {
name: "SettingsLibTests",
- defaults: ["SettingsLibDefaults"],
+ defaults: [
+ "SettingsLibDefaults",
+ "framework-wifi-test-defaults"
+ ],
certificate: "platform",
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 61cbbd3..03201ae 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -83,7 +83,6 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AccessPointTest {
-
private static final String TEST_SSID = "\"test_ssid\"";
private static final String ROAMING_SSID = "\"roaming_ssid\"";
private static final String OSU_FRIENDLY_NAME = "osu_friendly_name";
@@ -98,6 +97,7 @@
20 * DateUtils.MINUTE_IN_MILLIS;
private Context mContext;
+ private int mMaxSignalLevel;
private WifiInfo mWifiInfo;
@Mock private Context mMockContext;
@Mock private WifiManager mMockWifiManager;
@@ -128,6 +128,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getTargetContext();
+ mMaxSignalLevel = mContext.getSystemService(WifiManager.class).getMaxSignalLevel();
mWifiInfo = new WifiInfo();
mWifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(TEST_SSID));
mWifiInfo.setBSSID(TEST_BSSID);
@@ -180,7 +181,7 @@
@Test
public void testCompareTo_GivesHighLevelBeforeLowLevel() {
- final int highLevel = AccessPoint.SIGNAL_LEVELS - 1;
+ final int highLevel = mMaxSignalLevel;
final int lowLevel = 1;
assertThat(highLevel).isGreaterThan(lowLevel);
@@ -226,7 +227,7 @@
.setReachable(true).build();
AccessPoint saved = new TestAccessPointBuilder(mContext).setSaved(true).build();
AccessPoint highLevelAndReachable = new TestAccessPointBuilder(mContext)
- .setLevel(AccessPoint.SIGNAL_LEVELS - 1).build();
+ .setLevel(mMaxSignalLevel).build();
AccessPoint firstName = new TestAccessPointBuilder(mContext).setSsid("a").build();
AccessPoint lastname = new TestAccessPointBuilder(mContext).setSsid("z").build();
@@ -520,6 +521,8 @@
when(packageManager.getApplicationInfoAsUser(eq(appPackageName), anyInt(), anyInt()))
.thenReturn(applicationInfo);
when(applicationInfo.loadLabel(packageManager)).thenReturn(appLabel);
+ when(context.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+ when(mMockWifiManager.calculateSignalLevel(rssi)).thenReturn(4);
NetworkInfo networkInfo =
new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
@@ -636,14 +639,14 @@
public void testBuilder_setLevel() {
AccessPoint testAp;
- for (int i = 0; i < AccessPoint.SIGNAL_LEVELS; i++) {
+ for (int i = 0; i <= mMaxSignalLevel; i++) {
testAp = new TestAccessPointBuilder(mContext).setLevel(i).build();
assertThat(testAp.getLevel()).isEqualTo(i);
}
// numbers larger than the max level should be set to max
- testAp = new TestAccessPointBuilder(mContext).setLevel(AccessPoint.SIGNAL_LEVELS).build();
- assertThat(testAp.getLevel()).isEqualTo(AccessPoint.SIGNAL_LEVELS - 1);
+ testAp = new TestAccessPointBuilder(mContext).setLevel(mMaxSignalLevel + 1).build();
+ assertThat(testAp.getLevel()).isEqualTo(mMaxSignalLevel);
// numbers less than 0 should give level 0
testAp = new TestAccessPointBuilder(mContext).setLevel(-100).build();
@@ -653,7 +656,7 @@
@Test
public void testBuilder_settingReachableAfterLevelDoesNotAffectLevel() {
int level = 1;
- assertThat(level).isLessThan(AccessPoint.SIGNAL_LEVELS - 1);
+ assertThat(level).isLessThan(mMaxSignalLevel);
AccessPoint testAp =
new TestAccessPointBuilder(mContext).setLevel(level).setReachable(true).build();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
index d98f50b..7be9fee 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
@@ -31,6 +31,7 @@
import android.telephony.TelephonyManager;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -75,6 +76,7 @@
}
@Test
+ @Ignore
public void getMobileTemplate_infoNull_returnMobileAll() {
when(mSubscriptionManager.isActiveSubId(SUB_ID)).thenReturn(false);
@@ -84,6 +86,7 @@
}
@Test
+ @Ignore
public void getMobileTemplate_groupUuidNull_returnMobileAll() {
when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
when(mInfo1.getGroupUuid()).thenReturn(null);
@@ -96,6 +99,7 @@
}
@Test
+ @Ignore
public void getMobileTemplate_groupUuidExist_returnMobileMerged() {
when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
when(mInfo1.getGroupUuid()).thenReturn(mParcelUuid);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index 21aa526..c288d51 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -26,6 +26,7 @@
import android.graphics.drawable.ColorDrawable;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -58,6 +59,7 @@
}
@Test
+ @Ignore
public void refresh_openNetwork_updateContentDescription() {
final String ssid = "ssid";
final String summary = "connected";
diff --git a/packages/SystemUI/res/layout-land/global_actions_grid_item.xml b/packages/SystemUI/res/layout-land/global_actions_grid_item.xml
index bc12338..0f9deaa 100644
--- a/packages/SystemUI/res/layout-land/global_actions_grid_item.xml
+++ b/packages/SystemUI/res/layout-land/global_actions_grid_item.xml
@@ -57,15 +57,5 @@
android:textColor="@color/global_actions_text"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
-
- <TextView
- android:visibility="gone"
- android:id="@*android:id/status"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textColor="@color/global_actions_text"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml
index 4404c874..31c7cbf 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml
@@ -56,15 +56,5 @@
android:textColor="@color/global_actions_text"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
-
- <TextView
- android:visibility="gone"
- android:id="@*android:id/status"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textColor="@color/global_actions_text"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml
new file mode 100644
index 0000000..50aa212
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<!-- RelativeLayouts have an issue enforcing minimum heights, so just
+ work around this for now with LinearLayouts. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingTop="@dimen/global_actions_grid_item_vertical_margin"
+ android:paddingBottom="@dimen/global_actions_grid_item_vertical_margin"
+ android:paddingLeft="@dimen/global_actions_grid_item_side_margin"
+ android:paddingRight="@dimen/global_actions_grid_item_side_margin"
+ android:layout_marginRight="3dp"
+ android:layout_marginLeft="3dp"
+ android:background="@drawable/rounded_bg_full">
+ <LinearLayout
+ android:layout_width="@dimen/global_actions_grid_item_width"
+ android:layout_height="@dimen/global_actions_grid_item_height"
+ android:gravity="top|center_horizontal"
+ android:orientation="vertical">
+ <ImageView
+ android:id="@*android:id/icon"
+ android:layout_width="@dimen/global_actions_grid_item_icon_width"
+ android:layout_height="@dimen/global_actions_grid_item_icon_height"
+ android:layout_marginTop="@dimen/global_actions_grid_item_icon_top_margin"
+ android:layout_marginBottom="@dimen/global_actions_grid_item_icon_bottom_margin"
+ android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin"
+ android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin"
+ android:scaleType="centerInside"
+ android:tint="@color/global_actions_text" />
+
+ <TextView
+ android:id="@*android:id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:singleLine="true"
+ android:gravity="center"
+ android:textSize="12dp"
+ android:textColor="@color/global_actions_text"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <TextView
+ android:visibility="gone"
+ android:id="@*android:id/status"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="@color/global_actions_text"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
new file mode 100644
index 0000000..4cfb47e
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/global_actions_grid_root"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset">
+
+ <com.android.systemui.globalactions.GlobalActionsFlatLayout
+ android:id="@id/global_actions_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:theme="@style/qs_theme"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ android:gravity="top | center_horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset"
+ android:layout_marginTop="@dimen/global_actions_top_margin"
+ android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset">
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layoutDirection="ltr"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin">
+ <!-- For separated items-->
+ <LinearLayout
+ android:id="@+id/separated_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
+ android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+ android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+ android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:translationZ="@dimen/global_actions_translate"
+ />
+ <!-- Grid of action items -->
+ <com.android.systemui.globalactions.ListGridLayout
+ android:id="@android:id/list"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="right"
+ android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+ android:translationZ="@dimen/global_actions_translate"
+ android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+ android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ >
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layoutDirection="locale"
+ />
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layoutDirection="locale"
+ />
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layoutDirection="locale"
+ />
+ </com.android.systemui.globalactions.ListGridLayout>
+ </LinearLayout>
+ </com.android.systemui.globalactions.GlobalActionsFlatLayout>
+
+ <LinearLayout
+ android:id="@+id/global_actions_panel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/global_actions_view">
+
+ <FrameLayout
+ android:translationY="@dimen/global_actions_plugin_offset"
+ android:id="@+id/global_actions_panel_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:translationY="@dimen/global_actions_plugin_offset"
+ android:id="@+id/global_actions_controls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/global_actions_panel">
+ <TextView
+ android:text="Home"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:gravity="center"
+ android:textSize="25dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@*android:string/config_headlineFontFamily" />
+ <LinearLayout
+ android:id="@+id/global_actions_controls_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+ </LinearLayout>
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</ScrollView>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 6ac9da4..995cb7d 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
diff --git a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
index 6b42400..366abaa 100644
--- a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
@@ -14,12 +14,27 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_screenshot_action_chip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/screenshot_action_chip_margin_horizontal"
- android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical"
- android:paddingHorizontal="@dimen/screenshot_action_chip_padding_horizontal"
- android:background="@drawable/action_chip_background"
- android:textColor="@color/global_screenshot_button_text"/>
+<com.android.systemui.screenshot.ScreenshotActionChip
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/global_screenshot_action_chip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/screenshot_action_chip_margin_horizontal"
+ android:layout_gravity="center"
+ android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical"
+ android:background="@drawable/action_chip_background"
+ android:gravity="center">
+ <ImageView
+ android:id="@+id/screenshot_action_chip_icon"
+ android:layout_width="@dimen/screenshot_action_chip_icon_size"
+ android:layout_height="@dimen/screenshot_action_chip_icon_size"
+ android:layout_marginStart="@dimen/screenshot_action_chip_padding_start"
+ android:layout_marginEnd="@dimen/screenshot_action_chip_padding_middle"/>
+ <TextView
+ android:id="@+id/screenshot_action_chip_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/screenshot_action_chip_padding_end"
+ android:textSize="@dimen/screenshot_action_chip_text_size"
+ android:textColor="@color/global_screenshot_button_text"/>
+</com.android.systemui.screenshot.ScreenshotActionChip>
diff --git a/packages/SystemUI/res/layout/global_screenshot_legacy.xml b/packages/SystemUI/res/layout/global_screenshot_legacy.xml
new file mode 100644
index 0000000..791c7ea
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_screenshot_legacy.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView android:id="@+id/global_screenshot_legacy_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@android:color/black"
+ android:visibility="gone" />
+ <ImageView android:id="@+id/global_screenshot_legacy"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:background="@drawable/screenshot_panel"
+ android:visibility="gone"
+ android:adjustViewBounds="true" />
+ <ImageView android:id="@+id/global_screenshot_legacy_flash"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@android:color/white"
+ android:visibility="gone" />
+ <com.android.systemui.screenshot.ScreenshotSelectorView
+ android:id="@+id/global_screenshot_legacy_selector"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ android:pointerIcon="crosshair"/>
+</FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index da0323a..c26cb9a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -288,14 +288,20 @@
<!-- Dimensions related to screenshots -->
<!-- The padding on the global screenshot background image -->
+ <dimen name="global_screenshot_legacy_bg_padding">20dp</dimen>
<dimen name="global_screenshot_bg_padding">20dp</dimen>
<dimen name="screenshot_action_container_corner_radius">10dp</dimen>
- <dimen name="screenshot_action_container_padding">20dp</dimen>
+ <dimen name="screenshot_action_container_padding">10dp</dimen>
<!-- Radius of the chip background on global screenshot actions -->
<dimen name="screenshot_button_corner_radius">20dp</dimen>
- <dimen name="screenshot_action_chip_margin_horizontal">10dp</dimen>
+ <dimen name="screenshot_action_chip_margin_horizontal">4dp</dimen>
<dimen name="screenshot_action_chip_padding_vertical">10dp</dimen>
- <dimen name="screenshot_action_chip_padding_horizontal">15dp</dimen>
+ <dimen name="screenshot_action_chip_icon_size">20dp</dimen>
+ <dimen name="screenshot_action_chip_padding_start">4dp</dimen>
+ <!-- Padding between icon and text -->
+ <dimen name="screenshot_action_chip_padding_middle">8dp</dimen>
+ <dimen name="screenshot_action_chip_padding_end">12dp</dimen>
+ <dimen name="screenshot_action_chip_text_size">14sp</dimen>
<!-- The width of the view containing navigation buttons -->
@@ -950,6 +956,9 @@
<dimen name="cell_overlay_padding">18dp</dimen>
<!-- Global actions power menu -->
+ <dimen name="global_actions_top_margin">12dp</dimen>
+ <dimen name="global_actions_plugin_offset">-145dp</dimen>
+
<dimen name="global_actions_panel_width">120dp</dimen>
<dimen name="global_actions_padding">12dp</dimen>
<dimen name="global_actions_translate">9dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index 8105faa..eab9706 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -27,9 +27,9 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -49,7 +49,7 @@
public ForegroundServiceNotificationListener(Context context,
ForegroundServiceController foregroundServiceController,
NotificationEntryManager notificationEntryManager,
- NotifCollection notifCollection) {
+ NotifPipeline notifPipeline) {
mContext = context;
mForegroundServiceController = foregroundServiceController;
@@ -77,7 +77,7 @@
});
mEntryManager.addNotificationLifetimeExtender(new ForegroundServiceLifetimeExtender());
- notifCollection.addCollectionListener(new NotifCollectionListener() {
+ notifPipeline.addCollectionListener(new NotifCollectionListener() {
@Override
public void onEntryAdded(NotificationEntry entry) {
addNotification(entry, entry.getImportance());
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index b083123..23d6458 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -18,7 +18,6 @@
import android.app.AppOpsManager;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -64,7 +63,6 @@
private H mBGHandler;
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
- private final PermissionFlagsCache mFlagsCache;
private boolean mListening;
@GuardedBy("mActiveItems")
@@ -81,17 +79,10 @@
};
@Inject
- public AppOpsControllerImpl(Context context, @Background Looper bgLooper,
- DumpController dumpController) {
- this(context, bgLooper, new PermissionFlagsCache(context), dumpController);
- }
-
- @VisibleForTesting
- protected AppOpsControllerImpl(Context context, Looper bgLooper, PermissionFlagsCache cache,
- DumpController dumpController) {
+ public AppOpsControllerImpl(Context context,
+ @Background Looper bgLooper, DumpController dumpController) {
mContext = context;
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- mFlagsCache = cache;
mBGHandler = new H(bgLooper);
final int numOps = OPS.length;
for (int i = 0; i < numOps; i++) {
@@ -209,7 +200,14 @@
mNotedItems.remove(item);
if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
}
- notifySuscribers(code, uid, packageName, false);
+ boolean active;
+ // Check if the item is also active
+ synchronized (mActiveItems) {
+ active = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null;
+ }
+ if (!active) {
+ notifySuscribers(code, uid, packageName, false);
+ }
}
private boolean addNoted(int code, int uid, String packageName) {
@@ -224,64 +222,13 @@
createdNew = true;
}
}
+ // We should keep this so we make sure it cannot time out.
+ mBGHandler.removeCallbacksAndMessages(item);
mBGHandler.scheduleRemoval(item, NOTED_OP_TIME_DELAY_MS);
return createdNew;
}
/**
- * Does the app-op code refer to a user sensitive permission for the specified user id
- * and package. Only user sensitive permission should be shown to the user by default.
- *
- * @param appOpCode The code of the app-op.
- * @param uid The uid of the user.
- * @param packageName The name of the package.
- *
- * @return {@code true} iff the app-op item is user sensitive
- */
- private boolean isUserSensitive(int appOpCode, int uid, String packageName) {
- String permission = AppOpsManager.opToPermission(appOpCode);
- if (permission == null) {
- return false;
- }
- int permFlags = mFlagsCache.getPermissionFlags(permission,
- packageName, UserHandle.getUserHandleForUid(uid));
- return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
- }
-
- /**
- * Does the app-op item refer to an operation that should be shown to the user.
- * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive
- * permission should be shown to the user by default.
- *
- * @param item The item
- *
- * @return {@code true} iff the app-op item should be shown to the user
- */
- private boolean isUserVisible(AppOpItem item) {
- return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
- }
-
-
- /**
- * Does the app-op, uid and package name, refer to an operation that should be shown to the
- * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or
- * ops that refer to user sensitive permission should be shown to the user by default.
- *
- * @param item The item
- *
- * @return {@code true} iff the app-op for should be shown to the user
- */
- private boolean isUserVisible(int appOpCode, int uid, String packageName) {
- // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
- // which may be user senstive, so for now always show it to the user.
- if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
- return true;
- }
-
- return isUserSensitive(appOpCode, uid, packageName);
- }
-
- /**
* Returns a copy of the list containing all the active AppOps that the controller tracks.
*
* @return List of active AppOps information
@@ -304,8 +251,8 @@
final int numActiveItems = mActiveItems.size();
for (int i = 0; i < numActiveItems; i++) {
AppOpItem item = mActiveItems.get(i);
- if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId)
- && isUserVisible(item)) {
+ if ((userId == UserHandle.USER_ALL
+ || UserHandle.getUserId(item.getUid()) == userId)) {
list.add(item);
}
}
@@ -314,8 +261,8 @@
final int numNotedItems = mNotedItems.size();
for (int i = 0; i < numNotedItems; i++) {
AppOpItem item = mNotedItems.get(i);
- if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId)
- && isUserVisible(item)) {
+ if ((userId == UserHandle.USER_ALL
+ || UserHandle.getUserId(item.getUid()) == userId)) {
list.add(item);
}
}
@@ -325,7 +272,21 @@
@Override
public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
- if (updateActives(code, uid, packageName, active)) {
+ if (DEBUG) {
+ Log.w(TAG, String.format("onActiveChanged(%d,%d,%s,%s", code, uid, packageName,
+ Boolean.toString(active)));
+ }
+ boolean activeChanged = updateActives(code, uid, packageName, active);
+ if (!activeChanged) return; // early return
+ // Check if the item is also noted, in that case, there's no update.
+ boolean alsoNoted;
+ synchronized (mNotedItems) {
+ alsoNoted = getAppOpItemLocked(mNotedItems, code, uid, packageName) != null;
+ }
+ // If active is true, we only send the update if the op is not actively noted (already true)
+ // If active is false, we only send the update if the op is not actively noted (prevent
+ // early removal)
+ if (!alsoNoted) {
mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active));
}
}
@@ -333,17 +294,23 @@
@Override
public void onOpNoted(int code, int uid, String packageName, int result) {
if (DEBUG) {
- Log.w(TAG, "Op: " + code + " with result " + AppOpsManager.MODE_NAMES[result]);
+ Log.w(TAG, "Noted op: " + code + " with result "
+ + AppOpsManager.MODE_NAMES[result] + " for package " + packageName);
}
if (result != AppOpsManager.MODE_ALLOWED) return;
- if (addNoted(code, uid, packageName)) {
+ boolean notedAdded = addNoted(code, uid, packageName);
+ if (!notedAdded) return; // early return
+ boolean alsoActive;
+ synchronized (mActiveItems) {
+ alsoActive = getAppOpItemLocked(mActiveItems, code, uid, packageName) != null;
+ }
+ if (!alsoActive) {
mBGHandler.post(() -> notifySuscribers(code, uid, packageName, true));
}
}
private void notifySuscribers(int code, int uid, String packageName, boolean active) {
- if (mCallbacksByCode.containsKey(code)
- && isUserVisible(code, uid, packageName)) {
+ if (mCallbacksByCode.containsKey(code)) {
if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
for (Callback cb: mCallbacksByCode.get(code)) {
cb.onActiveStateChanged(code, uid, packageName, active);
@@ -368,7 +335,7 @@
}
- protected final class H extends Handler {
+ protected class H extends Handler {
H(Looper looper) {
super(looper);
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
deleted file mode 100644
index f02c7af..0000000
--- a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
+++ /dev/null
@@ -1,70 +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 com.android.systemui.appops
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.os.UserHandle
-import android.util.ArrayMap
-import com.android.internal.annotations.VisibleForTesting
-
-private data class PermissionFlag(val flag: Int, val timestamp: Long)
-
-private data class PermissionFlagKey(
- val permission: String,
- val packageName: String,
- val user: UserHandle
-)
-
-internal const val CACHE_EXPIRATION = 10000L
-
-/**
- * Cache for PackageManager's PermissionFlags.
- *
- * Flags older than [CACHE_EXPIRATION] will be retrieved again.
- */
-internal open class PermissionFlagsCache(context: Context) {
- private val packageManager = context.packageManager
- private val permissionFlagsCache = ArrayMap<PermissionFlagKey, PermissionFlag>()
-
- /**
- * Retrieve permission flags from cache or PackageManager. There parameters will be passed
- * directly to [PackageManager].
- *
- * Calls to this method should be done from a background thread.
- */
- fun getPermissionFlags(permission: String, packageName: String, user: UserHandle): Int {
- val key = PermissionFlagKey(permission, packageName, user)
- val now = getCurrentTime()
- val value = permissionFlagsCache.getOrPut(key) {
- PermissionFlag(getFlags(key), now)
- }
- if (now - value.timestamp > CACHE_EXPIRATION) {
- val newValue = PermissionFlag(getFlags(key), now)
- permissionFlagsCache.put(key, newValue)
- return newValue.flag
- } else {
- return value.flag
- }
- }
-
- private fun getFlags(key: PermissionFlagKey) =
- packageManager.getPermissionFlags(key.permission, key.packageName, key.user)
-
- @VisibleForTesting
- protected open fun getCurrentTime() = System.currentTimeMillis()
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
index 563a0a7..31656a0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
@@ -961,6 +961,13 @@
if (view != null) {
final SpringAnimation animation =
(SpringAnimation) view.getTag(getTagIdForProperty(property));
+
+ // If the animation is null, the view was probably removed from the layout before
+ // the animation started.
+ if (animation == null) {
+ return;
+ }
+
if (afterCallbacks != null) {
animation.addEndListener(new OneTimeEndListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index b195238..a6fa414 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -30,10 +30,8 @@
import com.android.systemui.recents.Recents;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -113,9 +111,4 @@
@Singleton
@Binds
abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
-
- @Singleton
- @Binds
- abstract NotifListBuilder bindNotifListBuilder(NotifListBuilderImpl impl);
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index fc19fa0..c138462 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -59,7 +59,6 @@
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
-import android.text.TextUtils;
import android.util.ArraySet;
import android.util.FeatureFlagUtils;
import android.util.Log;
@@ -444,7 +443,8 @@
mKeyguardManager.isDeviceLocked())
: null;
- ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController);
+ ActionsDialog dialog = new ActionsDialog(
+ mContext, mAdapter, panelViewController, isControlsEnabled(mContext));
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.setKeyguardShowing(mKeyguardShowing);
@@ -518,7 +518,7 @@
@Override
public boolean shouldBeSeparated() {
- return shouldUseSeparatedView();
+ return !isControlsEnabled(mContext);
}
@Override
@@ -1154,6 +1154,9 @@
}
protected int getActionLayoutId(Context context) {
+ if (isControlsEnabled(context)) {
+ return com.android.systemui.R.layout.global_actions_grid_item_v2;
+ }
return com.android.systemui.R.layout.global_actions_grid_item;
}
@@ -1166,13 +1169,6 @@
TextView messageView = (TextView) v.findViewById(R.id.message);
messageView.setSelected(true); // necessary for marquee to work
- TextView statusView = (TextView) v.findViewById(R.id.status);
- final String status = getStatus();
- if (!TextUtils.isEmpty(status)) {
- statusView.setText(status);
- } else {
- statusView.setVisibility(View.GONE);
- }
if (mIcon != null) {
icon.setImageDrawable(mIcon);
icon.setScaleType(ScaleType.CENTER_CROP);
@@ -1257,32 +1253,26 @@
LayoutInflater inflater) {
willCreate();
- View v = inflater.inflate(R
- .layout.global_actions_item, parent, false);
+ View v = inflater.inflate(com.android.systemui.R
+ .layout.global_actions_grid_item, parent, false);
ImageView icon = (ImageView) v.findViewById(R.id.icon);
TextView messageView = (TextView) v.findViewById(R.id.message);
- TextView statusView = (TextView) v.findViewById(R.id.status);
final boolean enabled = isEnabled();
+ boolean on = ((mState == State.On) || (mState == State.TurningOn));
if (messageView != null) {
- messageView.setText(mMessageResId);
+ messageView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
messageView.setEnabled(enabled);
messageView.setSelected(true); // necessary for marquee to work
}
- boolean on = ((mState == State.On) || (mState == State.TurningOn));
if (icon != null) {
icon.setImageDrawable(context.getDrawable(
(on ? mEnabledIconResId : mDisabledIconResid)));
icon.setEnabled(enabled);
}
- if (statusView != null) {
- statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
- statusView.setVisibility(View.VISIBLE);
- statusView.setEnabled(enabled);
- }
v.setEnabled(enabled);
return v;
@@ -1428,8 +1418,8 @@
} else if (TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
// Airplane mode can be changed after ECM exits if airplane toggle button
// is pressed during ECM mode
- if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
- mIsWaitingForEcmExit) {
+ if (!(intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false))
+ && mIsWaitingForEcmExit) {
mIsWaitingForEcmExit = false;
changeAirplaneModeSystemSetting(true);
}
@@ -1539,15 +1529,18 @@
private ResetOrientationData mResetOrientationData;
private boolean mHadTopUi;
private final StatusBarWindowController mStatusBarWindowController;
+ private boolean mControlsEnabled;
ActionsDialog(Context context, MyAdapter adapter,
- GlobalActionsPanelPlugin.PanelViewController plugin) {
+ GlobalActionsPanelPlugin.PanelViewController plugin,
+ boolean controlsEnabled) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
mContext = context;
mAdapter = adapter;
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
mStatusBarService = Dependency.get(IStatusBarService.class);
mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
+ mControlsEnabled = controlsEnabled;
// Window initialization
Window window = getWindow();
@@ -1664,6 +1657,10 @@
}
private int getGlobalActionsLayoutId(Context context) {
+ if (mControlsEnabled) {
+ return com.android.systemui.R.layout.global_actions_grid_v2;
+ }
+
int rotation = RotationUtils.getRotation(context);
boolean useGridLayout = isForceGridEnabled(context)
|| (shouldUsePanel() && rotation == RotationUtils.ROTATION_NONE);
@@ -1867,4 +1864,9 @@
private static boolean shouldUseSeparatedView() {
return true;
}
+
+ private static boolean isControlsEnabled(Context context) {
+ return Settings.Secure.getInt(
+ context.getContentResolver(), "systemui.controls_available", 0) == 1;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
new file mode 100644
index 0000000..6749f1d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
@@ -0,0 +1,186 @@
+/*
+ * 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.systemui.globalactions;
+
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Single row implementation of the button layout created by the global actions dialog.
+ */
+public class GlobalActionsFlatLayout extends GlobalActionsLayout {
+ public GlobalActionsFlatLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mBackgroundsSet = true;
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ // backgrounds set only once, the first time onMeasure is called after inflation
+ // if (getListView() != null && !mBackgroundsSet) {
+ // setBackgrounds();
+ // mBackgroundsSet = true;
+ // }
+ }
+
+ @VisibleForTesting
+ protected void setupListView() {
+ ListGridLayout listView = getListView();
+ listView.setExpectedCount(Math.min(2, mAdapter.countListItems()));
+ listView.setReverseSublists(shouldReverseSublists());
+ listView.setReverseItems(shouldReverseListItems());
+ listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns());
+ }
+
+ @Override
+ public void onUpdateList() {
+ setupListView();
+ super.onUpdateList();
+ updateSeparatedItemSize();
+ }
+
+ /**
+ * If the separated view contains only one item, expand the bounds of that item to take up the
+ * entire view, so that the whole thing is touch-able.
+ */
+ @VisibleForTesting
+ protected void updateSeparatedItemSize() {
+ ViewGroup separated = getSeparatedView();
+ if (separated.getChildCount() == 0) {
+ return;
+ }
+ View firstChild = separated.getChildAt(0);
+ ViewGroup.LayoutParams childParams = firstChild.getLayoutParams();
+
+ if (separated.getChildCount() == 1) {
+ childParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ childParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ } else {
+ childParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ childParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+ }
+
+ @Override
+ protected ListGridLayout getListView() {
+ return (ListGridLayout) super.getListView();
+ }
+
+ @Override
+ protected void removeAllListViews() {
+ ListGridLayout list = getListView();
+ if (list != null) {
+ list.removeAllItems();
+ }
+ }
+
+ @Override
+ protected void addToListView(View v, boolean reverse) {
+ ListGridLayout list = getListView();
+ if (list != null) {
+ list.addItem(v);
+ }
+ }
+
+ @Override
+ public void removeAllItems() {
+ ViewGroup separatedList = getSeparatedView();
+ ListGridLayout list = getListView();
+ if (separatedList != null) {
+ separatedList.removeAllViews();
+ }
+ if (list != null) {
+ list.removeAllItems();
+ }
+ }
+
+ /**
+ * Determines whether the ListGridLayout should fill sublists in the reverse order.
+ * Used to account for sublist ordering changing between landscape and seascape views.
+ */
+ @VisibleForTesting
+ protected boolean shouldReverseSublists() {
+ if (getCurrentRotation() == ROTATION_SEASCAPE) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Determines whether the ListGridLayout should fill rows first instead of columns.
+ * Used to account for vertical/horizontal changes due to landscape or seascape rotations.
+ */
+ @VisibleForTesting
+ protected boolean shouldSwapRowsAndColumns() {
+ if (getCurrentRotation() == ROTATION_NONE) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean shouldReverseListItems() {
+ int rotation = getCurrentRotation();
+ boolean reverse = false; // should we add items to parents in the reverse order?
+ if (rotation == ROTATION_NONE
+ || rotation == ROTATION_SEASCAPE) {
+ reverse = !reverse; // if we're in portrait or seascape, reverse items
+ }
+ if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ reverse = !reverse; // if we're in an RTL language, reverse items (again)
+ }
+ return reverse;
+ }
+
+ @VisibleForTesting
+ protected float getAnimationDistance() {
+ int rows = getListView().getRowCount();
+ float gridItemSize = getContext().getResources().getDimension(
+ com.android.systemui.R.dimen.global_actions_grid_item_height);
+ return rows * gridItemSize / 2;
+ }
+
+ @Override
+ public float getAnimationOffsetX() {
+ switch (getCurrentRotation()) {
+ case ROTATION_LANDSCAPE:
+ return getAnimationDistance();
+ case ROTATION_SEASCAPE:
+ return -getAnimationDistance();
+ default: // Portrait
+ return 0;
+ }
+ }
+
+ @Override
+ public float getAnimationOffsetY() {
+ if (getCurrentRotation() == ROTATION_NONE) {
+ return getAnimationDistance();
+ }
+ return 0;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 02c4beb..27c9555 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -20,7 +20,7 @@
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_CORNER_FLOW;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED;
import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
import android.animation.Animator;
@@ -64,7 +64,6 @@
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.TextView;
import android.widget.Toast;
import com.android.systemui.R;
@@ -246,20 +245,13 @@
mSaveInBgTask.cancel(false);
}
- if (!DeviceConfig.getBoolean(
- NAMESPACE_SYSTEMUI, SCREENSHOT_CORNER_FLOW, false)) {
- mNotificationsController.reset();
- mNotificationsController.setImage(mScreenBitmap);
- mNotificationsController.showSavingScreenshotNotification();
- }
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data).execute();
}
/**
* Takes a screenshot of the current display and shows an animation.
*/
- private void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible,
- boolean navBarVisible, Rect crop) {
+ private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
int rot = mDisplay.getRotation();
int width = crop.width();
int height = crop.height();
@@ -278,21 +270,20 @@
mScreenBitmap.prepareToDraw();
// Start the post-screenshot animation
- startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
- statusBarVisible, navBarVisible);
+ startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels);
}
- void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) {
+ void takeScreenshot(Consumer<Uri> finisher) {
mDisplay.getRealMetrics(mDisplayMetrics);
- takeScreenshot(finisher, statusBarVisible, navBarVisible,
+ takeScreenshot(
+ finisher,
new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
}
/**
* Displays a screenshot selector
*/
- void takeScreenshotPartial(final Consumer<Uri> finisher, final boolean statusBarVisible,
- final boolean navBarVisible) {
+ void takeScreenshotPartial(final Consumer<Uri> finisher) {
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
@Override
@@ -312,8 +303,7 @@
if (rect != null) {
if (rect.width() != 0 && rect.height() != 0) {
// Need mScreenshotLayout to handle it after the view disappears
- mScreenshotLayout.post(() -> takeScreenshot(
- finisher, statusBarVisible, navBarVisible, rect));
+ mScreenshotLayout.post(() -> takeScreenshot(finisher, rect));
}
}
@@ -364,8 +354,7 @@
/**
* Starts the animation after taking the screenshot
*/
- private void startAnimation(final Consumer<Uri> finisher, int w, int h,
- boolean statusBarVisible, boolean navBarVisible) {
+ private void startAnimation(final Consumer<Uri> finisher, int w, int h) {
// If power save is on, show a toast so there is some visual indication that a screenshot
// has been taken.
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -385,50 +374,30 @@
mScreenshotAnimation.removeAllListeners();
}
- boolean useCornerFlow =
- DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, SCREENSHOT_CORNER_FLOW, false);
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
- ValueAnimator screenshotFadeOutAnim = useCornerFlow
- ? createScreenshotToCornerAnimation(w, h)
- : createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible);
+ ValueAnimator screenshotFadeOutAnim = createScreenshotToCornerAnimation(w, h);
mScreenshotAnimation = new AnimatorSet();
mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Save the screenshot once we have a bit of time now
- if (!useCornerFlow) {
- saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
- @Override
- void onActionsReady(Uri uri, List<Notification.Action> actions) {
- if (uri == null) {
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- } else {
- mNotificationsController
- .showScreenshotActionsNotification(uri, actions);
- }
+ saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
+ @Override
+ void onActionsReady(Uri uri, List<Notification.Action> actions) {
+ if (uri == null) {
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ } else {
+ mScreenshotHandler.post(() ->
+ createScreenshotActionsShadeAnimation(actions).start());
}
- });
- clearScreenshot();
- } else {
- saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
- @Override
- void onActionsReady(Uri uri, List<Notification.Action> actions) {
- if (uri == null) {
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- } else {
- mScreenshotHandler.post(() ->
- createScreenshotActionsShadeAnimation(actions).start());
- }
- }
- });
- mScreenshotHandler.sendMessageDelayed(
- mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
- SCREENSHOT_CORNER_TIMEOUT_MILLIS);
- }
+ }
+ });
+ mScreenshotHandler.sendMessageDelayed(
+ mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
+ SCREENSHOT_CORNER_TIMEOUT_MILLIS);
}
});
mScreenshotHandler.post(() -> {
@@ -492,7 +461,7 @@
}
@Override
- public void onAnimationEnd(android.animation.Animator animation) {
+ public void onAnimationEnd(Animator animation) {
mScreenshotFlash.setVisibility(View.GONE);
}
});
@@ -513,81 +482,6 @@
return anim;
}
- private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
- boolean navBarVisible) {
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBackgroundView.setVisibility(View.GONE);
- mScreenshotView.setVisibility(View.GONE);
- mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- });
-
- if (!statusBarVisible || !navBarVisible) {
- // There is no status bar/nav bar, so just fade the screenshot away in place
- anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION);
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
- - t * (SCREENSHOT_DROP_IN_MIN_SCALE
- - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
- mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(1f - t);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- }
- });
- } else {
- // In the case where there is a status bar, animate to the origin of the bar (top-left)
- final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION
- / SCREENSHOT_DROP_OUT_DURATION;
- final Interpolator scaleInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- if (x < scaleDurationPct) {
- // Decelerate, and scale the input accordingly
- return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
- }
- return 1f;
- }
- };
-
- // Determine the bounds of how to scale
- float halfScreenWidth = (w - 2f * mBgPadding) / 2f;
- float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
- final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
- final PointF finalPos = new PointF(
- -halfScreenWidth
- + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
- -halfScreenHeight
- + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
-
- // Animate the screenshot to the status bar
- anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
- - scaleInterpolator.getInterpolation(t)
- * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);
- mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t));
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotView.setTranslationX(t * finalPos.x);
- mScreenshotView.setTranslationY(t * finalPos.y);
- }
- });
- }
- return anim;
- }
-
private ValueAnimator createScreenshotToCornerAnimation(int w, int h) {
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
@@ -634,9 +528,10 @@
mActionsView.removeAllViews();
for (Notification.Action action : actions) {
- TextView actionChip = (TextView) inflater.inflate(
+ ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
R.layout.global_screenshot_action_chip, mActionsView, false);
actionChip.setText(action.title);
+ actionChip.setIcon(action.getIcon(), true);
actionChip.setOnClickListener(v -> {
try {
action.actionIntent.send();
@@ -648,13 +543,16 @@
});
mActionsView.addView(actionChip);
}
- TextView scrollChip = (TextView) inflater.inflate(
- R.layout.global_screenshot_action_chip, mActionsView, false);
- Toast scrollNotImplemented = Toast.makeText(
- mContext, "Not implemented", Toast.LENGTH_SHORT);
- scrollChip.setText("Scroll"); // TODO (mkephart): add resource and translate
- scrollChip.setOnClickListener(v -> scrollNotImplemented.show());
- mActionsView.addView(scrollChip);
+
+ if (DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, SCREENSHOT_SCROLLING_ENABLED, false)) {
+ ScreenshotActionChip scrollChip = (ScreenshotActionChip) inflater.inflate(
+ R.layout.global_screenshot_action_chip, mActionsView, false);
+ Toast scrollNotImplemented = Toast.makeText(
+ mContext, "Not implemented", Toast.LENGTH_SHORT);
+ scrollChip.setText("Extend"); // TODO (mkephart): add resource and translate
+ scrollChip.setOnClickListener(v -> scrollNotImplemented.show());
+ mActionsView.addView(scrollChip);
+ }
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
mActionsView.setY(mDisplayMetrics.heightPixels);
@@ -776,8 +674,7 @@
String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
Slog.d(TAG, "Executing smart action [" + actionType + "]:" + actionIntent);
ActivityOptions opts = ActivityOptions.makeBasic();
- context.startActivityAsUser(actionIntent, opts.toBundle(),
- UserHandle.CURRENT);
+ context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
ScreenshotSmartActions.notifyScreenshotAction(
context, intent.getStringExtra(EXTRA_ID), actionType, true);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
new file mode 100644
index 0000000..11aa80b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.media.MediaActionSound;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.PowerManager;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Class for handling device screen shots
+ *
+ * @deprecated will be removed when corner flow is complete and tested
+ */
+@Singleton
+@Deprecated
+public class GlobalScreenshotLegacy {
+
+ // These strings are used for communicating the action invoked to
+ // ScreenshotNotificationSmartActionsProvider.
+ static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
+ static final String EXTRA_ID = "android:screenshot_id";
+ static final String ACTION_TYPE_DELETE = "Delete";
+ static final String ACTION_TYPE_SHARE = "Share";
+ static final String ACTION_TYPE_EDIT = "Edit";
+ static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
+ static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
+
+ static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
+ static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
+ static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
+
+ private static final String TAG = "GlobalScreenshot";
+
+ private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
+ private static final int SCREENSHOT_DROP_IN_DURATION = 430;
+ private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
+ private static final int SCREENSHOT_DROP_OUT_DURATION = 430;
+ private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;
+ private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320;
+ private static final float BACKGROUND_ALPHA = 0.5f;
+ private static final float SCREENSHOT_SCALE = 1f;
+ private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f;
+ private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f;
+ private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f;
+ private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f;
+
+ private final ScreenshotNotificationsController mNotificationsController;
+
+ private Context mContext;
+ private WindowManager mWindowManager;
+ private WindowManager.LayoutParams mWindowLayoutParams;
+ private Display mDisplay;
+ private DisplayMetrics mDisplayMetrics;
+
+ private Bitmap mScreenBitmap;
+ private View mScreenshotLayout;
+ private ScreenshotSelectorView mScreenshotSelectorView;
+ private ImageView mBackgroundView;
+ private ImageView mScreenshotView;
+ private ImageView mScreenshotFlash;
+
+ private AnimatorSet mScreenshotAnimation;
+
+ private float mBgPadding;
+ private float mBgPaddingScale;
+
+ private AsyncTask<Void, Void, Void> mSaveInBgTask;
+
+ private MediaActionSound mCameraSound;
+
+ /**
+ * @param context everything needs a context :(
+ */
+ @Inject
+ public GlobalScreenshotLegacy(
+ Context context, @Main Resources resources, LayoutInflater layoutInflater,
+ ScreenshotNotificationsController screenshotNotificationsController) {
+ mContext = context;
+ mNotificationsController = screenshotNotificationsController;
+
+ // Inflate the screenshot layout
+ mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot_legacy, null);
+ mBackgroundView = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy_background);
+ mScreenshotView = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy);
+
+ mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy_flash);
+ mScreenshotSelectorView = mScreenshotLayout.findViewById(
+ R.id.global_screenshot_legacy_selector);
+ mScreenshotLayout.setFocusable(true);
+ mScreenshotSelectorView.setFocusable(true);
+ mScreenshotSelectorView.setFocusableInTouchMode(true);
+ mScreenshotLayout.setOnTouchListener((v, event) -> {
+ // Intercept and ignore all touch events
+ return true;
+ });
+
+ // Setup the window that we are going to use
+ mWindowLayoutParams = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
+ WindowManager.LayoutParams.TYPE_SCREENSHOT,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
+ PixelFormat.TRANSLUCENT);
+ mWindowLayoutParams.setTitle("ScreenshotAnimation");
+ mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mWindowLayoutParams.setFitWindowInsetsTypes(0 /* types */);
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mDisplay = mWindowManager.getDefaultDisplay();
+ mDisplayMetrics = new DisplayMetrics();
+ mDisplay.getRealMetrics(mDisplayMetrics);
+
+ // Scale has to account for both sides of the bg
+ mBgPadding = (float) resources.getDimensionPixelSize(
+ R.dimen.global_screenshot_legacy_bg_padding);
+ mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels;
+
+
+ // Setup the Camera shutter sound
+ mCameraSound = new MediaActionSound();
+ mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
+ }
+
+ /**
+ * Creates a new worker thread and saves the screenshot to the media store.
+ */
+ private void saveScreenshotInWorkerThread(
+ Consumer<Uri> finisher,
+ @Nullable GlobalScreenshot.ActionsReadyListener actionsReadyListener) {
+ GlobalScreenshot.SaveImageInBackgroundData data =
+ new GlobalScreenshot.SaveImageInBackgroundData();
+ data.image = mScreenBitmap;
+ data.finisher = finisher;
+ data.mActionsReadyListener = actionsReadyListener;
+ if (mSaveInBgTask != null) {
+ mSaveInBgTask.cancel(false);
+ }
+
+ mNotificationsController.reset();
+ mNotificationsController.setImage(mScreenBitmap);
+ mNotificationsController.showSavingScreenshotNotification();
+
+ mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data).execute();
+ }
+
+ /**
+ * Takes a screenshot of the current display and shows an animation.
+ */
+ private void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible,
+ boolean navBarVisible, Rect crop) {
+ int rot = mDisplay.getRotation();
+ int width = crop.width();
+ int height = crop.height();
+
+ // Take the screenshot
+ mScreenBitmap = SurfaceControl.screenshot(crop, width, height, rot);
+ if (mScreenBitmap == null) {
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ finisher.accept(null);
+ return;
+ }
+
+ // Optimizations
+ mScreenBitmap.setHasAlpha(false);
+ mScreenBitmap.prepareToDraw();
+
+ // Start the post-screenshot animation
+ startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+ statusBarVisible, navBarVisible);
+ }
+
+ void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) {
+ mDisplay.getRealMetrics(mDisplayMetrics);
+ takeScreenshot(finisher, statusBarVisible, navBarVisible,
+ new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
+ }
+
+ /**
+ * Displays a screenshot selector
+ */
+ void takeScreenshotPartial(final Consumer<Uri> finisher, final boolean statusBarVisible,
+ final boolean navBarVisible) {
+ mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ ScreenshotSelectorView view = (ScreenshotSelectorView) v;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ view.startSelection((int) event.getX(), (int) event.getY());
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ view.updateSelection((int) event.getX(), (int) event.getY());
+ return true;
+ case MotionEvent.ACTION_UP:
+ view.setVisibility(View.GONE);
+ mWindowManager.removeView(mScreenshotLayout);
+ final Rect rect = view.getSelectionRect();
+ if (rect != null) {
+ if (rect.width() != 0 && rect.height() != 0) {
+ // Need mScreenshotLayout to handle it after the view disappears
+ mScreenshotLayout.post(() -> takeScreenshot(
+ finisher, statusBarVisible, navBarVisible, rect));
+ }
+ }
+
+ view.stopSelection();
+ return true;
+ }
+
+ return false;
+ }
+ });
+ mScreenshotLayout.post(new Runnable() {
+ @Override
+ public void run() {
+ mScreenshotSelectorView.setVisibility(View.VISIBLE);
+ mScreenshotSelectorView.requestFocus();
+ }
+ });
+ }
+
+ /**
+ * Cancels screenshot request
+ */
+ void stopScreenshot() {
+ // If the selector layer still presents on screen, we remove it and resets its state.
+ if (mScreenshotSelectorView.getSelectionRect() != null) {
+ mWindowManager.removeView(mScreenshotLayout);
+ mScreenshotSelectorView.stopSelection();
+ }
+ }
+
+ /**
+ * Clears current screenshot
+ */
+ private void clearScreenshot() {
+ if (mScreenshotLayout.isAttachedToWindow()) {
+ mWindowManager.removeView(mScreenshotLayout);
+ }
+
+ // Clear any references to the bitmap
+ mScreenBitmap = null;
+ mScreenshotView.setImageBitmap(null);
+ mBackgroundView.setVisibility(View.GONE);
+ mScreenshotView.setVisibility(View.GONE);
+ mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+
+ /**
+ * Starts the animation after taking the screenshot
+ */
+ private void startAnimation(final Consumer<Uri> finisher, int w, int h,
+ boolean statusBarVisible, boolean navBarVisible) {
+ // If power save is on, show a toast so there is some visual indication that a screenshot
+ // has been taken.
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ if (powerManager.isPowerSaveMode()) {
+ Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
+ }
+
+ // Add the view for the animation
+ mScreenshotView.setImageBitmap(mScreenBitmap);
+ mScreenshotLayout.requestFocus();
+
+ // Setup the animation with the screenshot just taken
+ if (mScreenshotAnimation != null) {
+ if (mScreenshotAnimation.isStarted()) {
+ mScreenshotAnimation.end();
+ }
+ mScreenshotAnimation.removeAllListeners();
+ }
+
+ mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
+ ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,
+ statusBarVisible, navBarVisible);
+ mScreenshotAnimation = new AnimatorSet();
+ mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
+ mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Save the screenshot once we have a bit of time now
+ saveScreenshotInWorkerThread(finisher, new GlobalScreenshot.ActionsReadyListener() {
+ @Override
+ void onActionsReady(Uri uri, List<Notification.Action> actions) {
+ if (uri == null) {
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ } else {
+ mNotificationsController.showScreenshotActionsNotification(
+ uri, actions);
+ }
+ }
+ });
+ clearScreenshot();
+ }
+ });
+ mScreenshotLayout.post(() -> {
+ // Play the shutter sound to notify that we've taken a screenshot
+ mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+
+ mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mScreenshotView.buildLayer();
+ mScreenshotAnimation.start();
+ });
+ }
+
+ private ValueAnimator createScreenshotDropInAnimation() {
+ final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
+ / SCREENSHOT_DROP_IN_DURATION);
+ final float flashDurationPct = 2f * flashPeakDurationPct;
+ final Interpolator flashAlphaInterpolator = new Interpolator() {
+ @Override
+ public float getInterpolation(float x) {
+ // Flash the flash view in and out quickly
+ if (x <= flashDurationPct) {
+ return (float) Math.sin(Math.PI * (x / flashDurationPct));
+ }
+ return 0;
+ }
+ };
+ final Interpolator scaleInterpolator = new Interpolator() {
+ @Override
+ public float getInterpolation(float x) {
+ // We start scaling when the flash is at it's peak
+ if (x < flashPeakDurationPct) {
+ return 0;
+ }
+ return (x - flashDurationPct) / (1f - flashDurationPct);
+ }
+ };
+
+ Resources r = mContext.getResources();
+ if ((r.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES) {
+ mScreenshotView.getBackground().setTint(Color.BLACK);
+ } else {
+ mScreenshotView.getBackground().setTintList(null);
+ }
+
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mBackgroundView.setAlpha(0f);
+ mBackgroundView.setVisibility(View.VISIBLE);
+ mScreenshotView.setAlpha(0f);
+ mScreenshotView.setTranslationX(0f);
+ mScreenshotView.setTranslationY(0f);
+ mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
+ mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
+ mScreenshotView.setVisibility(View.VISIBLE);
+ mScreenshotFlash.setAlpha(0f);
+ mScreenshotFlash.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(android.animation.Animator animation) {
+ mScreenshotFlash.setVisibility(View.GONE);
+ }
+ });
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = (Float) animation.getAnimatedValue();
+ float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
+ - scaleInterpolator.getInterpolation(t)
+ * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
+ mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
+ mScreenshotView.setAlpha(t);
+ mScreenshotView.setScaleX(scaleT);
+ mScreenshotView.setScaleY(scaleT);
+ mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
+ }
+ });
+ return anim;
+ }
+
+ private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
+ boolean navBarVisible) {
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBackgroundView.setVisibility(View.GONE);
+ mScreenshotView.setVisibility(View.GONE);
+ mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ });
+
+ if (!statusBarVisible || !navBarVisible) {
+ // There is no status bar/nav bar, so just fade the screenshot away in place
+ anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION);
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = (Float) animation.getAnimatedValue();
+ float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
+ - t * (SCREENSHOT_DROP_IN_MIN_SCALE
+ - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
+ mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
+ mScreenshotView.setAlpha(1f - t);
+ mScreenshotView.setScaleX(scaleT);
+ mScreenshotView.setScaleY(scaleT);
+ }
+ });
+ } else {
+ // In the case where there is a status bar, animate to the origin of the bar (top-left)
+ final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION
+ / SCREENSHOT_DROP_OUT_DURATION;
+ final Interpolator scaleInterpolator = new Interpolator() {
+ @Override
+ public float getInterpolation(float x) {
+ if (x < scaleDurationPct) {
+ // Decelerate, and scale the input accordingly
+ return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
+ }
+ return 1f;
+ }
+ };
+
+ // Determine the bounds of how to scale
+ float halfScreenWidth = (w - 2f * mBgPadding) / 2f;
+ float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
+ final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
+ final PointF finalPos = new PointF(
+ -halfScreenWidth
+ + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
+ -halfScreenHeight
+ + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
+
+ // Animate the screenshot to the status bar
+ anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = (Float) animation.getAnimatedValue();
+ float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
+ - scaleInterpolator.getInterpolation(t)
+ * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);
+ mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
+ mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t));
+ mScreenshotView.setScaleX(scaleT);
+ mScreenshotView.setScaleY(scaleT);
+ mScreenshotView.setTranslationX(t * finalPos.x);
+ mScreenshotView.setTranslationY(t * finalPos.y);
+ }
+ });
+ }
+ return anim;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
new file mode 100644
index 0000000..6edacd1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
@@ -0,0 +1,73 @@
+/*
+ * 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.screenshot;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+
+/**
+ * View for a chip with an icon and text.
+ */
+public class ScreenshotActionChip extends LinearLayout {
+
+ private ImageView mIcon;
+ private TextView mText;
+ private @ColorInt int mIconColor;
+
+ public ScreenshotActionChip(Context context) {
+ this(context, null);
+ }
+
+ public ScreenshotActionChip(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ScreenshotActionChip(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public ScreenshotActionChip(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ mIconColor = context.getColor(R.color.global_screenshot_button_text);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mIcon = findViewById(R.id.screenshot_action_chip_icon);
+ mText = findViewById(R.id.screenshot_action_chip_text);
+ }
+
+ void setIcon(Icon icon, boolean tint) {
+ if (tint) {
+ icon.setTint(mIconColor);
+ }
+ mIcon.setImageIcon(icon);
+ }
+
+ void setText(CharSequence text) {
+ mText.setText(text);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 29b96a9..4f045d5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -16,15 +16,21 @@
package com.android.systemui.screenshot;
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
+
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_CORNER_FLOW;
+
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.util.Log;
import android.view.WindowManager;
@@ -36,9 +42,10 @@
private static final String TAG = "TakeScreenshotService";
private final GlobalScreenshot mScreenshot;
+ private final GlobalScreenshotLegacy mScreenshotLegacy;
private final UserManager mUserManager;
- private Handler mHandler = new Handler() {
+ private Handler mHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
final Messenger callback = msg.replyTo;
@@ -59,12 +66,24 @@
return;
}
+ // TODO (mkephart): clean up once notifications flow is fully deprecated
+ boolean useCornerFlow = DeviceConfig.getBoolean(
+ NAMESPACE_SYSTEMUI, SCREENSHOT_CORNER_FLOW, false);
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
- mScreenshot.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0);
+ if (useCornerFlow) {
+ mScreenshot.takeScreenshot(finisher);
+ } else {
+ mScreenshotLegacy.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0);
+ }
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
- mScreenshot.takeScreenshotPartial(finisher, msg.arg1 > 0, msg.arg2 > 0);
+ if (useCornerFlow) {
+ mScreenshot.takeScreenshotPartial(finisher);
+ } else {
+ mScreenshotLegacy.takeScreenshotPartial(
+ finisher, msg.arg1 > 0, msg.arg2 > 0);
+ }
break;
default:
Log.d(TAG, "Invalid screenshot option: " + msg.what);
@@ -73,8 +92,10 @@
};
@Inject
- public TakeScreenshotService(GlobalScreenshot globalScreenshot, UserManager userManager) {
+ public TakeScreenshotService(GlobalScreenshot globalScreenshot,
+ GlobalScreenshotLegacy globalScreenshotLegacy, UserManager userManager) {
mScreenshot = globalScreenshot;
+ mScreenshotLegacy = globalScreenshotLegacy;
mUserManager = userManager;
}
@@ -86,6 +107,7 @@
@Override
public boolean onUnbind(Intent intent) {
if (mScreenshot != null) mScreenshot.stopScreenshot();
+ if (mScreenshotLegacy != null) mScreenshotLegacy.stopScreenshot();
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 36dcaac..4a22831 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -43,7 +43,7 @@
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.logging.NotifEvent;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index e1268f6..73bfe25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -16,13 +16,11 @@
package com.android.systemui.statusbar.notification.collection;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
-
import java.util.List;
/**
- * Utility class for dumping the results of a {@link NotifListBuilder} to a debug string.
+ * Utility class for dumping the results of a {@link ShadeListBuilder} to a debug string.
*/
public class ListDumper {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 856b75b..4b15b7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -47,9 +47,13 @@
import android.util.Log;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CoalescedEvent;
-import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer;
-import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer.BatchableNotificationHandler;
+import com.android.systemui.statusbar.notification.collection.coalescer.CoalescedEvent;
+import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
+import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer.BatchableNotificationHandler;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.Assert;
import java.lang.annotation.Retention;
@@ -123,36 +127,25 @@
* Sets the class responsible for converting the collection into the list of currently-visible
* notifications.
*/
- public void setBuildListener(CollectionReadyForBuildListener buildListener) {
+ void setBuildListener(CollectionReadyForBuildListener buildListener) {
Assert.isMainThread();
mBuildListener = buildListener;
}
- /**
- * Returns the list of "active" notifications, i.e. the notifications that are currently posted
- * to the phone. In general, this tracks closely to the list maintained by NotificationManager,
- * but it can diverge slightly due to lifetime extenders.
- *
- * The returned list is read-only, unsorted, unfiltered, and ungrouped.
- */
- public Collection<NotificationEntry> getNotifs() {
+ /** @see NotifPipeline#getActiveNotifs() */
+ Collection<NotificationEntry> getActiveNotifs() {
Assert.isMainThread();
return mReadOnlyNotificationSet;
}
- /**
- * Registers a listener to be informed when notifications are added, removed or updated.
- */
- public void addCollectionListener(NotifCollectionListener listener) {
+ /** @see NotifPipeline#addCollectionListener(NotifCollectionListener) */
+ void addCollectionListener(NotifCollectionListener listener) {
Assert.isMainThread();
mNotifCollectionListeners.add(listener);
}
- /**
- * Registers a lifetime extender. Lifetime extenders can cause notifications that have been
- * dismissed or retracted to be temporarily retained in the collection.
- */
- public void addNotificationLifetimeExtender(NotifLifetimeExtender extender) {
+ /** @see NotifPipeline#addNotificationLifetimeExtender(NotifLifetimeExtender) */
+ void addNotificationLifetimeExtender(NotifLifetimeExtender extender) {
Assert.isMainThread();
checkForReentrantCall();
if (mLifetimeExtenders.contains(extender)) {
@@ -165,7 +158,7 @@
/**
* Dismiss a notification on behalf of the user.
*/
- public void dismissNotification(
+ void dismissNotification(
NotificationEntry entry,
@CancellationReason int reason,
@NonNull DismissedByUserStats stats) {
@@ -446,7 +439,7 @@
REASON_TIMEOUT,
})
@Retention(RetentionPolicy.SOURCE)
- @interface CancellationReason {}
+ public @interface CancellationReason {}
public static final int REASON_UNKNOWN = 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index 0d17557..e7b772f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -25,6 +25,9 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
@@ -107,7 +110,7 @@
DISMISS_SENTIMENT_NEUTRAL,
NotificationVisibility.obtain(entry.getKey(),
entry.getRanking().getRank(),
- mNotifCollection.getNotifs().size(),
+ mNotifCollection.getActiveNotifs().size(),
true,
NotificationLogger.getNotificationLocation(entry))
));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
new file mode 100644
index 0000000..7124517
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+
+import java.util.Collection;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * The system that constructs the "shade list", the filtered, grouped, and sorted list of
+ * notifications that are currently being displayed to the user in the notification shade.
+ *
+ * The pipeline proceeds through a series of stages in order to produce the final list (see below).
+ * Each stage exposes hooks and listeners to allow other code to participate.
+ *
+ * This list differs from the canonical one we receive from system server in a few ways:
+ * - Filtered: Some notifications are filtered out. For example, we filter out notifications whose
+ * views haven't been inflated yet. We also filter out some notifications if we're on the lock
+ * screen and notifications for other users. So participate, see
+ * {@link #addPreGroupFilter} and similar methods.
+ * - Grouped: Notifications that are part of the same group are clustered together into a single
+ * GroupEntry. These groups are then transformed in order to remove children or completely split
+ * them apart. To participate, see {@link #addPromoter}.
+ * - Sorted: All top-level notifications are sorted. To participate, see
+ * {@link #setSectionsProvider} and {@link #setComparators}
+ *
+ * The exact order of all hooks is as follows:
+ * 0. Collection listeners are fired ({@link #addCollectionListener}).
+ * 1. Pre-group filters are fired on each notification ({@link #addPreGroupFilter}).
+ * 2. Initial grouping is performed (NotificationEntries will have their parents set
+ * appropriately).
+ * 3. OnBeforeTransformGroupListeners are fired ({@link #addOnBeforeTransformGroupsListener})
+ * 4. NotifPromoters are called on each notification with a parent ({@link #addPromoter})
+ * 5. OnBeforeSortListeners are fired ({@link #addOnBeforeSortListener})
+ * 6. SectionsProvider is called on each top-level entry in the list ({@link #setSectionsProvider})
+ * 7. Top-level entries within the same section are sorted by NotifComparators
+ * ({@link #setComparators})
+ * 8. Pre-render filters are fired on each notification ({@link #addPreRenderFilter})
+ * 9. OnBeforeRenderListListeners are fired ({@link #addOnBeforeRenderListListener})
+ * 9. The list is handed off to the view layer to be rendered
+ */
+@Singleton
+public class NotifPipeline {
+ private final NotifCollection mNotifCollection;
+ private final ShadeListBuilder mShadeListBuilder;
+
+ @Inject
+ public NotifPipeline(
+ NotifCollection notifCollection,
+ ShadeListBuilder shadeListBuilder) {
+ mNotifCollection = notifCollection;
+ mShadeListBuilder = shadeListBuilder;
+ }
+
+ /**
+ * Returns the list of "active" notifications, i.e. the notifications that are currently posted
+ * to the phone. In general, this tracks closely to the list maintained by NotificationManager,
+ * but it can diverge slightly due to lifetime extenders.
+ *
+ * The returned collection is read-only, unsorted, unfiltered, and ungrouped.
+ */
+ public Collection<NotificationEntry> getActiveNotifs() {
+ return mNotifCollection.getActiveNotifs();
+ }
+
+ /**
+ * Registers a listener to be informed when notifications are added, removed or updated.
+ */
+ public void addCollectionListener(NotifCollectionListener listener) {
+ mNotifCollection.addCollectionListener(listener);
+ }
+
+ /**
+ * Registers a lifetime extender. Lifetime extenders can cause notifications that have been
+ * dismissed or retracted to be temporarily retained in the collection.
+ */
+ public void addNotificationLifetimeExtender(NotifLifetimeExtender extender) {
+ mNotifCollection.addNotificationLifetimeExtender(extender);
+ }
+
+ /**
+ * Registers a filter with the pipeline before grouping, promoting and sorting occurs. Filters
+ * are called on each notification in the order that they were registered. If any filter
+ * returns true, the notification is removed from the pipeline (and no other filters are
+ * called on that notif).
+ */
+ public void addPreGroupFilter(NotifFilter filter) {
+ mShadeListBuilder.addPreGroupFilter(filter);
+ }
+
+ /**
+ * Called after notifications have been filtered and after the initial grouping has been
+ * performed but before NotifPromoters have had a chance to promote children out of groups.
+ */
+ public void addOnBeforeTransformGroupsListener(OnBeforeTransformGroupsListener listener) {
+ mShadeListBuilder.addOnBeforeTransformGroupsListener(listener);
+ }
+
+ /**
+ * Registers a promoter with the pipeline. Promoters are able to promote child notifications to
+ * top-level, i.e. move a notification that would be a child of a group and make it appear
+ * ungrouped. Promoters are called on each child notification in the order that they are
+ * registered. If any promoter returns true, the notification is removed from the group (and no
+ * other promoters are called on it).
+ */
+ public void addPromoter(NotifPromoter promoter) {
+ mShadeListBuilder.addPromoter(promoter);
+ }
+
+ /**
+ * Called after notifs have been filtered and groups have been determined but before sections
+ * have been determined or the notifs have been sorted.
+ */
+ public void addOnBeforeSortListener(OnBeforeSortListener listener) {
+ mShadeListBuilder.addOnBeforeSortListener(listener);
+ }
+
+ /**
+ * Assigns sections to each top-level entry, where a section is simply an integer. Sections are
+ * the primary metric by which top-level entries are sorted; NotifComparators are only consulted
+ * when two entries are in the same section. The pipeline doesn't assign any particular meaning
+ * to section IDs -- from it's perspective they're just numbers and it sorts them by a simple
+ * numerical comparison.
+ */
+ public void setSectionsProvider(SectionsProvider provider) {
+ mShadeListBuilder.setSectionsProvider(provider);
+ }
+
+ /**
+ * Comparators that are used to sort top-level entries that share the same section. The
+ * comparators are executed in order until one of them returns a non-zero result. If all return
+ * zero, the pipeline falls back to sorting by rank (and, failing that, Notification.when).
+ */
+ public void setComparators(List<NotifComparator> comparators) {
+ mShadeListBuilder.setComparators(comparators);
+ }
+
+ /**
+ * Registers a filter with the pipeline to filter right before rendering the list (after
+ * pre-group filtering, grouping, promoting and sorting occurs). Filters are
+ * called on each notification in the order that they were registered. If any filter returns
+ * true, the notification is removed from the pipeline (and no other filters are called on that
+ * notif).
+ */
+ public void addPreRenderFilter(NotifFilter filter) {
+ mShadeListBuilder.addPreRenderFilter(filter);
+ }
+
+ /**
+ * Called at the end of the pipeline after the notif list has been finalized but before it has
+ * been handed off to the view layer.
+ */
+ public void addOnBeforeRenderListListener(OnBeforeRenderListListener listener) {
+ mShadeListBuilder.addOnBeforeRenderListListener(listener);
+ }
+
+ /**
+ * Returns a read-only view in to the current shade list, i.e. the list of notifications that
+ * are currently present in the shade. If this method is called during pipeline execution it
+ * will return the current state of the list, which will likely be only partially-generated.
+ */
+ public List<ListEntry> getShadeList() {
+ return mShadeListBuilder.getShadeList();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 7301fe1..2fcfb8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -59,6 +59,7 @@
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 19d90f0..76c524b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -32,7 +32,6 @@
import android.annotation.Nullable;
import android.util.ArrayMap;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
@@ -41,6 +40,7 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.statusbar.notification.logging.NotifEvent;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.util.Assert;
@@ -57,11 +57,13 @@
import javax.inject.Singleton;
/**
- * The implementation of {@link NotifListBuilder}.
+ * The second half of {@link NotifPipeline}. Sits downstream of the NotifCollection and transforms
+ * its "notification set" into the "shade list", the filtered, grouped, and sorted list of
+ * notifications that are currently present in the notification shade.
*/
@MainThread
@Singleton
-public class NotifListBuilderImpl implements NotifListBuilder {
+public class ShadeListBuilder {
private final SystemClock mSystemClock;
private final NotifLog mNotifLog;
@@ -90,7 +92,7 @@
private final List<ListEntry> mReadOnlyNotifList = Collections.unmodifiableList(mNotifList);
@Inject
- public NotifListBuilderImpl(SystemClock systemClock, NotifLog notifLog) {
+ public ShadeListBuilder(SystemClock systemClock, NotifLog notifLog) {
Assert.isMainThread();
mSystemClock = systemClock;
mNotifLog = notifLog;
@@ -116,32 +118,28 @@
mOnRenderListListener = onRenderListListener;
}
- @Override
- public void addOnBeforeTransformGroupsListener(OnBeforeTransformGroupsListener listener) {
+ void addOnBeforeTransformGroupsListener(OnBeforeTransformGroupsListener listener) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
mOnBeforeTransformGroupsListeners.add(listener);
}
- @Override
- public void addOnBeforeSortListener(OnBeforeSortListener listener) {
+ void addOnBeforeSortListener(OnBeforeSortListener listener) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
mOnBeforeSortListeners.add(listener);
}
- @Override
- public void addOnBeforeRenderListListener(OnBeforeRenderListListener listener) {
+ void addOnBeforeRenderListListener(OnBeforeRenderListListener listener) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
mOnBeforeRenderListListeners.add(listener);
}
- @Override
- public void addPreGroupFilter(NotifFilter filter) {
+ void addPreGroupFilter(NotifFilter filter) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
@@ -149,8 +147,7 @@
filter.setInvalidationListener(this::onPreGroupFilterInvalidated);
}
- @Override
- public void addPreRenderFilter(NotifFilter filter) {
+ void addPreRenderFilter(NotifFilter filter) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
@@ -158,8 +155,7 @@
filter.setInvalidationListener(this::onPreRenderFilterInvalidated);
}
- @Override
- public void addPromoter(NotifPromoter promoter) {
+ void addPromoter(NotifPromoter promoter) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
@@ -167,8 +163,7 @@
promoter.setInvalidationListener(this::onPromoterInvalidated);
}
- @Override
- public void setSectionsProvider(SectionsProvider provider) {
+ void setSectionsProvider(SectionsProvider provider) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
@@ -176,8 +171,7 @@
provider.setInvalidationListener(this::onSectionsProviderInvalidated);
}
- @Override
- public void setComparators(List<NotifComparator> comparators) {
+ void setComparators(List<NotifComparator> comparators) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
@@ -188,8 +182,7 @@
}
}
- @Override
- public List<ListEntry> getActiveNotifs() {
+ List<ListEntry> getShadeList() {
Assert.isMainThread();
return mReadOnlyNotifList;
}
@@ -275,7 +268,7 @@
}
/**
- * The core algorithm of the pipeline. See the top comment in {@link NotifListBuilder} for
+ * The core algorithm of the pipeline. See the top comment in {@link NotifPipeline} for
* details on our contracts with other code.
*
* Once the build starts we are very careful to protect against reentrant code. Anything that
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CoalescedEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/CoalescedEvent.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CoalescedEvent.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/CoalescedEvent.kt
index b6218b4..143de8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CoalescedEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/CoalescedEvent.kt
@@ -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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection.notifcollection
+package com.android.systemui.statusbar.notification.collection.coalescer
import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.StatusBarNotification
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/EventBatch.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/EventBatch.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
index ac51178..2c6a165 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/EventBatch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection.notifcollection;
+package com.android.systemui.statusbar.notification.collection.coalescer;
import java.util.ArrayList;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index c3e3c53..8076616 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection.notifcollection;
+package com.android.systemui.statusbar.notification.collection.coalescer;
import static com.android.systemui.statusbar.notification.logging.NotifEvent.COALESCED_EVENT;
import static com.android.systemui.statusbar.notification.logging.NotifEvent.EARLY_BATCH_EMIT;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
index 898918e..c1a11b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
@@ -16,27 +16,16 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
-import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
/**
- * Interface for registering callbacks to the {@link NewNotifPipeline}.
- *
- * This includes registering:
- * {@link Pluggable}s to the {@link NotifListBuilder}
- * {@link NotifCollectionListener}s and {@link NotifLifetimeExtender}s to {@link NotifCollection}
+ * Interface for registering callbacks to the {@link NotifPipeline}.
*/
public interface Coordinator {
-
/**
* Called after the NewNotifPipeline is initialized.
- * Coordinators should register their {@link Pluggable}s to the notifListBuilder
- * and their {@link NotifCollectionListener}s and {@link NotifLifetimeExtender}s
- * to the notifCollection in this method.
+ * Coordinators should register their listeners and {@link Pluggable}s to the pipeline.
*/
- void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder);
+ void attach(NotifPipeline pipeline);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
index 5e7dd98..625d1b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
@@ -23,9 +23,8 @@
import android.os.RemoteException;
import android.service.notification.StatusBarNotification;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -52,10 +51,10 @@
}
@Override
- public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+ public void attach(NotifPipeline pipeline) {
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
- notifListBuilder.addPreGroupFilter(mNotifFilter);
+ pipeline.addPreGroupFilter(mNotifFilter);
}
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
index 62342b1..da119c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
@@ -25,12 +25,11 @@
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import java.util.HashMap;
import java.util.Map;
@@ -57,7 +56,7 @@
private final AppOpsController mAppOpsController;
private final Handler mMainHandler;
- private NotifCollection mNotifCollection;
+ private NotifPipeline mNotifPipeline;
@Inject
public ForegroundCoordinator(
@@ -70,20 +69,20 @@
}
@Override
- public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
- mNotifCollection = notifCollection;
+ public void attach(NotifPipeline pipeline) {
+ mNotifPipeline = pipeline;
// extend the lifetime of foreground notification services to show for at least 5 seconds
- mNotifCollection.addNotificationLifetimeExtender(mForegroundLifetimeExtender);
+ mNotifPipeline.addNotificationLifetimeExtender(mForegroundLifetimeExtender);
// listen for new notifications to add appOps
- mNotifCollection.addCollectionListener(mNotifCollectionListener);
+ mNotifPipeline.addCollectionListener(mNotifCollectionListener);
// when appOps change, update any relevant notifications to update appOps for
mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
// filter out foreground service notifications that aren't necessary anymore
- notifListBuilder.addPreGroupFilter(mNotifFilter);
+ mNotifPipeline.addPreGroupFilter(mNotifFilter);
}
/**
@@ -230,7 +229,7 @@
}
private NotificationEntry findNotificationEntryWithKey(String key) {
- for (NotificationEntry entry : mNotifCollection.getNotifs()) {
+ for (NotificationEntry entry : mNotifPipeline.getActiveNotifs()) {
if (entry.getKey().equals(key)) {
return entry;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index db107f5..a26ee545 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -39,9 +39,8 @@
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -86,9 +85,9 @@
}
@Override
- public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+ public void attach(NotifPipeline pipeline) {
setupInvalidateNotifListCallbacks();
- notifListBuilder.addPreRenderFilter(mNotifFilter);
+ pipeline.addPreRenderFilter(mNotifFilter);
}
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 2436bb9..562a618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -18,11 +18,10 @@
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -33,8 +32,8 @@
import javax.inject.Singleton;
/**
- * Handles the attachment of the {@link NotifListBuilder} and {@link NotifCollection} to the
- * {@link Coordinator}s, so that the Coordinators can register their respective callbacks.
+ * Handles the attachment of {@link Coordinator}s to the {@link NotifPipeline} so that the
+ * Coordinators can register their respective callbacks.
*/
@Singleton
public class NotifCoordinators implements Dumpable {
@@ -63,14 +62,12 @@
}
/**
- * Sends the initialized notifListBuilder and notifCollection to each
- * coordinator to indicate the notifListBuilder is ready to accept {@link Pluggable}s
- * and the notifCollection is ready to accept {@link NotifCollectionListener}s and
- * {@link NotifLifetimeExtender}s.
+ * Sends the pipeline to each coordinator when the pipeline is ready to accept
+ * {@link Pluggable}s, {@link NotifCollectionListener}s and {@link NotifLifetimeExtender}s.
*/
- public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+ public void attach(NotifPipeline pipeline) {
for (Coordinator c : mCoordinators) {
- c.attach(notifCollection, notifListBuilder);
+ c.attach(pipeline);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index a14f0e1..20c9cbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -16,13 +16,12 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.NotifInflater;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.logging.NotifEvent;
import com.android.systemui.statusbar.notification.logging.NotifLog;
@@ -55,10 +54,10 @@
}
@Override
- public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
- notifCollection.addCollectionListener(mNotifCollectionListener);
- notifListBuilder.addPreRenderFilter(mNotifInflationErrorFilter);
- notifListBuilder.addPreRenderFilter(mNotifInflatingFilter);
+ public void attach(NotifPipeline pipeline) {
+ pipeline.addCollectionListener(mNotifCollectionListener);
+ pipeline.addPreRenderFilter(mNotifInflationErrorFilter);
+ pipeline.addPreRenderFilter(mNotifInflatingFilter);
}
private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 0751aa8..7e9e760 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -17,9 +17,8 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import javax.inject.Inject;
@@ -43,10 +42,10 @@
}
@Override
- public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+ public void attach(NotifPipeline pipeline) {
mStatusBarStateController.addCallback(mStatusBarStateCallback);
- notifListBuilder.addPreGroupFilter(mNotifFilter);
+ pipeline.addPreGroupFilter(mNotifFilter);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java
index fc04827..ea0ece4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection;
+package com.android.systemui.statusbar.notification.collection.inflation;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.PreparationCoordinator;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
index 7504e86..3f500644 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.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.
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection;
+package com.android.systemui.statusbar.notification.collection.inflation;
import android.annotation.Nullable;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* Used by the {@link NotificationEntryManager}. When notifications are added or updated, the binder
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 80b5b8a..1ab20a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection;
+package com.android.systemui.statusbar.notification.collection.inflation;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java
index 986ee17..15f312d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java
@@ -19,8 +19,8 @@
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -36,7 +36,7 @@
private List<ListEntry> mEntries = Collections.emptyList();
/** Attach the consumer to the pipeline. */
- public void attach(NotifListBuilderImpl listBuilder) {
+ public void attach(ShadeListBuilder listBuilder) {
listBuilder.setOnRenderListListener(this::onBuildComplete);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
similarity index 78%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index 8d3d0ff..959b002 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -24,10 +24,11 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
-import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
+import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
-import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -39,10 +40,11 @@
* Initialization code for the new notification pipeline.
*/
@Singleton
-public class NewNotifPipeline implements Dumpable {
+public class NotifPipelineInitializer implements Dumpable {
+ private final NotifPipeline mPipelineWrapper;
private final GroupCoalescer mGroupCoalescer;
private final NotifCollection mNotifCollection;
- private final NotifListBuilderImpl mNotifPipeline;
+ private final ShadeListBuilder mListBuilder;
private final NotifCoordinators mNotifPluggableCoordinators;
private final NotifInflaterImpl mNotifInflater;
private final DumpController mDumpController;
@@ -51,17 +53,19 @@
private final FakePipelineConsumer mFakePipelineConsumer = new FakePipelineConsumer();
@Inject
- public NewNotifPipeline(
+ public NotifPipelineInitializer(
+ NotifPipeline pipelineWrapper,
GroupCoalescer groupCoalescer,
NotifCollection notifCollection,
- NotifListBuilderImpl notifPipeline,
+ ShadeListBuilder listBuilder,
NotifCoordinators notifCoordinators,
NotifInflaterImpl notifInflater,
DumpController dumpController,
FeatureFlags featureFlags) {
+ mPipelineWrapper = pipelineWrapper;
mGroupCoalescer = groupCoalescer;
mNotifCollection = notifCollection;
- mNotifPipeline = notifPipeline;
+ mListBuilder = listBuilder;
mNotifPluggableCoordinators = notifCoordinators;
mDumpController = dumpController;
mNotifInflater = notifInflater;
@@ -81,11 +85,11 @@
}
// Wire up coordinators
- mFakePipelineConsumer.attach(mNotifPipeline);
- mNotifPluggableCoordinators.attach(mNotifCollection, mNotifPipeline);
+ mNotifPluggableCoordinators.attach(mPipelineWrapper);
// Wire up pipeline
- mNotifPipeline.attach(mNotifCollection);
+ mFakePipelineConsumer.attach(mListBuilder);
+ mListBuilder.attach(mNotifCollection);
mNotifCollection.attach(mGroupCoalescer);
mGroupCoalescer.attach(notificationService);
@@ -99,5 +103,5 @@
mGroupCoalescer.dump(fd, pw, args);
}
- private static final String TAG = "NewNotifPipeline";
+ private static final String TAG = "NotifPipeline";
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifListBuilder.java
deleted file mode 100644
index 7580924..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifListBuilder.java
+++ /dev/null
@@ -1,127 +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 com.android.systemui.statusbar.notification.collection.listbuilder;
-
-import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider;
-
-import java.util.List;
-
-/**
- * The system that constructs the current "notification list", the list of notifications that are
- * currently being displayed to the user.
- *
- * The pipeline proceeds through a series of stages in order to produce the final list (see below).
- * Each stage exposes hooks and listeners for other code to participate.
- *
- * This list differs from the canonical one we receive from system server in a few ways:
- * - Filtered: Some notifications are filtered out. For example, we filter out notifications whose
- * views haven't been inflated yet. We also filter out some notifications if we're on the lock
- * screen. To participate, see {@link #addFilter(NotifFilter)}.
- * - Grouped: Notifications that are part of the same group are clustered together into a single
- * GroupEntry. These groups are then transformed in order to remove children or completely split
- * them apart. To participate, see {@link #addPromoter(NotifPromoter)}.
- * - Sorted: All top-level notifications are sorted. To participate, see
- * {@link #setSectionsProvider(SectionsProvider)} and {@link #setComparators(List)}
- *
- * The exact order of all hooks is as follows:
- * 0. Collection listeners are fired (see {@link NotifCollection}).
- * 1. NotifFilters are called on each notification currently in NotifCollection.
- * 2. Initial grouping is performed (NotificationEntries will have their parents set
- * appropriately).
- * 3. OnBeforeTransformGroupListeners are fired
- * 4. NotifPromoters are called on each notification with a parent
- * 5. OnBeforeSortListeners are fired
- * 6. SectionsProvider is called on each top-level entry in the list
- * 7. The top-level entries are sorted using the provided NotifComparators (plus some additional
- * built-in logic).
- * 8. OnBeforeRenderListListeners are fired
- * 9. The list is handed off to the view layer to be rendered.
- */
-public interface NotifListBuilder {
-
- /**
- * Registers a filter with the pipeline before grouping, promoting and sorting occurs. Filters
- * are called on each notification in the order that they were registered. If any filter
- * returns true, the notification is removed from the pipeline (and no other filters are
- * called on that notif).
- */
- void addPreGroupFilter(NotifFilter filter);
-
- /**
- * Registers a promoter with the pipeline. Promoters are able to promote child notifications to
- * top-level, i.e. move a notification that would be a child of a group and make it appear
- * ungrouped. Promoters are called on each child notification in the order that they are
- * registered. If any promoter returns true, the notification is removed from the group (and no
- * other promoters are called on it).
- */
- void addPromoter(NotifPromoter promoter);
-
- /**
- * Assigns sections to each top-level entry, where a section is simply an integer. Sections are
- * the primary metric by which top-level entries are sorted; NotifComparators are only consulted
- * when two entries are in the same section. The pipeline doesn't assign any particular meaning
- * to section IDs -- from it's perspective they're just numbers and it sorts them by a simple
- * numerical comparison.
- */
- void setSectionsProvider(SectionsProvider provider);
-
- /**
- * Comparators that are used to sort top-level entries that share the same section. The
- * comparators are executed in order until one of them returns a non-zero result. If all return
- * zero, the pipeline falls back to sorting by rank (and, failing that, Notification.when).
- */
- void setComparators(List<NotifComparator> comparators);
-
- /**
- * Registers a filter with the pipeline to filter right before rendering the list (after
- * pre-group filtering, grouping, promoting and sorting occurs). Filters are
- * called on each notification in the order that they were registered. If any filter returns
- * true, the notification is removed from the pipeline (and no other filters are called on that
- * notif).
- */
- void addPreRenderFilter(NotifFilter filter);
-
- /**
- * Called after notifications have been filtered and after the initial grouping has been
- * performed but before NotifPromoters have had a chance to promote children out of groups.
- */
- void addOnBeforeTransformGroupsListener(OnBeforeTransformGroupsListener listener);
-
- /**
- * Called after notifs have been filtered and groups have been determined but before sections
- * have been determined or the notifs have been sorted.
- */
- void addOnBeforeSortListener(OnBeforeSortListener listener);
-
- /**
- * Called at the end of the pipeline after the notif list has been finalized but before it has
- * been handed off to the view layer.
- */
- void addOnBeforeRenderListListener(OnBeforeRenderListListener listener);
-
- /**
- * Returns a read-only view in to the current notification list. If this method is called
- * during pipeline execution it will return the current state of the list, which will likely
- * be only partially-generated.
- */
- List<ListEntry> getActiveNotifs();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java
index f6ca12d..44a27a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java
@@ -17,10 +17,11 @@
package com.android.systemui.statusbar.notification.collection.listbuilder;
import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.List;
-/** See {@link NotifListBuilder#addOnBeforeRenderListListener(OnBeforeRenderListListener)} */
+/** See {@link NotifPipeline#addOnBeforeRenderListListener(OnBeforeRenderListListener)} */
public interface OnBeforeRenderListListener {
/**
* Called at the end of the pipeline after the notif list has been finalized but before it has
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java
index 7be7ac0..56cfe5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java
@@ -17,10 +17,11 @@
package com.android.systemui.statusbar.notification.collection.listbuilder;
import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.List;
-/** See {@link NotifListBuilder#addOnBeforeSortListener(OnBeforeSortListener)} */
+/** See {@link NotifPipeline#addOnBeforeSortListener(OnBeforeSortListener)} */
public interface OnBeforeSortListener {
/**
* Called after the notif list has been filtered and grouped but before sections have been
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java
index d7a0815..0dc4df0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java
@@ -17,13 +17,14 @@
package com.android.systemui.statusbar.notification.collection.listbuilder;
import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import java.util.List;
/**
* See
- * {@link NotifListBuilder#addOnBeforeTransformGroupsListener(OnBeforeTransformGroupsListener)}
+ * {@link NotifPipeline#addOnBeforeTransformGroupsListener(OnBeforeTransformGroupsListener)}
*/
public interface OnBeforeTransformGroupsListener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
index 084d038..1897ba2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
@@ -18,13 +18,13 @@
import android.annotation.IntDef;
-import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- * Used by {@link NotifListBuilderImpl} to track its internal state machine.
+ * Used by {@link ShadeListBuilder} to track its internal state machine.
*/
public class PipelineState {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
index a191c83..0d150ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
@@ -17,13 +17,13 @@
package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.Comparator;
import java.util.List;
/**
- * Pluggable for participating in notif sorting. See {@link NotifListBuilder#setComparators(List)}.
+ * Pluggable for participating in notif sorting. See {@link NotifPipeline#setComparators(List)}.
*/
public abstract class NotifComparator
extends Pluggable<NotifComparator>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
index e6189ed..8f575cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java
@@ -16,12 +16,12 @@
package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
/**
* Pluggable for participating in notif filtering.
- * See {@link NotifListBuilder#addPreGroupFilter} and {@link NotifListBuilder#addPreRenderFilter}.
+ * See {@link NotifPipeline#addPreGroupFilter} and {@link NotifPipeline#addPreRenderFilter}.
*/
public abstract class NotifFilter extends Pluggable<NotifFilter> {
protected NotifFilter(String name) {
@@ -35,9 +35,9 @@
* however. If another filter returns true before yours, we'll skip straight to the next notif.
*
* @param entry The entry in question.
- * If this filter is registered via {@link NotifListBuilder#addPreGroupFilter},
+ * If this filter is registered via {@link NotifPipeline#addPreGroupFilter},
* this entry will not have any grouping nor sorting information.
- * If this filter is registered via {@link NotifListBuilder#addPreRenderFilter},
+ * If this filter is registered via {@link NotifPipeline#addPreRenderFilter},
* this entry will have grouping and sorting information.
* @param now A timestamp in SystemClock.uptimeMillis that represents "now" for the purposes of
* pipeline execution. This value will be the same for all pluggable calls made
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifPromoter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifPromoter.java
index 84e16f4..5fce446 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifPromoter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifPromoter.java
@@ -16,13 +16,13 @@
package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
/**
* Pluggable for participating in notif promotion. Notif promoters can upgrade notifications
* from being children of a group to top-level notifications. See
- * {@link NotifListBuilder#addPromoter(NotifPromoter)}.
+ * {@link NotifPipeline#addPromoter}.
*/
public abstract class NotifPromoter extends Pluggable<NotifPromoter> {
protected NotifPromoter(String name) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
index f9ce197..4270408 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
@@ -18,10 +18,10 @@
import android.annotation.Nullable;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
/**
- * Generic superclass for chunks of code that can plug into the {@link NotifListBuilder}.
+ * Generic superclass for chunks of code that can plug into the {@link NotifPipeline}.
*
* A pluggable is fundamentally three things:
* 1. A name (for debugging purposes)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/CollectionReadyForBuildListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.java
similarity index 82%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/CollectionReadyForBuildListener.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.java
index 87aaea0..4023474 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/CollectionReadyForBuildListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.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.
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection;
+package com.android.systemui.statusbar.notification.collection.notifcollection;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.util.Collection;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/DismissedByUserStats.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/DismissedByUserStats.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/DismissedByUserStats.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/DismissedByUserStats.java
index ecce6ea..b268686 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/DismissedByUserStats.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/DismissedByUserStats.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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection;
+package com.android.systemui.statusbar.notification.collection.notifcollection;
import android.service.notification.NotificationStats.DismissalSentiment;
import android.service.notification.NotificationStats.DismissalSurface;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
similarity index 71%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionListener.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
index 032620e..9cbc7d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.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.
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection;
+package com.android.systemui.statusbar.notification.collection.notifcollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* Listener interface for {@link NotifCollection}.
@@ -36,7 +38,9 @@
}
/**
- * Called immediately after a notification has been removed from the collection.
+ * Called whenever a notification is retracted by system server. This method is not called
+ * immediately after a user dismisses a notification: we wait until we receive confirmation from
+ * system server before considering the notification removed.
*/
default void onEntryRemoved(
NotificationEntry entry,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLifetimeExtender.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java
index 2c7b138..05f5ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.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.
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection;
+package com.android.systemui.statusbar.notification.collection.notifcollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* A way for other code to temporarily extend the lifetime of a notification after it has been
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
index c6c36ee..02acc81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
@@ -22,8 +22,8 @@
import com.android.systemui.log.RichEvent;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
-import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer;
+import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
+import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -67,7 +67,7 @@
}
/**
- * @return if this event occurred in {@link NotifListBuilder}
+ * @return if this event occurred in {@link ShadeListBuilder}
*/
static boolean isListBuilderEvent(@EventType int type) {
return isBetweenInclusive(type, 0, TOTAL_LIST_BUILDER_EVENT_TYPES);
@@ -161,7 +161,7 @@
private static final int TOTAL_EVENT_LABELS = EVENT_LABELS.length;
/**
- * Events related to {@link NotifListBuilder}
+ * Events related to {@link ShadeListBuilder}
*/
public static final int WARN = 0;
public static final int ON_BUILD_LIST = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 78eaf3e..452d1eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -17,7 +17,9 @@
package com.android.systemui.statusbar.notification.people
import android.app.Notification
+import android.content.Context
import android.service.notification.StatusBarNotification
+import android.util.FeatureFlagUtils
import javax.inject.Inject
import javax.inject.Singleton
@@ -27,10 +29,16 @@
@Singleton
class PeopleNotificationIdentifierImpl @Inject constructor(
- private val personExtractor: NotificationPersonExtractor
+ private val personExtractor: NotificationPersonExtractor,
+ private val context: Context
) : PeopleNotificationIdentifier {
override fun isPeopleNotification(sbn: StatusBarNotification) =
- sbn.notification.notificationStyle == Notification.MessagingStyle::class.java ||
+ (sbn.notification.notificationStyle == Notification.MessagingStyle::class.java &&
+ (sbn.notification.shortcutId != null ||
+ FeatureFlagUtils.isEnabled(
+ context,
+ FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ
+ ))) ||
personExtractor.isPersonNotification(sbn)
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 189d3b6..ba70cf4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -199,8 +199,8 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -364,7 +364,7 @@
private final HeadsUpManagerPhone mHeadsUpManager;
private final DynamicPrivacyController mDynamicPrivacyController;
private final BypassHeadsUpNotifier mBypassHeadsUpNotifier;
- private final Lazy<NewNotifPipeline> mNewNotifPipeline;
+ private final Lazy<NotifPipelineInitializer> mNewNotifPipeline;
private final FalsingManager mFalsingManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final ConfigurationController mConfigurationController;
@@ -625,7 +625,7 @@
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- Lazy<NewNotifPipeline> newNotifPipeline,
+ Lazy<NotifPipelineInitializer> newNotifPipeline,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index be7f0a0..b4d5dad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -65,8 +65,8 @@
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
@@ -117,7 +117,7 @@
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- Lazy<NewNotifPipeline> newNotifPipeline,
+ Lazy<NotifPipelineInitializer> newNotifPipeline,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 12a6516..720f229 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -66,7 +66,7 @@
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 5916180..f05b585 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -15,6 +15,9 @@
*/
package com.android.systemui.statusbar.policy;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
@@ -704,7 +707,11 @@
}
mServiceState = state;
if (mServiceState != null) {
- updateDataNetType(mServiceState.getDataNetworkType());
+ NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo(
+ DOMAIN_PS, TRANSPORT_TYPE_WWAN);
+ if (regInfo != null) {
+ updateDataNetType(regInfo.getAccessNetworkTechnology());
+ }
}
updateTelephony();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 9c9a627..8d11b54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -48,7 +48,7 @@
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -71,7 +71,7 @@
@Mock private NotificationEntryManager mEntryManager;
@Mock private AppOpsController mAppOpsController;
@Mock private Handler mMainHandler;
- @Mock private NotifCollection mNotifCollection;
+ @Mock private NotifPipeline mNotifPipeline;
@Before
public void setUp() throws Exception {
@@ -81,7 +81,7 @@
MockitoAnnotations.initMocks(this);
mFsc = new ForegroundServiceController(mEntryManager, mAppOpsController, mMainHandler);
mListener = new ForegroundServiceNotificationListener(
- mContext, mFsc, mEntryManager, mNotifCollection);
+ mContext, mFsc, mEntryManager, mNotifPipeline);
ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
ArgumentCaptor.forClass(NotificationEntryListener.class);
verify(mEntryManager).addNotificationEntryListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index af0ef82..e5ae1aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -25,11 +25,11 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+
+import static java.lang.Thread.sleep;
import android.app.AppOpsManager;
import android.content.pm.PackageManager;
@@ -57,7 +57,6 @@
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = UserHandle.getUid(0, 0);
private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
- private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
@Mock
private AppOpsManager mAppOpsManager;
@@ -68,8 +67,6 @@
@Mock
private AppOpsControllerImpl.H mMockHandler;
@Mock
- private PermissionFlagsCache mFlagsCache;
- @Mock
private DumpController mDumpController;
private AppOpsControllerImpl mController;
@@ -85,17 +82,9 @@
// All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of
// TEST_UID_NON_USER_SENSITIVE are user sensitive.
getContext().setMockPackageManager(mPackageManager);
- when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
- eq(UserHandle.getUserHandleForUid(TEST_UID)))).thenReturn(
- PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
- when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
- eq(UserHandle.getUserHandleForUid(TEST_UID_OTHER)))).thenReturn(
- PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
- when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
- eq(UserHandle.getUserHandleForUid(TEST_UID_NON_USER_SENSITIVE)))).thenReturn(0);
- mController = new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mFlagsCache,
- mDumpController);
+ mController =
+ new AppOpsControllerImpl(mContext, mTestableLooper.getLooper(), mDumpController);
}
@Test
@@ -191,14 +180,6 @@
}
@Test
- public void nonUserSensitiveOpsAreIgnored() {
- mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
- TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
- assertEquals(0, mController.getActiveAppOpsForUser(
- UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
- }
-
- @Test
public void opNotedScheduledForRemoval() {
mController.setBGHandler(mMockHandler);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
@@ -251,4 +232,100 @@
// Only one post to notify subscribers
verify(mMockHandler, times(2)).scheduleRemoval(any(), anyLong());
}
+
+ @Test
+ public void testActiveOpNotRemovedAfterNoted() throws InterruptedException {
+ // Replaces the timeout delay with 5 ms
+ AppOpsControllerImpl.H testHandler = mController.new H(mTestableLooper.getLooper()) {
+ @Override
+ public void scheduleRemoval(AppOpItem item, long timeToRemoval) {
+ super.scheduleRemoval(item, 5L);
+ }
+ };
+
+ mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+ mController.setBGHandler(testHandler);
+
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+ mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+ AppOpsManager.MODE_ALLOWED);
+
+ mTestableLooper.processAllMessages();
+ List<AppOpItem> list = mController.getActiveAppOps();
+ verify(mCallback).onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+ // Duplicates are not removed between active and noted
+ assertEquals(2, list.size());
+
+ sleep(10L);
+
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback, never()).onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false);
+ list = mController.getActiveAppOps();
+ assertEquals(1, list.size());
+ }
+
+ @Test
+ public void testNotedNotRemovedAfterActive() {
+ mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+
+ mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+ AppOpsManager.MODE_ALLOWED);
+
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+ mTestableLooper.processAllMessages();
+ List<AppOpItem> list = mController.getActiveAppOps();
+ verify(mCallback).onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+ // Duplicates are not removed between active and noted
+ assertEquals(2, list.size());
+
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false);
+
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback, never()).onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false);
+ list = mController.getActiveAppOps();
+ assertEquals(1, list.size());
+ }
+
+ @Test
+ public void testNotedAndActiveOnlyOneCall() {
+ mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+
+ mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+ AppOpsManager.MODE_ALLOWED);
+
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+ mTestableLooper.processAllMessages();
+ verify(mCallback).onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+ }
+
+ @Test
+ public void testActiveAndNotedOnlyOneCall() {
+ mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+ mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+ AppOpsManager.MODE_ALLOWED);
+
+ mTestableLooper.processAllMessages();
+ verify(mCallback).onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
deleted file mode 100644
index dc070de..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
+++ /dev/null
@@ -1,88 +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 com.android.systemui.appops
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.os.UserHandle
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class PermissionFlagsCacheTest : SysuiTestCase() {
-
- companion object {
- const val TEST_PERMISSION = "test_permission"
- const val TEST_PACKAGE = "test_package"
- }
-
- @Mock
- private lateinit var mPackageManager: PackageManager
- @Mock
- private lateinit var mUserHandle: UserHandle
- private lateinit var flagsCache: TestPermissionFlagsCache
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- mContext.setMockPackageManager(mPackageManager)
- flagsCache = TestPermissionFlagsCache(mContext)
- }
-
- @Test
- fun testCallsPackageManager_exactlyOnce() {
- flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
- flagsCache.time = CACHE_EXPIRATION - 1
- verify(mPackageManager).getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
- }
-
- @Test
- fun testCallsPackageManager_cacheExpired() {
- flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
- flagsCache.time = CACHE_EXPIRATION + 1
- flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
- verify(mPackageManager, times(2))
- .getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
- }
-
- @Test
- fun testCallsPackageMaanger_multipleKeys() {
- flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, mUserHandle)
- flagsCache.getPermissionFlags(TEST_PERMISSION, "", mUserHandle)
- verify(mPackageManager, times(2))
- .getPermissionFlags(anyString(), anyString(), any())
- }
-
- private class TestPermissionFlagsCache(context: Context) : PermissionFlagsCache(context) {
- var time = 0L
-
- override fun getCurrentTime(): Long {
- return time
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 1e0179d..cc5514f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -78,7 +78,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
index bf84f2b..29ce920 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
@@ -21,7 +21,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinder
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder
import com.android.systemui.statusbar.notification.logging.NotifLog
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 28feaca..09cc5ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -52,9 +52,13 @@
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CoalescedEvent;
-import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer;
-import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer.BatchableNotificationHandler;
+import com.android.systemui.statusbar.notification.collection.coalescer.CoalescedEvent;
+import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
+import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer.BatchableNotificationHandler;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.Assert;
import org.junit.Before;
@@ -377,7 +381,7 @@
verify(mExtender3).shouldExtendLifetime(entry2, REASON_UNKNOWN);
// THEN the entry is not removed
- assertTrue(mCollection.getNotifs().contains(entry2));
+ assertTrue(mCollection.getActiveNotifs().contains(entry2));
// THEN the entry properly records all extenders that returned true
assertEquals(Arrays.asList(mExtender1, mExtender2), entry2.mLifetimeExtenders);
@@ -398,7 +402,7 @@
// GIVEN a notification gets lifetime-extended by one of them
mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
- assertTrue(mCollection.getNotifs().contains(entry2));
+ assertTrue(mCollection.getActiveNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN the last active extender expires (but new ones become active)
@@ -413,7 +417,7 @@
verify(mExtender3).shouldExtendLifetime(entry2, REASON_UNKNOWN);
// THEN the entry is not removed
- assertTrue(mCollection.getNotifs().contains(entry2));
+ assertTrue(mCollection.getActiveNotifs().contains(entry2));
// THEN the entry properly records all extenders that returned true
assertEquals(Arrays.asList(mExtender1, mExtender3), entry2.mLifetimeExtenders);
@@ -435,7 +439,7 @@
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
- assertTrue(mCollection.getNotifs().contains(entry2));
+ assertTrue(mCollection.getActiveNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN one (but not all) of the extenders expires
@@ -443,7 +447,7 @@
mExtender2.callback.onEndLifetimeExtension(mExtender2, entry2);
// THEN the entry is not removed
- assertTrue(mCollection.getNotifs().contains(entry2));
+ assertTrue(mCollection.getActiveNotifs().contains(entry2));
// THEN we don't re-query the extenders
verify(mExtender1, never()).shouldExtendLifetime(eq(entry2), anyInt());
@@ -470,7 +474,7 @@
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getNotifs().contains(entry2));
+ assertTrue(mCollection.getActiveNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN all of the active extenders expire
@@ -480,7 +484,7 @@
mExtender1.callback.onEndLifetimeExtension(mExtender1, entry2);
// THEN the entry removed
- assertFalse(mCollection.getNotifs().contains(entry2));
+ assertFalse(mCollection.getActiveNotifs().contains(entry2));
verify(mCollectionListener).onEntryRemoved(entry2, REASON_UNKNOWN, false);
}
@@ -500,7 +504,7 @@
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getNotifs().contains(entry2));
+ assertTrue(mCollection.getActiveNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN the notification is reposted
@@ -511,7 +515,7 @@
verify(mExtender2).cancelLifetimeExtension(entry2);
// THEN the notification is still present
- assertTrue(mCollection.getNotifs().contains(entry2));
+ assertTrue(mCollection.getActiveNotifs().contains(entry2));
}
@Test(expected = IllegalStateException.class)
@@ -530,7 +534,7 @@
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getNotifs().contains(entry2));
+ assertTrue(mCollection.getActiveNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN a lifetime extender makes a reentrant call during cancelLifetimeExtension()
@@ -559,7 +563,7 @@
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getNotifs().contains(entry2));
+ assertTrue(mCollection.getActiveNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN the notification is reposted
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 3e4068b..be06748 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -38,7 +38,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl.OnRenderListListener;
+import com.android.systemui.statusbar.notification.collection.ShadeListBuilder.OnRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
@@ -46,6 +46,7 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.util.Assert;
import com.android.systemui.util.time.FakeSystemClock;
@@ -72,9 +73,9 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class NotifListBuilderImplTest extends SysuiTestCase {
+public class ShadeListBuilderTest extends SysuiTestCase {
- private NotifListBuilderImpl mListBuilder;
+ private ShadeListBuilder mListBuilder;
private FakeSystemClock mSystemClock = new FakeSystemClock();
@Mock private NotifLog mNotifLog;
@@ -99,7 +100,7 @@
MockitoAnnotations.initMocks(this);
Assert.sMainLooper = TestableLooper.get(this).getLooper();
- mListBuilder = new NotifListBuilderImpl(mSystemClock, mNotifLog);
+ mListBuilder = new ShadeListBuilder(mSystemClock, mNotifLog);
mListBuilder.setOnRenderListListener(mOnRenderListListener);
mListBuilder.attach(mNotifCollection);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
index 7ff3240..5e0baf2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection.notifcollection;
+package com.android.systemui.statusbar.notification.collection.coalescer;
import static com.android.internal.util.Preconditions.checkNotNull;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java
index ea6c70a..701cf95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java
@@ -36,7 +36,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
@@ -65,7 +65,7 @@
@Mock private ActivityManagerInternal mActivityMangerInternal;
@Mock private IPackageManager mIPackageManager;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
- @Mock private NotifListBuilderImpl mNotifListBuilder;
+ @Mock private NotifPipeline mNotifPipeline;
private Notification mNotification;
private NotificationEntry mEntry;
private DeviceProvisionedCoordinator mDeviceProvisionedCoordinator;
@@ -84,8 +84,8 @@
.build();
ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
- mDeviceProvisionedCoordinator.attach(null, mNotifListBuilder);
- verify(mNotifListBuilder, times(1)).addPreGroupFilter(filterCaptor.capture());
+ mDeviceProvisionedCoordinator.attach(mNotifPipeline);
+ verify(mNotifPipeline, times(1)).addPreGroupFilter(filterCaptor.capture());
mDeviceProvisionedFilter = filterCaptor.getValue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
index 01bca0d..6cc8dd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
@@ -36,12 +36,11 @@
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
-import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import org.junit.Before;
import org.junit.Test;
@@ -59,8 +58,7 @@
@Mock private Handler mMainHandler;
@Mock private ForegroundServiceController mForegroundServiceController;
@Mock private AppOpsController mAppOpsController;
- @Mock private NotifListBuilderImpl mNotifListBuilder;
- @Mock private NotifCollection mNotifCollection;
+ @Mock private NotifPipeline mNotifPipeline;
private NotificationEntry mEntry;
private Notification mNotification;
@@ -84,9 +82,9 @@
ArgumentCaptor<NotifLifetimeExtender> lifetimeExtenderCaptor =
ArgumentCaptor.forClass(NotifLifetimeExtender.class);
- mForegroundCoordinator.attach(mNotifCollection, mNotifListBuilder);
- verify(mNotifListBuilder, times(1)).addPreGroupFilter(filterCaptor.capture());
- verify(mNotifCollection, times(1)).addNotificationLifetimeExtender(
+ mForegroundCoordinator.attach(mNotifPipeline);
+ verify(mNotifPipeline, times(1)).addPreGroupFilter(filterCaptor.capture());
+ verify(mNotifPipeline, times(1)).addNotificationLifetimeExtender(
lifetimeExtenderCaptor.capture());
mForegroundFilter = filterCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
index f921cf9..5866d90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -41,7 +41,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
@@ -68,7 +68,7 @@
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private HighPriorityProvider mHighPriorityProvider;
- @Mock private NotifListBuilderImpl mNotifListBuilder;
+ @Mock private NotifPipeline mNotifPipeline;
private NotificationEntry mEntry;
private KeyguardCoordinator mKeyguardCoordinator;
@@ -87,8 +87,8 @@
.build();
ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
- mKeyguardCoordinator.attach(null, mNotifListBuilder);
- verify(mNotifListBuilder, times(1)).addPreRenderFilter(filterCaptor.capture());
+ mKeyguardCoordinator.attach(mNotifPipeline);
+ verify(mNotifPipeline, times(1)).addPreRenderFilter(filterCaptor.capture());
mKeyguardFilter = filterCaptor.getValue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index d3b16c3..e84f9cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -33,7 +33,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
@@ -50,7 +50,7 @@
public class RankingCoordinatorTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
- @Mock private NotifListBuilderImpl mNotifListBuilder;
+ @Mock private NotifPipeline mNotifPipeline;
private NotificationEntry mEntry;
private RankingCoordinator mRankingCoordinator;
private NotifFilter mRankingFilter;
@@ -62,8 +62,8 @@
mEntry = new NotificationEntryBuilder().build();
ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
- mRankingCoordinator.attach(null, mNotifListBuilder);
- verify(mNotifListBuilder, times(1)).addPreGroupFilter(filterCaptor.capture());
+ mRankingCoordinator.attach(mNotifPipeline);
+ verify(mNotifPipeline, times(1)).addPreGroupFilter(filterCaptor.capture());
mRankingFilter = filterCaptor.getValue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 9bd3914..d9939f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -71,7 +71,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 5ac7bfb..782e14c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -54,7 +54,7 @@
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 560aadb..fee4852 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -120,8 +120,8 @@
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -214,7 +214,7 @@
@Mock private NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
@Mock private KeyguardBypassController mKeyguardBypassController;
@Mock private DynamicPrivacyController mDynamicPrivacyController;
- @Mock private NewNotifPipeline mNewNotifPipeline;
+ @Mock private NotifPipelineInitializer mNewNotifPipeline;
@Mock private ZenModeController mZenModeController;
@Mock private AutoHideController mAutoHideController;
@Mock private NotificationViewHierarchyManager mNotificationViewHierarchyManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 9cb06a5..13f3a5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.policy;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -39,6 +42,7 @@
import android.os.Handler;
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -358,7 +362,13 @@
}
public void updateDataConnectionState(int dataState, int dataNetType) {
- when(mServiceState.getDataNetworkType()).thenReturn(dataNetType);
+ NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder()
+ .setTransportType(TRANSPORT_TYPE_WWAN)
+ .setDomain(DOMAIN_PS)
+ .setAccessNetworkTechnology(dataNetType)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN))
+ .thenReturn(fakeRegInfo);
mPhoneStateListener.onDataConnectionStateChanged(dataState, dataNetType);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 5a5ef8b..f6c750d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -1,5 +1,8 @@
package com.android.systemui.statusbar.policy;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
@@ -418,7 +421,13 @@
// State from NR_5G to NONE NR_STATE_RESTRICTED, showing corresponding icon
doReturn(NetworkRegistrationInfo.NR_STATE_RESTRICTED).when(mServiceState).getNrState();
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder()
+ .setTransportType(TRANSPORT_TYPE_WWAN)
+ .setDomain(DOMAIN_PS)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .build();
+ doReturn(fakeRegInfo).when(mServiceState)
+ .getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED,
TelephonyManager.NETWORK_TYPE_LTE);
@@ -480,8 +489,13 @@
verifyDataIndicators(TelephonyIcons.ICON_LTE);
- when(mServiceState.getDataNetworkType())
- .thenReturn(TelephonyManager.NETWORK_TYPE_HSPA);
+ NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder()
+ .setTransportType(TRANSPORT_TYPE_WWAN)
+ .setDomain(DOMAIN_PS)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_HSPA)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN))
+ .thenReturn(fakeRegInfo);
updateServiceState();
verifyDataIndicators(TelephonyIcons.ICON_H);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 4103d71..cd89d3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -30,12 +30,12 @@
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.net.DataUsageController;
@@ -418,7 +418,7 @@
intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mSubId);
+ SubscriptionManager.putSubscriptionIdExtra(intent, mSubId);
return intent;
}
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
index e99c2c5..c71d0d7 100644
--- a/packages/Tethering/AndroidManifest.xml
+++ b/packages/Tethering/AndroidManifest.xml
@@ -32,9 +32,11 @@
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MANAGE_USB" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
<uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<application
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 37e679d..ca290c6 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -1,7 +1,152 @@
<?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>
<!--
OEMs that wish to change the below settings must do so via a runtime resource overlay package
and *NOT* by changing this file. This file is part of the tethering mainline module.
+ TODO: define two resources for each config item: a default_* resource and a config_* resource,
+ config_* is empty by default but may be overridden by RROs.
-->
+ <!-- List of regexpressions describing the interface (if any) that represent tetherable
+ USB interfaces. If the device doesn't want to support tethering over USB this should
+ be empty. An example would be "usb.*" -->
+ <string-array translatable="false" name="config_tether_usb_regexs">
+ <item>"usb\\d"</item>
+ <item>"rndis\\d"</item>
+ </string-array>
+
+ <!-- List of regexpressions describing the interface (if any) that represent tetherable
+ Wifi interfaces. If the device doesn't want to support tethering over Wifi this
+ should be empty. An example would be "softap.*" -->
+ <string-array translatable="false" name="config_tether_wifi_regexs">
+ <item>"wlan\\d"</item>
+ <item>"softap\\d"</item>
+ </string-array>
+
+ <!-- List of regexpressions describing the interface (if any) that represent tetherable
+ Wifi P2P interfaces. If the device doesn't want to support tethering over Wifi P2p this
+ should be empty. An example would be "p2p-p2p.*" -->
+ <string-array translatable="false" name="config_tether_wifi_p2p_regexs">
+ </string-array>
+
+ <!-- List of regexpressions describing the interface (if any) that represent tetherable
+ bluetooth interfaces. If the device doesn't want to support tethering over bluetooth this
+ should be empty. -->
+ <string-array translatable="false" name="config_tether_bluetooth_regexs">
+ <item>"bt-pan"</item>
+ </string-array>
+
+ <!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. -->
+ <bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool>
+
+ <!-- Dhcp range (min, max) to use for tethering purposes -->
+ <string-array translatable="false" name="config_tether_dhcp_range">
+ </string-array>
+
+ <!-- Array of ConnectivityManager.TYPE_{BLUETOOTH, ETHERNET, MOBILE, MOBILE_DUN, MOBILE_HIPRI,
+ WIFI} values allowable for tethering.
+
+ Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
+ [1,7,0] for TYPE_WIFI, TYPE_BLUETOOTH, and TYPE_MOBILE.
+
+ This list is also modified by code within the framework, including:
+
+ - TYPE_ETHERNET (9) is prepended to this list, and
+
+ - the return value of TelephonyManager.isTetheringApnRequired()
+ determines how the array is further modified:
+
+ * TRUE (DUN REQUIRED).
+ TYPE_MOBILE is removed (if present).
+ TYPE_MOBILE_HIPRI is removed (if present).
+ TYPE_MOBILE_DUN is appended (if not already present).
+
+ * FALSE (DUN NOT REQUIRED).
+ TYPE_MOBILE_DUN is removed (if present).
+ If both of TYPE_MOBILE{,_HIPRI} are not present:
+ TYPE_MOBILE is appended.
+ TYPE_MOBILE_HIPRI is appended.
+
+ For other changes applied to this list, now and in the future, see
+ com.android.server.connectivity.tethering.TetheringConfiguration.
+
+ Note also: the order of this is important. The first upstream type
+ for which a satisfying network exists is used.
+ -->
+ <integer-array translatable="false" name="config_tether_upstream_types">
+ </integer-array>
+
+ <!-- When true, the tethering upstream network follows the current default
+ Internet network (except when the current default network is mobile,
+ in which case a DUN network will be used if required).
+
+ When true, overrides the config_tether_upstream_types setting above.
+ -->
+ <bool translatable="false" name="config_tether_upstream_automatic">true</bool>
+
+
+ <!-- If the mobile hotspot feature requires provisioning, a package name and class name
+ can be provided to launch a supported application that provisions the devices.
+ EntitlementManager will send an inent to Settings with the specified package name and
+ class name in extras to launch provision app.
+ TODO: note what extras here.
+
+ See EntitlementManager#runUiTetherProvisioning and
+ packages/apps/Settings/src/com/android/settings/network/TetherProvisioningActivity.java
+ for more details.
+
+ For ui-less/periodic recheck support see config_mobile_hotspot_provision_app_no_ui
+ -->
+ <!-- The first element is the package name and the second element is the class name
+ of the provisioning app -->
+ <string-array translatable="false" name="config_mobile_hotspot_provision_app">
+ <!--
+ <item>com.example.provisioning</item>
+ <item>com.example.provisioning.Activity</item>
+ -->
+ </string-array>
+
+ <!-- If the mobile hotspot feature requires provisioning, an action can be provided
+ that will be broadcast in non-ui cases for checking the provisioning status.
+ EntitlementManager will pass the specified name to Settings and Settings would
+ launch provisioning app by sending an intent with the package name.
+
+ A second broadcast, action defined by config_mobile_hotspot_provision_response,
+ will be sent back to notify if provisioning succeeded or not. The response will
+ match that of the activity in config_mobile_hotspot_provision_app, but instead
+ contained within the int extra "EntitlementResult".
+ TODO: provide the system api for "EntitlementResult" extra and note it here.
+
+ See EntitlementManager#runSilentTetherProvisioning and
+ packages/apps/Settings/src/com/android/settings/wifi/tether/TetherService.java for more
+ details.
+ -->
+ <string translatable="false" name="config_mobile_hotspot_provision_app_no_ui"></string>
+
+ <!-- Sent in response to a provisioning check. The caller must hold the
+ permission android.permission.TETHER_PRIVILEGED for Settings to
+ receive this response.
+
+ See config_mobile_hotspot_provision_response
+ -->
+ <string translatable="false" name="config_mobile_hotspot_provision_response"></string>
+
+ <!-- Number of hours between each background provisioning call -->
+ <integer translatable="false" name="config_mobile_hotspot_provision_check_period">24</integer>
+
+ <!-- ComponentName of the service used to run no ui tether provisioning. -->
+ <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
</resources>
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
new file mode 100644
index 0000000..e089d9d
--- /dev/null
+++ b/packages/Tethering/res/values/overlayable.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <overlayable name="TetheringConfig">
+ <policy type="product|system|vendor">
+ <item type="array" name="config_tether_usb_regexs"/>
+ <item type="array" name="config_tether_wifi_regexs"/>
+ <item type="array" name="config_tether_wifi_p2p_regexs"/>
+ <item type="array" name="config_tether_bluetooth_regexs"/>
+ <item type="array" name="config_tether_dhcp_range"/>
+ <item type="bool" name="config_tether_enable_legacy_dhcp_server"/>
+ <item type="array" name="config_tether_upstream_types"/>
+ <item type="bool" name="config_tether_upstream_automatic"/>
+ <!-- Configuration values for tethering entitlement check -->
+ <item type="array" name="config_mobile_hotspot_provision_app"/>
+ <item type="string" name="config_mobile_hotspot_provision_app_no_ui"/>
+ <item type="string" name="config_mobile_hotspot_provision_response"/>
+ <item type="integer" name="config_mobile_hotspot_provision_check_period"/>
+ <item type="string" name="config_wifi_tether_enable"/>
+ </policy>
+ </overlayable>
+</resources>
diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml
index ca866a9..792bce9 100644
--- a/packages/Tethering/res/values/strings.xml
+++ b/packages/Tethering/res/values/strings.xml
@@ -1,4 +1,18 @@
<?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>
<!-- Shown when the device is tethered -->
<!-- Strings for tethered notification title [CHAR LIMIT=200] -->
@@ -9,8 +23,11 @@
<!-- This notification is shown when tethering has been disabled on a user's device.
The device is managed by the user's employer. Tethering can't be turned on unless the
IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
- <!-- Strings for tether disabling notification title [CHAR LIMIT=200] -->
+ <!-- Strings for tether disabling notification title [CHAR LIMIT=200] -->
<string name="disable_tether_notification_title">Tethering is disabled</string>
- <!-- Strings for tether disabling notification message [CHAR LIMIT=200] -->
+ <!-- Strings for tether disabling notification message [CHAR LIMIT=200] -->
<string name="disable_tether_notification_message">Contact your admin for details</string>
+
+ <!-- Strings for tether notification channel name [CHAR LIMIT=200] -->
+ <string name="notification_channel_tethering_status">Hotspot & tethering status</string>
</resources>
\ No newline at end of file
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 6ac467e..4306cf0b 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -26,7 +26,6 @@
import android.net.INetd;
import android.net.INetworkStackStatusCallback;
-import android.net.INetworkStatsService;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -176,7 +175,6 @@
private final SharedLog mLog;
private final INetd mNetd;
- private final INetworkStatsService mStatsService;
private final Callback mCallback;
private final InterfaceController mInterfaceCtrl;
@@ -208,12 +206,10 @@
public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
- INetd netd, INetworkStatsService statsService, Callback callback,
- boolean usingLegacyDhcp, Dependencies deps) {
+ INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNetd = netd;
- mStatsService = statsService;
mCallback = callback;
mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
mIfaceName = ifaceName;
@@ -882,12 +878,6 @@
// to remove their rules, which generates errors.
// Just do the best we can.
try {
- // About to tear down NAT; gather remaining statistics.
- mStatsService.forceUpdate();
- } catch (Exception e) {
- mLog.e("Exception in forceUpdate: " + e.toString());
- }
- try {
mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface);
} catch (RemoteException | ServiceSpecificException e) {
mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString());
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index ce7c2a6..cc36f4a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -16,35 +16,40 @@
package com.android.server.connectivity.tethering;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
-import static android.net.TrafficStats.UID_TETHERING;
+import static android.net.NetworkStats.UID_TETHERING;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.usage.NetworkStatsManager;
import android.content.ContentResolver;
-import android.net.ITetheringStatsProvider;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkStats;
+import android.net.NetworkStats.Entry;
import android.net.RouteInfo;
import android.net.netlink.ConntrackMessage;
import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkSocket;
+import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProviderCallback;
import android.net.util.SharedLog;
import android.os.Handler;
-import android.os.INetworkManagementService;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.SystemClock;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.text.TextUtils;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
@@ -73,13 +78,19 @@
private static final String ANYIP = "0.0.0.0";
private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
+ @VisibleForTesting
+ enum StatsType {
+ STATS_PER_IFACE,
+ STATS_PER_UID,
+ }
+
private enum UpdateType { IF_NEEDED, FORCE };
private final Handler mHandler;
private final OffloadHardwareInterface mHwInterface;
private final ContentResolver mContentResolver;
- private final INetworkManagementService mNms;
- private final ITetheringStatsProvider mStatsProvider;
+ private final @NonNull OffloadTetheringStatsProvider mStatsProvider;
+ private final @Nullable NetworkStatsProviderCallback mStatsProviderCb;
private final SharedLog mLog;
private final HashMap<String, LinkProperties> mDownstreams;
private boolean mConfigInitialized;
@@ -109,22 +120,23 @@
private int mNatUpdateNetlinkErrors;
public OffloadController(Handler h, OffloadHardwareInterface hwi,
- ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) {
+ ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log) {
mHandler = h;
mHwInterface = hwi;
mContentResolver = contentResolver;
- mNms = nms;
mStatsProvider = new OffloadTetheringStatsProvider();
mLog = log.forSubComponent(TAG);
mDownstreams = new HashMap<>();
mExemptPrefixes = new HashSet<>();
mLastLocalPrefixStrs = new HashSet<>();
-
+ NetworkStatsProviderCallback providerCallback = null;
try {
- mNms.registerTetheringStatsProvider(mStatsProvider, getClass().getSimpleName());
- } catch (RemoteException e) {
- mLog.e("Cannot register offload stats provider: " + e);
+ providerCallback = nsm.registerNetworkStatsProvider(
+ getClass().getSimpleName(), mStatsProvider);
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Cannot register offload stats provider: " + e);
}
+ mStatsProviderCb = providerCallback;
}
/** Start hardware offload. */
@@ -173,7 +185,7 @@
// and we need to synchronize stats and limits between
// software and hardware forwarding.
updateStatsForAllUpstreams();
- forceTetherStatsPoll();
+ mStatsProvider.pushTetherStats();
}
@Override
@@ -186,7 +198,7 @@
// limits set take into account any software tethering
// traffic that has been happening in the meantime.
updateStatsForAllUpstreams();
- forceTetherStatsPoll();
+ mStatsProvider.pushTetherStats();
// [2] (Re)Push all state.
computeAndPushLocalPrefixes(UpdateType.FORCE);
pushAllDownstreamState();
@@ -204,14 +216,11 @@
// the HAL queued the callback.
// TODO: rev the HAL so that it provides an interface name.
- // Fetch current stats, so that when our notification reaches
- // NetworkStatsService and triggers a poll, we will respond with
- // current data (which will be above the limit that was reached).
- // Note that if we just changed upstream, this is unnecessary but harmless.
- // The stats for the previous upstream were already updated on this thread
- // just after the upstream was changed, so they are also up-to-date.
updateStatsForCurrentUpstream();
- forceTetherStatsPoll();
+ mStatsProvider.pushTetherStats();
+ // Push stats to service does not cause the service react to it immediately.
+ // Inform the service about limit reached.
+ if (mStatsProviderCb != null) mStatsProviderCb.onLimitReached();
}
@Override
@@ -253,42 +262,37 @@
return mConfigInitialized && mControlInitialized;
}
- private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
- @Override
- public NetworkStats getTetherStats(int how) {
- // getTetherStats() is the only function in OffloadController that can be called from
- // a different thread. Do not attempt to update stats by querying the offload HAL
- // synchronously from a different thread than our Handler thread. http://b/64771555.
- Runnable updateStats = () -> {
- updateStatsForCurrentUpstream();
- };
- if (Looper.myLooper() == mHandler.getLooper()) {
- updateStats.run();
- } else {
- mHandler.post(updateStats);
- }
+ @VisibleForTesting
+ class OffloadTetheringStatsProvider extends AbstractNetworkStatsProvider {
+ // These stats must only ever be touched on the handler thread.
+ @NonNull
+ private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
+ @NonNull
+ private NetworkStats mUidStats = new NetworkStats(0L, 0);
- NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- NetworkStats.Entry entry = new NetworkStats.Entry();
- entry.set = SET_DEFAULT;
- entry.tag = TAG_NONE;
- entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL;
+ @VisibleForTesting
+ @NonNull
+ NetworkStats getTetherStats(@NonNull StatsType how) {
+ NetworkStats stats = new NetworkStats(0L, 0);
+ final int uid = (how == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL;
- for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
- ForwardedStats value = kv.getValue();
- entry.iface = kv.getKey();
- entry.rxBytes = value.rxBytes;
- entry.txBytes = value.txBytes;
- stats.addEntry(entry);
+ for (final Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
+ final ForwardedStats value = kv.getValue();
+ final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L);
+ stats = stats.addValues(entry);
}
return stats;
}
@Override
- public void setInterfaceQuota(String iface, long quotaBytes) {
+ public void setLimit(String iface, long quotaBytes) {
+ mLog.i("setLimit: " + iface + "," + quotaBytes);
+ // Listen for all iface is necessary since upstream might be changed after limit
+ // is set.
mHandler.post(() -> {
- if (quotaBytes == ITetheringStatsProvider.QUOTA_UNLIMITED) {
+ if (quotaBytes == QUOTA_UNLIMITED) {
mInterfaceQuotas.remove(iface);
} else {
mInterfaceQuotas.put(iface, quotaBytes);
@@ -296,6 +300,42 @@
maybeUpdateDataLimit(iface);
});
}
+
+ /**
+ * Push stats to service, but does not cause a force polling. Note that this can only be
+ * called on the handler thread.
+ */
+ public void pushTetherStats() {
+ // TODO: remove the accumulated stats and report the diff from HAL directly.
+ if (null == mStatsProviderCb) return;
+ final NetworkStats ifaceDiff =
+ getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats);
+ final NetworkStats uidDiff =
+ getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats);
+ try {
+ mStatsProviderCb.onStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
+ mIfaceStats = mIfaceStats.add(ifaceDiff);
+ mUidStats = mUidStats.add(uidDiff);
+ } catch (RuntimeException e) {
+ mLog.e("Cannot report network stats: ", e);
+ }
+ }
+
+ @Override
+ public void requestStatsUpdate(int token) {
+ mLog.i("requestStatsUpdate: " + token);
+ // Do not attempt to update stats by querying the offload HAL
+ // synchronously from a different thread than the Handler thread. http://b/64771555.
+ mHandler.post(() -> {
+ updateStatsForCurrentUpstream();
+ pushTetherStats();
+ });
+ }
+
+ @Override
+ public void setAlert(long quotaBytes) {
+ // TODO: Ask offload HAL to notify alert without stopping traffic.
+ }
}
private String currentUpstreamInterface() {
@@ -353,14 +393,6 @@
}
}
- private void forceTetherStatsPoll() {
- try {
- mNms.tetherLimitReached(mStatsProvider);
- } catch (RemoteException e) {
- mLog.e("Cannot report data limit reached: " + e);
- }
- }
-
/** Set current tethering upstream LinkProperties. */
public void setUpstreamLinkProperties(LinkProperties lp) {
if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 4a8ef1f..90b9d3f 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -29,6 +29,8 @@
import android.os.RemoteException;
import android.system.OsConstants;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
@@ -91,6 +93,12 @@
txBytes = 0;
}
+ @VisibleForTesting
+ public ForwardedStats(long rxBytes, long txBytes) {
+ this.rxBytes = rxBytes;
+ this.txBytes = txBytes;
+ }
+
/** Add Tx/Rx bytes. */
public void add(ForwardedStats other) {
rxBytes += other.rxBytes;
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 d6abfb9..b301d3d 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -50,8 +50,10 @@
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.app.Notification;
+import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile;
@@ -64,7 +66,6 @@
import android.hardware.usb.UsbManager;
import android.net.INetd;
import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
import android.net.ITetheringEventCallback;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -106,8 +107,6 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
@@ -177,7 +176,6 @@
private final Context mContext;
private final ArrayMap<String, TetherState> mTetherStates;
private final BroadcastReceiver mStateReceiver;
- private final INetworkStatsService mStatsService;
private final INetworkPolicyManager mPolicyManager;
private final Looper mLooper;
private final StateMachine mTetherMasterSM;
@@ -213,7 +211,6 @@
mLog.mark("Tethering.constructed");
mDeps = deps;
mContext = mDeps.getContext();
- mStatsService = mDeps.getINetworkStatsService();
mPolicyManager = mDeps.getINetworkPolicyManager();
mNetd = mDeps.getINetd(mContext);
mLooper = mDeps.getTetheringLooper();
@@ -225,10 +222,12 @@
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
mTetherMasterSM.start();
+ final NetworkStatsManager statsManager =
+ (NetworkStatsManager) mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
mHandler = mTetherMasterSM.getHandler();
mOffloadController = new OffloadController(mHandler,
mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(),
- mDeps.getINetworkManagementService(), mLog);
+ statsManager, mLog);
mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mForwardedDownstreams = new HashSet<>();
@@ -265,7 +264,7 @@
}
final UserManager userManager = (UserManager) mContext.getSystemService(
- Context.USER_SERVICE);
+ Context.USER_SERVICE);
mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler);
mActiveDataSubIdListener = new ActiveDataSubIdListener(executor);
@@ -663,19 +662,19 @@
if (usbTethered) {
if (wifiTethered || bluetoothTethered) {
- showTetheredNotification(SystemMessage.NOTE_TETHER_GENERAL);
+ showTetheredNotification(R.drawable.stat_sys_tether_general);
} else {
- showTetheredNotification(SystemMessage.NOTE_TETHER_USB);
+ showTetheredNotification(R.drawable.stat_sys_tether_usb);
}
} else if (wifiTethered) {
if (bluetoothTethered) {
- showTetheredNotification(SystemMessage.NOTE_TETHER_GENERAL);
+ showTetheredNotification(R.drawable.stat_sys_tether_general);
} else {
/* We now have a status bar icon for WifiTethering, so drop the notification */
clearTetheredNotification();
}
} else if (bluetoothTethered) {
- showTetheredNotification(SystemMessage.NOTE_TETHER_BLUETOOTH);
+ showTetheredNotification(R.drawable.stat_sys_tether_bluetooth);
} else {
clearTetheredNotification();
}
@@ -688,30 +687,22 @@
@VisibleForTesting
protected void showTetheredNotification(int id, boolean tetheringOn) {
NotificationManager notificationManager =
- (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0)
+ .getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null) {
return;
}
- int icon = 0;
- switch(id) {
- case SystemMessage.NOTE_TETHER_USB:
- icon = R.drawable.stat_sys_tether_usb;
- break;
- case SystemMessage.NOTE_TETHER_BLUETOOTH:
- icon = R.drawable.stat_sys_tether_bluetooth;
- break;
- case SystemMessage.NOTE_TETHER_GENERAL:
- default:
- icon = R.drawable.stat_sys_tether_general;
- break;
- }
+ final NotificationChannel channel = new NotificationChannel(
+ "TETHERING_STATUS",
+ mContext.getResources().getString(R.string.notification_channel_tethering_status),
+ NotificationManager.IMPORTANCE_LOW);
+ notificationManager.createNotificationChannel(channel);
if (mLastNotificationId != 0) {
- if (mLastNotificationId == icon) {
+ if (mLastNotificationId == id) {
return;
}
- notificationManager.cancelAsUser(null, mLastNotificationId,
- UserHandle.ALL);
+ notificationManager.cancel(null, mLastNotificationId);
mLastNotificationId = 0;
}
@@ -719,8 +710,8 @@
intent.setClassName("com.android.settings", "com.android.settings.TetherSettings");
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
- PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0,
- null, UserHandle.CURRENT);
+ PendingIntent pi = PendingIntent.getActivity(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0), 0, intent, 0, null);
Resources r = mContext.getResources();
final CharSequence title;
@@ -735,32 +726,31 @@
}
if (mTetheredNotificationBuilder == null) {
- mTetheredNotificationBuilder = new Notification.Builder(mContext,
- SystemNotificationChannels.NETWORK_STATUS);
+ mTetheredNotificationBuilder = new Notification.Builder(mContext, channel.getId());
mTetheredNotificationBuilder.setWhen(0)
.setOngoing(true)
.setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color))
+ android.R.color.system_notification_accent_color))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setCategory(Notification.CATEGORY_STATUS);
}
- mTetheredNotificationBuilder.setSmallIcon(icon)
+ mTetheredNotificationBuilder.setSmallIcon(id)
.setContentTitle(title)
.setContentText(message)
.setContentIntent(pi);
mLastNotificationId = id;
- notificationManager.notifyAsUser(null, mLastNotificationId,
- mTetheredNotificationBuilder.buildInto(new Notification()), UserHandle.ALL);
+ notificationManager.notify(null, mLastNotificationId,
+ mTetheredNotificationBuilder.buildInto(new Notification()));
}
@VisibleForTesting
protected void clearTetheredNotification() {
NotificationManager notificationManager =
- (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0)
+ .getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null && mLastNotificationId != 0) {
- notificationManager.cancelAsUser(null, mLastNotificationId,
- UserHandle.ALL);
+ notificationManager.cancel(null, mLastNotificationId);
mLastNotificationId = 0;
}
}
@@ -2064,7 +2054,7 @@
mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
final TetherState tetherState = new TetherState(
- new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mStatsService,
+ new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState);
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 397ba8a..dbe7892 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -21,7 +21,7 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
+import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
import static com.android.internal.R.array.config_tether_bluetooth_regexs;
@@ -33,13 +33,13 @@
import static com.android.internal.R.bool.config_tether_upstream_automatic;
import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period;
import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
+import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.TetheringConfigurationParcel;
import android.net.util.SharedLog;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -84,6 +84,12 @@
private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
+ /**
+ * Use the old dnsmasq DHCP server for tethering instead of the framework implementation.
+ */
+ public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
+ "tether_enable_legacy_dhcp_server";
+
public final String[] tetherableUsbRegexs;
public final String[] tetherableWifiRegexs;
public final String[] tetherableWifiP2pRegexs;
@@ -122,7 +128,7 @@
legacyDhcpRanges = getLegacyDhcpRanges(res);
defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
- enableLegacyDhcpServer = getEnableLegacyDhcpServer(ctx);
+ enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app);
provisioningAppNoUi = getProvisioningAppNoUi(res);
@@ -332,10 +338,14 @@
}
}
- private static boolean getEnableLegacyDhcpServer(Context ctx) {
- final ContentResolver cr = ctx.getContentResolver();
- final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
- return intVal != 0;
+ private boolean getEnableLegacyDhcpServer(final Resources res) {
+ return getResourceBoolean(res, config_tether_enable_legacy_dhcp_server)
+ || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER);
+ }
+
+ @VisibleForTesting
+ protected boolean getDeviceConfigBoolean(final String name) {
+ return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, false /** defaultValue */);
}
private Resources getResources(Context ctx, int subId) {
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
index b16b329..79e99e8 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.net.INetd;
import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
import android.net.NetworkRequest;
import android.net.ip.IpServer;
import android.net.util.SharedLog;
@@ -107,15 +106,6 @@
}
/**
- * Get a reference to INetworkStatsService to force update tethering usage.
- * Note: This should be removed in R development cycle.
- */
- public INetworkStatsService getINetworkStatsService() {
- return INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
- }
-
- /**
* Get a reference to INetworkPolicyManager to be used by tethering.
*/
public INetworkPolicyManager getINetworkPolicyManager() {
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index e4e4a09..99130d2 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -42,7 +42,6 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserManager;
import android.provider.Settings;
@@ -363,7 +362,7 @@
IBinder connector;
try {
final long before = System.currentTimeMillis();
- while ((connector = ServiceManager.getService(
+ while ((connector = (IBinder) mContext.getSystemService(
Context.NETWORK_STACK_SERVICE)) == null) {
if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 5692a6f..2875f71 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -23,6 +23,8 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -215,19 +217,28 @@
mLog.e("registerMobileNetworkRequest() already registered");
return;
}
- // The following use of the legacy type system cannot be removed until
- // after upstream selection no longer finds networks by legacy type.
- // See also http://b/34364553 .
- final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
- final NetworkRequest mobileUpstreamRequest = new NetworkRequest.Builder()
- .setCapabilities(networkCapabilitiesForType(legacyType))
- .build();
+ final NetworkRequest mobileUpstreamRequest;
+ if (mDunRequired) {
+ mobileUpstreamRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_DUN)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ } else {
+ mobileUpstreamRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ }
// The existing default network and DUN callbacks will be notified.
// Therefore, to avoid duplicate notifications, we only register a no-op.
mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST);
+ // The following use of the legacy type system cannot be removed until
+ // upstream selection no longer finds networks by legacy type.
+ // See also http://b/34364553 .
+ final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
+
// TODO: Change the timeout from 0 (no onUnavailable callback) to some
// moderate callback timeout. This might be useful for updating some UI.
// Additionally, we log a message to aid in any subsequent debugging.
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 65a0ac1..1f50b6b 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -52,7 +52,6 @@
import static org.mockito.Mockito.when;
import android.net.INetd;
-import android.net.INetworkStatsService;
import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -99,7 +98,6 @@
private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
@Mock private INetd mNetd;
- @Mock private INetworkStatsService mStatsService;
@Mock private IpServer.Callback mCallback;
@Mock private SharedLog mSharedLog;
@Mock private IDhcpServer mDhcpServer;
@@ -139,13 +137,13 @@
mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
}
mIpServer = new IpServer(
- IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mStatsService,
+ IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
mCallback, usingLegacyDhcp, mDependencies);
mIpServer.start();
// Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll();
- reset(mNetd, mStatsService, mCallback);
+ reset(mNetd, mCallback);
when(mRaDaemon.start()).thenReturn(true);
}
@@ -162,7 +160,7 @@
if (upstreamIface != null) {
dispatchTetherConnectionChanged(upstreamIface);
}
- reset(mNetd, mStatsService, mCallback);
+ reset(mNetd, mCallback);
}
@Before public void setUp() throws Exception {
@@ -173,13 +171,13 @@
@Test
public void startsOutAvailable() {
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
- mNetd, mStatsService, mCallback, false /* usingLegacyDhcp */, mDependencies);
+ mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
mIpServer.start();
mLooper.dispatchAll();
verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mCallback, mNetd, mStatsService);
+ verifyNoMoreInteractions(mCallback, mNetd);
}
@Test
@@ -198,7 +196,7 @@
// None of these commands should trigger us to request action from
// the rest of the system.
dispatchCommand(command);
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
}
@@ -210,7 +208,7 @@
verify(mCallback).updateInterfaceState(
mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -228,7 +226,7 @@
mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -236,7 +234,7 @@
initTetheredStateMachine(TETHERING_BLUETOOTH, null);
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
+ InOrder inOrder = inOrder(mNetd, mCallback);
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
@@ -245,7 +243,7 @@
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -265,7 +263,7 @@
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -285,7 +283,7 @@
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -298,7 +296,7 @@
InOrder inOrder = inOrder(mNetd);
inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -306,13 +304,12 @@
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd, mStatsService);
- inOrder.verify(mStatsService).forceUpdate();
+ InOrder inOrder = inOrder(mNetd);
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -322,12 +319,10 @@
doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd, mStatsService);
- inOrder.verify(mStatsService).forceUpdate();
+ InOrder inOrder = inOrder(mNetd);
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mStatsService).forceUpdate();
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
}
@@ -340,13 +335,11 @@
IFACE_NAME, UPSTREAM_IFACE2);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd, mStatsService);
- inOrder.verify(mStatsService).forceUpdate();
+ InOrder inOrder = inOrder(mNetd);
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mStatsService).forceUpdate();
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
}
@@ -356,8 +349,7 @@
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
- inOrder.verify(mStatsService).forceUpdate();
+ InOrder inOrder = inOrder(mNetd, mCallback);
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
@@ -368,7 +360,7 @@
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -435,11 +427,11 @@
public void ignoresDuplicateUpstreamNotifications() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
for (int i = 0; i < 5; i++) {
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 66eba9a..79bba7f 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -22,10 +22,12 @@
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -39,7 +41,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.util.SharedLog;
@@ -49,9 +50,8 @@
import android.os.ResultReceiver;
import android.os.SystemProperties;
import android.os.test.TestLooper;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
import android.telephony.CarrierConfigManager;
-import android.test.mock.MockContentResolver;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -60,7 +60,6 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
-import com.android.internal.util.test.FakeSettingsProvider;
import org.junit.After;
import org.junit.Before;
@@ -94,7 +93,6 @@
private final PersistableBundle mCarrierConfig = new PersistableBundle();
private final TestLooper mLooper = new TestLooper();
private Context mMockContext;
- private MockContentResolver mContentResolver;
private TestStateMachine mSM;
private WrappedEntitlementManager mEnMgr;
@@ -110,11 +108,6 @@
public Resources getResources() {
return mResources;
}
-
- @Override
- public ContentResolver getContentResolver() {
- return mContentResolver;
- }
}
public class WrappedEntitlementManager extends EntitlementManager {
@@ -151,13 +144,17 @@
MockitoAnnotations.initMocks(this);
mMockingSession = mockitoSession()
.initMocks(this)
- .spyStatic(SystemProperties.class)
+ .mockStatic(SystemProperties.class)
+ .mockStatic(DeviceConfig.class)
.strictness(Strictness.WARN)
.startMocking();
// Don't disable tethering provisioning unless requested.
doReturn(false).when(
() -> SystemProperties.getBoolean(
eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean()));
+ doReturn(false).when(
+ () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
@@ -169,10 +166,9 @@
.thenReturn(new String[0]);
when(mResources.getIntArray(R.array.config_tether_upstream_types))
.thenReturn(new int[0]);
+ when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
- mContentResolver = new MockContentResolver();
- mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
mMockContext = new MockContext(mContext);
mSM = new TestStateMachine();
mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE);
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 7886ca6..7e62e5a 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -16,21 +16,26 @@
package com.android.server.connectivity.tethering;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.STATS_PER_IFACE;
-import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
+import static android.net.NetworkStats.UID_TETHERING;
import static android.net.RouteInfo.RTN_UNICAST;
-import static android.net.TrafficStats.UID_TETHERING;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
+import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_IFACE;
+import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_UID;
import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
import static com.android.testutils.MiscAssertsKt.assertContainsAll;
import static com.android.testutils.MiscAssertsKt.assertThrows;
+import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
@@ -39,11 +44,14 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
+import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.net.ITetheringStatsProvider;
@@ -51,10 +59,12 @@
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkStats;
+import android.net.NetworkStats.Entry;
import android.net.RouteInfo;
+import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProviderCallback;
import android.net.util.SharedLog;
import android.os.Handler;
-import android.os.INetworkManagementService;
import android.os.Looper;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
@@ -97,11 +107,13 @@
@Mock private OffloadHardwareInterface mHardware;
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
- @Mock private INetworkManagementService mNMService;
+ @Mock private NetworkStatsManager mStatsManager;
+ @Mock private NetworkStatsProviderCallback mTetherStatsProviderCb;
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
ArgumentCaptor.forClass(ArrayList.class);
- private final ArgumentCaptor<ITetheringStatsProvider.Stub> mTetherStatsProviderCaptor =
- ArgumentCaptor.forClass(ITetheringStatsProvider.Stub.class);
+ private final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
+ mTetherStatsProviderCaptor =
+ ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
private MockContentResolver mContentResolver;
@@ -114,6 +126,8 @@
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
FakeSettingsProvider.clearSettingsProvider();
+ when(mStatsManager.registerNetworkStatsProvider(anyString(), any()))
+ .thenReturn(mTetherStatsProviderCb);
}
@After public void tearDown() throws Exception {
@@ -139,9 +153,9 @@
private OffloadController makeOffloadController() throws Exception {
OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
- mHardware, mContentResolver, mNMService, new SharedLog("test"));
- verify(mNMService).registerTetheringStatsProvider(
- mTetherStatsProviderCaptor.capture(), anyString());
+ mHardware, mContentResolver, mStatsManager, new SharedLog("test"));
+ verify(mStatsManager).registerNetworkStatsProvider(anyString(),
+ mTetherStatsProviderCaptor.capture());
return offload;
}
@@ -384,12 +398,11 @@
inOrder.verifyNoMoreInteractions();
}
- private void assertNetworkStats(String iface, ForwardedStats stats, NetworkStats.Entry entry) {
- assertEquals(iface, entry.iface);
- assertEquals(stats.rxBytes, entry.rxBytes);
- assertEquals(stats.txBytes, entry.txBytes);
- assertEquals(SET_DEFAULT, entry.set);
- assertEquals(TAG_NONE, entry.tag);
+ private static @NonNull Entry buildTestEntry(@NonNull OffloadController.StatsType how,
+ @NonNull String iface, long rxBytes, long txBytes) {
+ return new Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, SET_DEFAULT,
+ TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, 0L,
+ txBytes, 0L, 0L);
}
@Test
@@ -400,19 +413,16 @@
final OffloadController offload = makeOffloadController();
offload.start();
+ final OffloadController.OffloadTetheringStatsProvider provider =
+ mTetherStatsProviderCaptor.getValue();
+
final String ethernetIface = "eth1";
final String mobileIface = "rmnet_data0";
- ForwardedStats ethernetStats = new ForwardedStats();
- ethernetStats.rxBytes = 12345;
- ethernetStats.txBytes = 54321;
-
- ForwardedStats mobileStats = new ForwardedStats();
- mobileStats.rxBytes = 999;
- mobileStats.txBytes = 99999;
-
- when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
- when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(mobileStats);
+ when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+ new ForwardedStats(12345, 54321));
+ when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(
+ new ForwardedStats(999, 99999));
InOrder inOrder = inOrder(mHardware);
@@ -432,10 +442,35 @@
// Expect that we fetch stats from the previous upstream.
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface));
- ethernetStats = new ForwardedStats();
- ethernetStats.rxBytes = 100000;
- ethernetStats.txBytes = 100000;
- when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
+ // Verify that the fetched stats are stored.
+ final NetworkStats ifaceStats = provider.getTetherStats(STATS_PER_IFACE);
+ final NetworkStats uidStats = provider.getTetherStats(STATS_PER_UID);
+ final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+ .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
+
+ final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+ .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
+
+ assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats));
+ assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats));
+
+ final ArgumentCaptor<NetworkStats> ifaceStatsCaptor = ArgumentCaptor.forClass(
+ NetworkStats.class);
+ final ArgumentCaptor<NetworkStats> uidStatsCaptor = ArgumentCaptor.forClass(
+ NetworkStats.class);
+
+ // Force pushing stats update to verify the stats reported.
+ provider.pushTetherStats();
+ verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
+ ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+ assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue()));
+ assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue()));
+
+
+ when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+ new ForwardedStats(100000, 100000));
offload.setUpstreamLinkProperties(null);
// Expect that we first clear the HAL's upstream parameters.
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
@@ -443,37 +478,38 @@
// Expect that we fetch stats from the previous upstream.
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));
- ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
- NetworkStats stats = provider.getTetherStats(STATS_PER_IFACE);
- NetworkStats perUidStats = provider.getTetherStats(STATS_PER_UID);
- waitForIdle();
// There is no current upstream, so no stats are fetched.
inOrder.verify(mHardware, never()).getForwardedStats(any());
inOrder.verifyNoMoreInteractions();
- assertEquals(2, stats.size());
- assertEquals(2, perUidStats.size());
+ // Verify that the stored stats is accumulated.
+ final NetworkStats ifaceStatsAccu = provider.getTetherStats(STATS_PER_IFACE);
+ final NetworkStats uidStatsAccu = provider.getTetherStats(STATS_PER_UID);
+ final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+ .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
- NetworkStats.Entry entry = null;
- for (int i = 0; i < stats.size(); i++) {
- assertEquals(UID_ALL, stats.getValues(i, entry).uid);
- assertEquals(UID_TETHERING, perUidStats.getValues(i, entry).uid);
- }
+ final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+ .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
- int ethernetPosition = ethernetIface.equals(stats.getValues(0, entry).iface) ? 0 : 1;
- int mobilePosition = 1 - ethernetPosition;
+ assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu));
+ assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu));
- entry = stats.getValues(mobilePosition, entry);
- assertNetworkStats(mobileIface, mobileStats, entry);
- entry = perUidStats.getValues(mobilePosition, entry);
- assertNetworkStats(mobileIface, mobileStats, entry);
+ // Verify that only diff of stats is reported.
+ reset(mTetherStatsProviderCb);
+ provider.pushTetherStats();
+ final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
+ .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
- ethernetStats.rxBytes = 12345 + 100000;
- ethernetStats.txBytes = 54321 + 100000;
- entry = stats.getValues(ethernetPosition, entry);
- assertNetworkStats(ethernetIface, ethernetStats, entry);
- entry = perUidStats.getValues(ethernetPosition, entry);
- assertNetworkStats(ethernetIface, ethernetStats, entry);
+ final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
+ .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
+ verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
+ ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+ assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue()));
+ assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue()));
}
@Test
@@ -493,19 +529,19 @@
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
- ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
+ AbstractNetworkStatsProvider provider = mTetherStatsProviderCaptor.getValue();
final InOrder inOrder = inOrder(mHardware);
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
// Applying an interface quota to the current upstream immediately sends it to the hardware.
- provider.setInterfaceQuota(ethernetIface, ethernetLimit);
+ provider.setLimit(ethernetIface, ethernetLimit);
waitForIdle();
inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
inOrder.verifyNoMoreInteractions();
// Applying an interface quota to another upstream does not take any immediate action.
- provider.setInterfaceQuota(mobileIface, mobileLimit);
+ provider.setLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
@@ -518,7 +554,7 @@
// Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
// to Long.MAX_VALUE.
- provider.setInterfaceQuota(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+ provider.setLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
waitForIdle();
inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
@@ -526,7 +562,7 @@
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
- provider.setInterfaceQuota(mobileIface, mobileLimit);
+ provider.setLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
@@ -535,7 +571,7 @@
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp);
- provider.setInterfaceQuota(mobileIface, mobileLimit);
+ provider.setLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware).getForwardedStats(ethernetIface);
inOrder.verify(mHardware).stopOffloadControl();
@@ -551,7 +587,7 @@
OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onStoppedLimitReached();
- verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
+ verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
}
@Test
@@ -654,9 +690,10 @@
// Verify forwarded stats behaviour.
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
+ // TODO: verify the exact stats reported.
+ verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+ verifyNoMoreInteractions(mTetherStatsProviderCb);
verifyNoMoreInteractions(mHardware);
- verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
- verifyNoMoreInteractions(mNMService);
}
@Test
@@ -719,8 +756,8 @@
// Verify forwarded stats behaviour.
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
- verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
- verifyNoMoreInteractions(mNMService);
+ verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+ verifyNoMoreInteractions(mTetherStatsProviderCb);
// TODO: verify local prefixes and downstreams are also pushed to the HAL.
verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index 7799da4..ef97ad4 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -21,40 +21,44 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
+import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
import static com.android.internal.R.array.config_tether_bluetooth_regexs;
import static com.android.internal.R.array.config_tether_dhcp_range;
import static com.android.internal.R.array.config_tether_upstream_types;
import static com.android.internal.R.array.config_tether_usb_regexs;
import static com.android.internal.R.array.config_tether_wifi_regexs;
+import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.util.SharedLog;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
import android.telephony.TelephonyManager;
-import android.test.mock.MockContentResolver;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.BroadcastInterceptingContext;
-import com.android.internal.util.test.FakeSettingsProvider;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
import java.util.Arrays;
import java.util.Iterator;
@@ -69,9 +73,10 @@
@Mock private TelephonyManager mTelephonyManager;
@Mock private Resources mResources;
@Mock private Resources mResourcesForSubId;
- private MockContentResolver mContentResolver;
private Context mMockContext;
private boolean mHasTelephonyManager;
+ private boolean mEnableLegacyDhcpServer;
+ private MockitoSession mMockingSession;
private class MockTetheringConfiguration extends TetheringConfiguration {
MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
@@ -101,16 +106,20 @@
}
return super.getSystemService(name);
}
-
- @Override
- public ContentResolver getContentResolver() {
- return mContentResolver;
- }
}
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
+ // TODO: use a dependencies class instead of mock statics.
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(DeviceConfig.class)
+ .strictness(Strictness.WARN)
+ .startMocking();
+ doReturn(false).when(
+ () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+
when(mResources.getStringArray(config_tether_dhcp_range)).thenReturn(new String[0]);
when(mResources.getStringArray(config_tether_usb_regexs)).thenReturn(new String[0]);
when(mResources.getStringArray(config_tether_wifi_regexs))
@@ -119,10 +128,15 @@
when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
when(mResources.getStringArray(config_mobile_hotspot_provision_app))
.thenReturn(new String[0]);
- mContentResolver = new MockContentResolver();
- mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
mHasTelephonyManager = true;
mMockContext = new MockContext(mContext);
+ mEnableLegacyDhcpServer = false;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mMockingSession.finishMocking();
}
private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
@@ -268,19 +282,35 @@
@Test
public void testNewDhcpServerDisabled() {
- Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+ when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true);
+ doReturn(false).when(
+ () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
- final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- assertTrue(cfg.enableLegacyDhcpServer);
+ final TetheringConfiguration enableByRes =
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ assertTrue(enableByRes.enableLegacyDhcpServer);
+
+ when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ doReturn(true).when(
+ () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+
+ final TetheringConfiguration enableByDevConfig =
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ assertTrue(enableByDevConfig.enableLegacyDhcpServer);
}
@Test
public void testNewDhcpServerEnabled() {
- Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
+ when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ doReturn(false).when(
+ () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
+ eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
- final TetheringConfiguration cfg = new TetheringConfiguration(
- mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ final TetheringConfiguration cfg =
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+
assertFalse(cfg.enableLegacyDhcpServer);
}
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 7af48a8..7c2c75b 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
@@ -35,9 +35,10 @@
import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
-import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -59,6 +60,7 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.app.usage.NetworkStatsManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -69,7 +71,6 @@
import android.hardware.usb.UsbManager;
import android.net.INetd;
import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
import android.net.ITetheringEventCallback;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
@@ -151,7 +152,7 @@
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@Mock private INetworkManagementService mNMService;
- @Mock private INetworkStatsService mStatsService;
+ @Mock private NetworkStatsManager mStatsManager;
@Mock private INetworkPolicyManager mPolicyManager;
@Mock private OffloadHardwareInterface mOffloadHardwareInterface;
@Mock private Resources mResources;
@@ -216,6 +217,7 @@
if (Context.USB_SERVICE.equals(name)) return mUsbManager;
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
if (Context.USER_SERVICE.equals(name)) return mUserManager;
+ if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
return super.getSystemService(name);
}
@@ -224,6 +226,11 @@
if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE;
return super.getSystemServiceName(serviceClass);
}
+
+ @Override
+ public Context createContextAsUser(UserHandle user, int flags) {
+ return mContext;
+ }
}
public class MockIpServerDependencies extends IpServer.Dependencies {
@@ -265,6 +272,11 @@
}
@Override
+ protected boolean getDeviceConfigBoolean(final String name) {
+ return false;
+ }
+
+ @Override
protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
return mResources;
}
@@ -328,11 +340,6 @@
}
@Override
- public INetworkStatsService getINetworkStatsService() {
- return mStatsService;
- }
-
- @Override
public INetworkPolicyManager getINetworkPolicyManager() {
return mPolicyManager;
}
@@ -423,6 +430,7 @@
.thenReturn(new int[0]);
when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
.thenReturn(false);
+ when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
when(mNetd.interfaceGetList())
.thenReturn(new String[] {
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME});
@@ -432,9 +440,9 @@
.thenReturn(true);
mServiceContext = new TestContext(mContext);
+ when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(null);
mContentResolver = new MockContentResolver(mServiceContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
mIntents = new Vector<>();
mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -445,7 +453,7 @@
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ACTION_TETHER_STATE_CHANGED));
mTethering = makeTethering();
- verify(mNMService).registerTetheringStatsProvider(any(), anyString());
+ verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any());
verify(mNetd).registerUnsolicitedEventListener(any());
final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor =
ArgumentCaptor.forClass(PhoneStateListener.class);
@@ -489,13 +497,15 @@
p2pInfo.groupFormed = isGroupFormed;
p2pInfo.isGroupOwner = isGroupOwner;
- WifiP2pGroup group = new WifiP2pGroup();
- group.setIsGroupOwner(isGroupOwner);
- group.setInterface(ifname);
+ WifiP2pGroup group = mock(WifiP2pGroup.class);
+ when(group.isGroupOwner()).thenReturn(isGroupOwner);
+ when(group.getInterface()).thenReturn(ifname);
- final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
- intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, p2pInfo);
- intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, group);
+ final Intent intent = mock(Intent.class);
+ when(intent.getAction()).thenReturn(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+ when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO)).thenReturn(p2pInfo);
+ when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)).thenReturn(group);
+
mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL,
P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST);
}
@@ -686,7 +696,7 @@
@Test
public void workingMobileUsbTethering_IPv4LegacyDhcp() {
- Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+ when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true);
sendConfigurationChanged();
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
diff --git a/proto/Android.bp b/proto/Android.bp
index 65bccbb..7cf6ce7 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -27,3 +27,8 @@
srcs: ["src/metrics_constants/metrics_constants.proto"],
sdk_version: "system_current",
}
+
+filegroup {
+ name: "system-messages-proto-src",
+ srcs: ["src/system_messages.proto"],
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index cbff6bd..37ac3ec 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -35,6 +35,7 @@
import android.util.Slog;
import android.view.Display;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -264,6 +265,24 @@
}
@Override
+ public boolean switchToInputMethod(String imeId) {
+ synchronized (mLock) {
+ if (!hasRightsToCurrentUserLocked()) {
+ return false;
+ }
+ }
+ final boolean result;
+ final int callingUserId = UserHandle.getCallingUserId();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ result = InputMethodManagerInternal.get().switchToInputMethod(imeId, callingUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return result;
+ }
+
+ @Override
public boolean isAccessibilityButtonAvailable() {
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 7dd4a70..7a8a112 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -297,6 +297,11 @@
}
@Override
+ public boolean switchToInputMethod(String imeId) {
+ return false;
+ }
+
+ @Override
public boolean isAccessibilityButtonAvailable() {
return false;
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 03d9626..5405fc7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -331,8 +331,8 @@
}
@Override // from SystemService
- public boolean isSupported(UserInfo userInfo) {
- return userInfo.isFull() || userInfo.isManagedProfile();
+ public boolean isSupportedUser(TargetUser user) {
+ return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile();
}
@Override // from SystemService
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f34b5e7..7d354d2 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -162,6 +162,9 @@
/** uid the session is for */
public final int uid;
+ /** user id the session is for */
+ public final int userId;
+
/** ID of the task associated with this session's activity */
public final int taskId;
@@ -613,7 +616,7 @@
int newState, int flags) {
if (isInlineSuggestionsEnabled()) {
mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallbackImpl();
- mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
+ mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(userId,
mComponentName, mCurrentViewId, mInlineSuggestionsRequestCallback);
}
@@ -759,6 +762,7 @@
mFlags = flags;
this.taskId = taskId;
this.uid = uid;
+ this.userId = userId;
mStartTime = SystemClock.elapsedRealtime();
mService = service;
mLock = lock;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d45a54e..b13bef2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1406,10 +1406,7 @@
long oldId = Binder.clearCallingIdentity();
final int[] userIds;
try {
- userIds =
- mContext
- .getSystemService(UserManager.class)
- .getProfileIds(callingUserId, false);
+ userIds = getUserManager().getProfileIds(callingUserId, false);
} 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 eb62620..b06fc52 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -21,12 +21,10 @@
import static com.android.server.backup.BackupManagerService.TAG;
import android.app.backup.RestoreSet;
-import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.EventLog;
import android.util.Pair;
import android.util.Slog;
@@ -40,7 +38,6 @@
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.PerformAdbBackupTask;
-import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.keyvalue.BackupRequest;
import com.android.server.backup.keyvalue.KeyValueBackupTask;
import com.android.server.backup.params.AdbBackupParams;
@@ -73,10 +70,7 @@
public static final int MSG_RESTORE_SESSION_TIMEOUT = 8;
public static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
public static final int MSG_RUN_ADB_RESTORE = 10;
- public static final int MSG_RETRY_INIT = 11;
public static final int MSG_RETRY_CLEAR = 12;
- public static final int MSG_WIDGET_BROADCAST = 13;
- public static final int MSG_RUN_FULL_TRANSPORT_BACKUP = 14;
public static final int MSG_REQUEST_BACKUP = 15;
public static final int MSG_SCHEDULE_BACKUP_PACKAGE = 16;
public static final int MSG_BACKUP_OPERATION_TIMEOUT = 17;
@@ -279,12 +273,6 @@
break;
}
- case MSG_RUN_FULL_TRANSPORT_BACKUP: {
- PerformFullTransportBackupTask task = (PerformFullTransportBackupTask) msg.obj;
- (new Thread(task, "transport-backup")).start();
- break;
- }
-
case MSG_RUN_RESTORE: {
RestoreParams params = (RestoreParams) msg.obj;
Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
@@ -445,12 +433,6 @@
break;
}
- case MSG_WIDGET_BROADCAST: {
- final Intent intent = (Intent) msg.obj;
- backupManagerService.getContext().sendBroadcastAsUser(intent, UserHandle.SYSTEM);
- break;
- }
-
case MSG_REQUEST_BACKUP: {
BackupParams params = (BackupParams) msg.obj;
if (MORE_DEBUG) {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 27824af..8eca62a 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -175,8 +175,8 @@
}
@Override // from SystemService
- public boolean isSupported(UserInfo userInfo) {
- return userInfo.isFull() || userInfo.isManagedProfile();
+ public boolean isSupportedUser(TargetUser user) {
+ return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile();
}
@Override // from SystemService
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 76572d3..368416b 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -41,6 +41,7 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
@@ -478,10 +479,12 @@
* will be disabled. Pass in null or an empty list to disable
* all overlays. The order of the items is significant if several
* overlays modify the same resource.
+ * @param outUpdatedPackageNames An output list that contains the package names of packages
+ * affected by the update of enabled overlays.
* @return true if all packages names were known by the package manager, false otherwise
*/
public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
- List<String> overlayPackageNames);
+ List<String> overlayPackageNames, Collection<String> outUpdatedPackageNames);
/**
* Resolves an activity intent, allowing instant apps to be resolved.
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 73b6c7a..0f2fb92 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -208,6 +208,7 @@
AppWakeupHistory mAppWakeupHistory;
ClockReceiver mClockReceiver;
final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
+ IBinder.DeathRecipient mListenerDeathRecipient;
Intent mTimeTickIntent;
IAlarmListener mTimeTickTrigger;
PendingIntent mDateChangeSender;
@@ -1447,6 +1448,18 @@
public void onStart() {
mInjector.init();
+ mListenerDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ }
+
+ @Override
+ public void binderDied(IBinder who) {
+ final IAlarmListener listener = IAlarmListener.Stub.asInterface(who);
+ removeImpl(null, listener);
+ }
+ };
+
synchronized (mLock) {
mHandler = new AlarmHandler();
mConstants = new Constants(mHandler);
@@ -1653,6 +1666,15 @@
return;
}
+ if (directReceiver != null) {
+ try {
+ directReceiver.asBinder().linkToDeath(mListenerDeathRecipient, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Dropping unreachable alarm listener " + listenerTag);
+ return;
+ }
+ }
+
// Sanity check the window length. This will catch people mistakenly
// trying to pass an end-of-window timestamp rather than a duration.
if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
@@ -2079,8 +2101,9 @@
@Override
public long currentNetworkTimeMillis() {
final NtpTrustedTime time = NtpTrustedTime.getInstance(getContext());
- if (time.hasCache()) {
- return time.currentTimeMillis();
+ NtpTrustedTime.TimeResult ntpResult = time.getCachedTimeResult();
+ if (ntpResult != null) {
+ return ntpResult.currentTimeMillis();
} else {
throw new ParcelableException(new DateTimeException("Missing NTP fix"));
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c2e32d3..c1e23e4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -79,17 +79,15 @@
import android.net.IpMemoryStore;
import android.net.IpPrefix;
import android.net.LinkProperties;
-import android.net.LinkProperties.CompareResult;
import android.net.MatchAllNetworkSpecifier;
import android.net.NattSocketKeepalive;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
-import android.net.NetworkFactory;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkMisc;
import android.net.NetworkMonitorManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkProvider;
@@ -114,6 +112,7 @@
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
import android.net.shared.PrivateDnsConfig;
+import android.net.util.LinkPropertiesUtils.CompareResult;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
import android.os.Binder;
@@ -364,10 +363,10 @@
private static final int EVENT_PROXY_HAS_CHANGED = 16;
/**
- * used internally when registering NetworkFactories
- * obj = NetworkFactoryInfo
+ * used internally when registering NetworkProviders
+ * obj = NetworkProviderInfo
*/
- private static final int EVENT_REGISTER_NETWORK_FACTORY = 17;
+ private static final int EVENT_REGISTER_NETWORK_PROVIDER = 17;
/**
* used internally when registering NetworkAgents
@@ -403,10 +402,10 @@
private static final int EVENT_RELEASE_NETWORK_REQUEST = 22;
/**
- * used internally when registering NetworkFactories
+ * used internally when registering NetworkProviders
* obj = Messenger
*/
- private static final int EVENT_UNREGISTER_NETWORK_FACTORY = 23;
+ private static final int EVENT_UNREGISTER_NETWORK_PROVIDER = 23;
/**
* used internally to expire a wakelock when transitioning
@@ -2394,9 +2393,9 @@
return;
}
- pw.print("NetworkFactories for:");
- for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- pw.print(" " + nfi.name);
+ pw.print("NetworkProviders for:");
+ for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ pw.print(" " + npi.name);
}
pw.println();
pw.println();
@@ -2631,8 +2630,8 @@
if (nai.everConnected) {
loge("ERROR: cannot call explicitlySelected on already-connected network");
}
- nai.networkMisc.explicitlySelected = toBool(msg.arg1);
- nai.networkMisc.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2);
+ nai.networkAgentConfig.explicitlySelected = toBool(msg.arg1);
+ nai.networkAgentConfig.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2);
// Mark the network as temporarily accepting partial connectivity so that it
// will be validated (and possibly become default) even if it only provides
// partial internet access. Note that if user connects to partial connectivity
@@ -2640,7 +2639,7 @@
// out of wifi coverage) and if the same wifi is available again, the device
// will auto connect to this wifi even though the wifi has "no internet".
// TODO: Evaluate using a separate setting in IpMemoryStore.
- nai.networkMisc.acceptPartialConnectivity = toBool(msg.arg2);
+ nai.networkAgentConfig.acceptPartialConnectivity = toBool(msg.arg2);
break;
}
case NetworkAgent.EVENT_SOCKET_KEEPALIVE: {
@@ -2672,10 +2671,10 @@
}
// Only show the notification when the private DNS is broken and the
// PRIVATE_DNS_BROKEN notification hasn't shown since last valid.
- if (privateDnsBroken && !nai.networkMisc.hasShownBroken) {
+ if (privateDnsBroken && !nai.networkAgentConfig.hasShownBroken) {
showNetworkNotification(nai, NotificationType.PRIVATE_DNS_BROKEN);
}
- nai.networkMisc.hasShownBroken = privateDnsBroken;
+ nai.networkAgentConfig.hasShownBroken = privateDnsBroken;
} else if (nai.networkCapabilities.isPrivateDnsBroken()) {
// If probePrivateDnsCompleted is false but nai.networkCapabilities says
// private DNS is broken, it means this network is being reevaluated.
@@ -2685,7 +2684,7 @@
nai.networkCapabilities.setPrivateDnsBroken(false);
final int oldScore = nai.getCurrentScore();
updateCapabilities(oldScore, nai, nai.networkCapabilities);
- nai.networkMisc.hasShownBroken = false;
+ nai.networkAgentConfig.hasShownBroken = false;
}
break;
}
@@ -2727,7 +2726,7 @@
nai.lastValidated = valid;
nai.everValidated |= valid;
updateCapabilities(oldScore, nai, nai.networkCapabilities);
- // If score has changed, rebroadcast to NetworkFactories. b/17726566
+ // If score has changed, rebroadcast to NetworkProviders. b/17726566
if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
if (valid) {
handleFreshlyValidatedNetwork(nai);
@@ -2744,7 +2743,7 @@
// If network becomes valid, the hasShownBroken should be reset for
// that network so that the notification will be fired when the private
// DNS is broken again.
- nai.networkMisc.hasShownBroken = false;
+ nai.networkAgentConfig.hasShownBroken = false;
}
} else if (partialConnectivityChanged) {
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -2803,9 +2802,10 @@
loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
break;
}
- if (!nai.networkMisc.provisioningNotificationDisabled) {
+ if (!nai.networkAgentConfig.provisioningNotificationDisabled) {
mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai, null,
- (PendingIntent) msg.obj, nai.networkMisc.explicitlySelected);
+ (PendingIntent) msg.obj,
+ nai.networkAgentConfig.explicitlySelected);
}
}
break;
@@ -2842,25 +2842,11 @@
return true;
}
- private boolean maybeHandleNetworkFactoryMessage(Message msg) {
- switch (msg.what) {
- default:
- return false;
- case NetworkFactory.EVENT_UNFULFILLABLE_REQUEST: {
- handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.sendingUid,
- /* callOnUnavailable */ true);
- break;
- }
- }
- return true;
- }
-
@Override
public void handleMessage(Message msg) {
if (!maybeHandleAsyncChannelMessage(msg)
&& !maybeHandleNetworkMonitorMessage(msg)
- && !maybeHandleNetworkAgentInfoMessage(msg)
- && !maybeHandleNetworkFactoryMessage(msg)) {
+ && !maybeHandleNetworkAgentInfoMessage(msg)) {
maybeHandleNetworkAgentMessage(msg);
}
}
@@ -3031,16 +3017,16 @@
private void handleAsyncChannelHalfConnect(Message msg) {
ensureRunningOnConnectivityServiceThread();
final AsyncChannel ac = (AsyncChannel) msg.obj;
- if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {
+ if (mNetworkProviderInfos.containsKey(msg.replyTo)) {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
if (VDBG) log("NetworkFactory connected");
// Finish setting up the full connection
- NetworkFactoryInfo nfi = mNetworkFactoryInfos.get(msg.replyTo);
- nfi.completeConnection();
- sendAllRequestsToFactory(nfi);
+ NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
+ npi.completeConnection();
+ sendAllRequestsToProvider(npi);
} else {
loge("Error connecting NetworkFactory");
- mNetworkFactoryInfos.remove(msg.obj);
+ mNetworkProviderInfos.remove(msg.obj);
}
} else if (mNetworkAgentInfos.containsKey(msg.replyTo)) {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
@@ -3072,8 +3058,8 @@
if (nai != null) {
disconnectAndDestroyNetwork(nai);
} else {
- NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(msg.replyTo);
- if (DBG && nfi != null) log("unregisterNetworkFactory for " + nfi.name);
+ NetworkProviderInfo npi = mNetworkProviderInfos.remove(msg.replyTo);
+ if (DBG && npi != null) log("unregisterNetworkFactory for " + npi.name);
}
}
@@ -3156,7 +3142,7 @@
// ip[6]tables to flush routes and remove the incoming packet mark rule, so do it
// after we've rematched networks with requests which should make a potential
// fallback network the default or requested a new network from the
- // NetworkFactories, so network traffic isn't interrupted for an unnecessarily
+ // NetworkProviders, so network traffic isn't interrupted for an unnecessarily
// long time.
destroyNativeNetwork(nai);
mDnsManager.removeNetwork(nai.network);
@@ -3169,8 +3155,8 @@
// This should never fail. Specifying an already in use NetID will cause failure.
if (networkAgent.isVPN()) {
mNetd.networkCreateVpn(networkAgent.network.netId,
- (networkAgent.networkMisc == null
- || !networkAgent.networkMisc.allowBypass));
+ (networkAgent.networkAgentConfig == null
+ || !networkAgent.networkAgentConfig.allowBypass));
} else {
mNetd.networkCreatePhysical(networkAgent.network.netId,
getNetworkPermission(networkAgent.networkCapabilities));
@@ -3419,8 +3405,8 @@
}
}
- for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- nfi.cancelRequest(nri.request);
+ for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ npi.cancelRequest(nri.request);
}
} else {
// listens don't have a singular affectedNetwork. Check all networks to see
@@ -3470,16 +3456,16 @@
return;
}
- if (!nai.networkMisc.explicitlySelected) {
+ if (!nai.networkAgentConfig.explicitlySelected) {
Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
}
- if (accept != nai.networkMisc.acceptUnvalidated) {
- nai.networkMisc.acceptUnvalidated = accept;
+ if (accept != nai.networkAgentConfig.acceptUnvalidated) {
+ nai.networkAgentConfig.acceptUnvalidated = accept;
// If network becomes partial connectivity and user already accepted to use this
// network, we should respect the user's option and don't need to popup the
// PARTIAL_CONNECTIVITY notification to user again.
- nai.networkMisc.acceptPartialConnectivity = accept;
+ nai.networkAgentConfig.acceptPartialConnectivity = accept;
rematchAllNetworksAndRequests();
sendUpdatedScoreToFactories(nai);
}
@@ -3516,8 +3502,8 @@
return;
}
- if (accept != nai.networkMisc.acceptPartialConnectivity) {
- nai.networkMisc.acceptPartialConnectivity = accept;
+ if (accept != nai.networkAgentConfig.acceptPartialConnectivity) {
+ nai.networkAgentConfig.acceptPartialConnectivity = accept;
}
// TODO: Use the current design or save the user choice into IpMemoryStore.
@@ -3615,17 +3601,32 @@
enforceSettingsPermission();
}
- // getNetworkAgentInfoForNetwork is thread-safe
- final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork);
- if (nai == null) return;
-
- // nai.networkMonitor() is thread-safe
- final NetworkMonitorManager nm = nai.networkMonitor();
+ final NetworkMonitorManager nm = getNetworkMonitorManager(mNetwork);
if (nm == null) return;
nm.notifyCaptivePortalAppFinished(response);
}
@Override
+ public void appRequest(final int request) {
+ final NetworkMonitorManager nm = getNetworkMonitorManager(mNetwork);
+ if (nm == null) return;
+
+ if (request == CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED) {
+ nm.forceReevaluation(Binder.getCallingUid());
+ }
+ }
+
+ @Nullable
+ private NetworkMonitorManager getNetworkMonitorManager(final Network network) {
+ // getNetworkAgentInfoForNetwork is thread-safe
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai == null) return null;
+
+ // nai.networkMonitor() is thread-safe
+ return nai.networkMonitor();
+ }
+
+ @Override
public void logEvent(int eventId, String packageName) {
enforceSettingsPermission();
@@ -3727,7 +3728,7 @@
action = ConnectivityManager.ACTION_PROMPT_PARTIAL_CONNECTIVITY;
// Don't bother the user with a high-priority notification if the network was not
// explicitly selected by the user.
- highPriority = nai.networkMisc.explicitlySelected;
+ highPriority = nai.networkAgentConfig.explicitlySelected;
break;
default:
Slog.wtf(TAG, "Unknown notification type " + type);
@@ -3760,14 +3761,15 @@
// automatically connects to a network that has partial Internet access, the user will
// always be able to use it, either because they've already chosen "don't ask again" or
// because we have prompt them.
- if (nai.partialConnectivity && !nai.networkMisc.acceptPartialConnectivity) {
+ if (nai.partialConnectivity && !nai.networkAgentConfig.acceptPartialConnectivity) {
return true;
}
// If a network has no Internet access, only prompt if the network was explicitly selected
// and if the user has not already told us to use the network regardless of whether it
// validated or not.
- if (nai.networkMisc.explicitlySelected && !nai.networkMisc.acceptUnvalidated) {
+ if (nai.networkAgentConfig.explicitlySelected
+ && !nai.networkAgentConfig.acceptUnvalidated) {
return true;
}
@@ -3855,12 +3857,12 @@
handleApplyDefaultProxy((ProxyInfo)msg.obj);
break;
}
- case EVENT_REGISTER_NETWORK_FACTORY: {
- handleRegisterNetworkFactory((NetworkFactoryInfo)msg.obj);
+ case EVENT_REGISTER_NETWORK_PROVIDER: {
+ handleRegisterNetworkProvider((NetworkProviderInfo) msg.obj);
break;
}
- case EVENT_UNREGISTER_NETWORK_FACTORY: {
- handleUnregisterNetworkFactory((Messenger)msg.obj);
+ case EVENT_UNREGISTER_NETWORK_PROVIDER: {
+ handleUnregisterNetworkProvider((Messenger) msg.obj);
break;
}
case EVENT_REGISTER_NETWORK_AGENT: {
@@ -4905,7 +4907,7 @@
}
};
- private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos = new HashMap<>();
+ private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>();
private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>();
private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
@@ -4913,18 +4915,18 @@
@GuardedBy("mUidToNetworkRequestCount")
private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray();
- private static class NetworkFactoryInfo {
+ private static class NetworkProviderInfo {
public final String name;
public final Messenger messenger;
private final AsyncChannel mAsyncChannel;
private final IBinder.DeathRecipient mDeathRecipient;
- public final int factorySerialNumber;
+ public final int providerId;
- NetworkFactoryInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
- int factorySerialNumber, IBinder.DeathRecipient deathRecipient) {
+ NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
+ int providerId, IBinder.DeathRecipient deathRecipient) {
this.name = name;
this.messenger = messenger;
- this.factorySerialNumber = factorySerialNumber;
+ this.providerId = providerId;
mAsyncChannel = asyncChannel;
mDeathRecipient = deathRecipient;
@@ -4942,17 +4944,17 @@
messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
} catch (RemoteException e) {
// Remote process died. Ignore; the death recipient will remove this
- // NetworkFactoryInfo from mNetworkFactoryInfos.
+ // NetworkProviderInfo from mNetworkProviderInfos.
}
}
- void requestNetwork(NetworkRequest request, int score, int servingSerialNumber) {
+ void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
if (isLegacyNetworkFactory()) {
mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
- servingSerialNumber, request);
+ servingProviderId, request);
} else {
sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
- servingSerialNumber, request);
+ servingProviderId, request);
}
}
@@ -5365,45 +5367,45 @@
@Override
public int registerNetworkFactory(Messenger messenger, String name) {
enforceNetworkFactoryPermission();
- NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel(),
+ NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(),
nextNetworkProviderId(), null /* deathRecipient */);
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
- return nfi.factorySerialNumber;
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
+ return npi.providerId;
}
- private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
- if (mNetworkFactoryInfos.containsKey(nfi.messenger)) {
+ private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
+ if (mNetworkProviderInfos.containsKey(npi.messenger)) {
// Avoid creating duplicates. even if an app makes a direct AIDL call.
// This will never happen if an app calls ConnectivityManager#registerNetworkProvider,
// as that will throw if a duplicate provider is registered.
- Slog.e(TAG, "Attempt to register existing NetworkFactoryInfo "
- + mNetworkFactoryInfos.get(nfi.messenger).name);
+ Slog.e(TAG, "Attempt to register existing NetworkProviderInfo "
+ + mNetworkProviderInfos.get(npi.messenger).name);
return;
}
- if (DBG) log("Got NetworkFactory Messenger for " + nfi.name);
- mNetworkFactoryInfos.put(nfi.messenger, nfi);
- nfi.connect(mContext, mTrackerHandler);
- if (!nfi.isLegacyNetworkFactory()) {
+ if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
+ mNetworkProviderInfos.put(npi.messenger, npi);
+ npi.connect(mContext, mTrackerHandler);
+ if (!npi.isLegacyNetworkFactory()) {
// Legacy NetworkFactories get their requests when their AsyncChannel connects.
- sendAllRequestsToFactory(nfi);
+ sendAllRequestsToProvider(npi);
}
}
@Override
public int registerNetworkProvider(Messenger messenger, String name) {
enforceNetworkFactoryPermission();
- NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger,
+ NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
null /* asyncChannel */, nextNetworkProviderId(),
() -> unregisterNetworkProvider(messenger));
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
- return nfi.factorySerialNumber;
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
+ return npi.providerId;
}
@Override
public void unregisterNetworkProvider(Messenger messenger) {
enforceNetworkFactoryPermission();
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_FACTORY, messenger));
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
}
@Override
@@ -5411,13 +5413,13 @@
unregisterNetworkProvider(messenger);
}
- private void handleUnregisterNetworkFactory(Messenger messenger) {
- NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(messenger);
- if (nfi == null) {
- loge("Failed to find Messenger in unregisterNetworkFactory");
+ private void handleUnregisterNetworkProvider(Messenger messenger) {
+ NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger);
+ if (npi == null) {
+ loge("Failed to find Messenger in unregisterNetworkProvider");
return;
}
- if (DBG) log("unregisterNetworkFactory for " + nfi.name);
+ if (DBG) log("unregisterNetworkProvider for " + npi.name);
}
@Override
@@ -5487,11 +5489,14 @@
// changes that would conflict throughout the automerger graph. Having this method temporarily
// helps with the process of going through with all these dependent changes across the entire
// tree.
- public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
+ /**
+ * Register a new agent. {@see #registerNetworkAgent} below.
+ */
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
- int currentScore, NetworkMisc networkMisc) {
+ int currentScore, NetworkAgentConfig networkAgentConfig) {
return registerNetworkAgent(messenger, networkInfo, linkProperties, networkCapabilities,
- currentScore, networkMisc, NetworkFactory.SerialNumber.NONE);
+ currentScore, networkAgentConfig, NetworkProvider.ID_NONE);
}
/**
@@ -5506,12 +5511,13 @@
* later : see {@link #updateCapabilities}.
* @param currentScore the initial score of the network. See
* {@link NetworkAgentInfo#getCurrentScore}.
- * @param networkMisc metadata about the network. This is never updated.
- * @param factorySerialNumber the serial number of the factory owning this NetworkAgent.
+ * @param networkAgentConfig metadata about the network. This is never updated.
+ * @param providerId the ID of the provider owning this NetworkAgent.
+ * @return the network created for this agent.
*/
- public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
- int currentScore, NetworkMisc networkMisc, int factorySerialNumber) {
+ int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
enforceNetworkFactoryPermission();
LinkProperties lp = new LinkProperties(linkProperties);
@@ -5523,8 +5529,8 @@
ns.putIntExtension(NetworkScore.LEGACY_SCORE, currentScore);
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
- ns, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd,
- mDnsResolver, mNMS, factorySerialNumber);
+ ns, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), this,
+ mNetd, mDnsResolver, mNMS, providerId);
// Make sure the network capabilities reflect what the agent info says.
nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
final String extraInfo = networkInfo.getExtraInfo();
@@ -5542,7 +5548,7 @@
// If the network disconnects or sends any other event before that, messages are deferred by
// NetworkAgent until nai.asyncChannel.connect(), which will be called when finalizing the
// registration.
- return nai.network.netId;
+ return nai.network;
}
private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
@@ -5803,7 +5809,7 @@
// Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
// Don't complain for VPNs since they're not driven by requests and there is no risk of
// causing a connect/teardown loop.
- // TODO: remove this altogether and make it the responsibility of the NetworkFactories to
+ // TODO: remove this altogether and make it the responsibility of the NetworkProviders to
// avoid connect/teardown loops.
if (nai.everConnected &&
!nai.isVPN() &&
@@ -5945,7 +5951,7 @@
LinkProperties lp) {
if (nc == null || lp == null) return false;
return nai.isVPN()
- && !nai.networkMisc.allowBypass
+ && !nai.networkAgentConfig.allowBypass
&& nc.getEstablishingVpnAppUid() != Process.SYSTEM_UID
&& lp.getInterfaceName() != null
&& (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute());
@@ -6043,13 +6049,13 @@
if (VDBG || DDBG){
log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
}
- for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- nfi.requestNetwork(networkRequest, score, serial);
+ for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ npi.requestNetwork(networkRequest, score, serial);
}
}
/** Sends all current NetworkRequests to the specified factory. */
- private void sendAllRequestsToFactory(NetworkFactoryInfo nfi) {
+ private void sendAllRequestsToProvider(NetworkProviderInfo npi) {
ensureRunningOnConnectivityServiceThread();
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (nri.request.isListen()) continue;
@@ -6061,9 +6067,9 @@
serial = nai.factorySerialNumber;
} else {
score = 0;
- serial = NetworkFactory.SerialNumber.NONE;
+ serial = NetworkProvider.ID_NONE;
}
- nfi.requestNetwork(nri.request, score, serial);
+ npi.requestNetwork(nri.request, score, serial);
}
}
@@ -6344,11 +6350,11 @@
Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
}
addedRequests.add(nri);
- // Tell NetworkFactories about the new score, so they can stop
+ // Tell NetworkProviders about the new score, so they can stop
// trying to connect if they know they cannot match it.
// TODO - this could get expensive if we have a lot of requests for this
// network. Think about if there is a way to reduce this. Push
- // netid->request mapping to each factory?
+ // netid->request mapping to each provider?
sendUpdatedScoreToFactories(nri.request, newSatisfier);
if (isDefaultRequest(nri)) {
isNewDefault = true;
@@ -6377,7 +6383,7 @@
} else {
Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
newNetwork.name() +
- " without updating mSatisfier or factories!");
+ " without updating mSatisfier or providers!");
}
// TODO: Technically, sending CALLBACK_LOST here is
// incorrect if there is a replacement network currently
@@ -6636,7 +6642,7 @@
// command must be sent after updating LinkProperties to maximize chances of
// NetworkMonitor seeing the correct LinkProperties when starting.
// TODO: pass LinkProperties to the NetworkMonitor in the notifyNetworkConnected call.
- if (networkAgent.networkMisc.acceptPartialConnectivity) {
+ if (networkAgent.networkAgentConfig.acceptPartialConnectivity) {
networkAgent.networkMonitor().setAcceptPartialConnectivity();
}
networkAgent.networkMonitor().notifyNetworkConnected(
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index b26ef92..d1d1cb3 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -523,7 +523,7 @@
@Override
public void accept(INetworkScoreCache networkScoreCache, Object cookie) {
- int filterType = NetworkScoreManager.CACHE_FILTER_NONE;
+ int filterType = NetworkScoreManager.SCORE_FILTER_NONE;
if (cookie instanceof Integer) {
filterType = (Integer) cookie;
}
@@ -547,17 +547,17 @@
private List<ScoredNetwork> filterScores(List<ScoredNetwork> scoredNetworkList,
int filterType) {
switch (filterType) {
- case NetworkScoreManager.CACHE_FILTER_NONE:
+ case NetworkScoreManager.SCORE_FILTER_NONE:
return scoredNetworkList;
- case NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK:
+ case NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK:
if (mCurrentNetworkFilter == null) {
mCurrentNetworkFilter =
new CurrentNetworkScoreCacheFilter(new WifiInfoSupplier(mContext));
}
return mCurrentNetworkFilter.apply(scoredNetworkList);
- case NetworkScoreManager.CACHE_FILTER_SCAN_RESULTS:
+ case NetworkScoreManager.SCORE_FILTER_SCAN_RESULTS:
if (mScanResultsFilter == null) {
mScanResultsFilter = new ScanResultsScoreCacheFilter(
new ScanResultsSupplier(mContext));
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
index d20936c..7894788 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
@@ -154,17 +154,20 @@
private void onPollNetworkTimeUnderWakeLock(int event) {
// Force an NTP fix when outdated
- if (mTime.getCacheAge() >= mPollingIntervalMs) {
+ NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
+ if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
mTime.forceRefresh();
+ cachedNtpResult = mTime.getCachedTimeResult();
}
- if (mTime.getCacheAge() < mPollingIntervalMs) {
+ if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
// Obtained fresh fix; schedule next normal update
resetAlarm(mPollingIntervalMs);
// Suggest the time to the time detector. It may choose use it to set the system clock.
- TimestampedValue<Long> timeSignal = mTime.getCachedNtpTimeSignal();
+ TimestampedValue<Long> timeSignal = new TimestampedValue<>(
+ cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateServiceImpl. event=" + event);
mTimeDetector.suggestNetworkTime(timeSuggestion);
@@ -275,8 +278,11 @@
TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
pw.println("\nTryAgainCounter: " + mTryAgainCounter);
- pw.println("NTP cache age: " + mTime.getCacheAge());
- pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
+ NtpTrustedTime.TimeResult ntpResult = mTime.getCachedTimeResult();
+ pw.println("NTP cache result: " + ntpResult);
+ if (ntpResult != null) {
+ pw.println("NTP result age: " + ntpResult.getAgeMillis());
+ }
pw.println();
}
}
diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
index 190fff1..21fa9f9 100644
--- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
+++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
@@ -46,4 +46,7 @@
/** Update the OEM unlock enabled bit, bypassing user restriction checks. */
void forceOemUnlockEnabled(boolean enabled);
+
+ /** Retrieves the UID that can access the persistent data partition. */
+ int getAllowedUid();
}
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 73c8520..00d8b0f 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -680,6 +680,11 @@
writeDataBuffer(getTestHarnessModeDataOffset(), ByteBuffer.allocate(size));
}
+ @Override
+ public int getAllowedUid() {
+ return mAllowedUid;
+ }
+
private void writeInternal(byte[] data, long offset, int dataLength) {
checkArgument(data == null || data.length > 0, "data must be null or non-empty");
checkArgument(
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index c5409f8..f46b9ae 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -18,18 +18,23 @@
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.IBinder;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.os.UserManager;
import com.android.server.pm.UserManagerService;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -57,15 +62,17 @@
*
* {@hide}
*/
+//@SystemApi(client = Client.MODULE_LIBRARIES, process = Process.SYSTEM_SERVER)
public abstract class SystemService {
+ /** @hide */
// TODO(b/133242016) STOPSHIP: change to false before R ships
protected static final boolean DEBUG_USER = true;
/*
- * Boot Phases
+ * The earliest boot phase the system send to system services on boot.
*/
- public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; // maybe should be a dependency?
+ public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100;
/**
* After receiving this boot phase, services can obtain lock settings data.
@@ -98,13 +105,70 @@
* After receiving this boot phase, services can allow user interaction with the device.
* This phase occurs when boot has completed and the home application has started.
* System services may prefer to listen to this phase rather than registering a
- * broadcast receiver for ACTION_BOOT_COMPLETED to reduce overall latency.
+ * broadcast receiver for {@link android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED}
+ * to reduce overall latency.
*/
public static final int PHASE_BOOT_COMPLETED = 1000;
+ /** @hide */
+ @IntDef(flag = true, prefix = { "PHASE_" }, value = {
+ PHASE_WAIT_FOR_DEFAULT_DISPLAY,
+ PHASE_LOCK_SETTINGS_READY,
+ PHASE_SYSTEM_SERVICES_READY,
+ PHASE_DEVICE_SPECIFIC_SERVICES_READY,
+ PHASE_ACTIVITY_MANAGER_READY,
+ PHASE_THIRD_PARTY_APPS_CAN_START,
+ PHASE_BOOT_COMPLETED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BootPhase {}
+
private final Context mContext;
/**
+ * Class representing user in question in the lifecycle callbacks.
+ * @hide
+ */
+ //@SystemApi(client = Client.MODULE_LIBRARIES, process = Process.SYSTEM_SERVER)
+ public static final class TargetUser {
+ @NonNull
+ private final UserInfo mUserInfo;
+
+ /** @hide */
+ public TargetUser(@NonNull UserInfo userInfo) {
+ mUserInfo = userInfo;
+ }
+
+ /**
+ * @return The information about the user. <b>NOTE: </b> this is a "live" object
+ * referenced by {@link UserManagerService} and hence should not be modified.
+ *
+ * @hide
+ */
+ @NonNull
+ public UserInfo getUserInfo() {
+ return mUserInfo;
+ }
+
+ /**
+ * @return the target {@link UserHandle}.
+ */
+ @NonNull
+ public UserHandle getUserHandle() {
+ return mUserInfo.getUserHandle();
+ }
+
+ /**
+ * @return the integer user id
+ *
+ * @hide
+ */
+ public int getUserIdentifier() {
+ return mUserInfo.id;
+ }
+ }
+
+ /**
* Initializes the system service.
* <p>
* Subclasses must define a single argument constructor that accepts the context
@@ -113,13 +177,14 @@
*
* @param context The system server context.
*/
- public SystemService(Context context) {
+ public SystemService(@NonNull Context context) {
mContext = context;
}
/**
* Gets the system context.
*/
+ @NonNull
public final Context getContext() {
return mContext;
}
@@ -128,6 +193,8 @@
* Get the system UI context. This context is to be used for displaying UI. It is themable,
* which means resources can be overridden at runtime. Do not use to retrieve properties that
* configure the behavior of the device that is not UX related.
+ *
+ * @hide
*/
public final Context getUiContext() {
// This has already been set up by the time any SystemServices are created.
@@ -137,15 +204,16 @@
/**
* Returns true if the system is running in safe mode.
* TODO: we should define in which phase this becomes valid
+ *
+ * @hide
*/
public final boolean isSafeMode() {
return getManager().isSafeMode();
}
/**
- * Called when the dependencies listed in the @Service class-annotation are available
- * and after the chosen start phase.
- * When this method returns, the service should be published.
+ * Called when the system service should publish a binder service using
+ * {@link #publishBinderService(String, IBinder).}
*/
public abstract void onStart();
@@ -155,7 +223,7 @@
*
* @param phase The current boot phase.
*/
- public void onBootPhase(int phase) {}
+ public void onBootPhase(@BootPhase int phase) {}
/**
* Checks if the service should be available for the given user.
@@ -163,12 +231,14 @@
* <p>By default returns {@code true}, but subclasses should extend for optimization, if they
* don't support some types (like headless system user).
*/
- public boolean isSupported(@NonNull UserInfo userInfo) {
+ public boolean isSupportedUser(@NonNull TargetUser user) {
return true;
}
/**
- * Helper method used to dump which users are {@link #onStartUser(UserInfo) supported}.
+ * Helper method used to dump which users are {@link #onStartUser(TargetUser) supported}.
+ *
+ * @hide
*/
protected void dumpSupportedUsers(@NonNull PrintWriter pw, @NonNull String prefix) {
final List<UserInfo> allUsers = UserManager.get(mContext).getUsers();
@@ -187,34 +257,59 @@
}
/**
- * @deprecated subclasses should extend {@link #onStartUser(UserInfo)} instead (which by default
- * calls this method).
+ * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead
+ * (which by default calls this method).
+ *
+ * @hide
*/
@Deprecated
public void onStartUser(@UserIdInt int userId) {}
/**
- * Called when a new user is starting, for system services to initialize any per-user
- * state they maintain for running users.
+ * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead
+ * (which by default calls this method).
*
- * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
- * user.
- *
- * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
- * referenced by {@link UserManagerService} and hence should not be modified.
+ * @hide
*/
+ @Deprecated
public void onStartUser(@NonNull UserInfo userInfo) {
onStartUser(userInfo.id);
}
/**
- * @deprecated subclasses should extend {@link #onUnlockUser(UserInfo)} instead (which by
+ * Called when a new user is starting, for system services to initialize any per-user
+ * state they maintain for running users.
+ *
+ * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+ * this user.
+ *
+ * @param user target user
+ */
+ public void onStartUser(@NonNull TargetUser user) {
+ onStartUser(user.getUserInfo());
+ }
+
+ /**
+ * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by
* default calls this method).
+ *
+ * @hide
*/
@Deprecated
public void onUnlockUser(@UserIdInt int userId) {}
/**
+ * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by
+ * default calls this method).
+ *
+ * @hide
+ */
+ @Deprecated
+ public void onUnlockUser(@NonNull UserInfo userInfo) {
+ onUnlockUser(userInfo.id);
+ }
+
+ /**
* Called when an existing user is in the process of being unlocked. This
* means the credential-encrypted storage for that user is now available,
* and encryption-aware component filtering is no longer in effect.
@@ -226,90 +321,127 @@
* {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of
* these states.
* <p>
- * This method is only called when the service {@link #isSupported(UserInfo) supports} this
- * user.
+ * This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+ * this user.
*
- * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
- * referenced by {@link UserManagerService} and hence should not be modified.
+ * @param user target user
*/
- public void onUnlockUser(@NonNull UserInfo userInfo) {
- onUnlockUser(userInfo.id);
+ public void onUnlockUser(@NonNull TargetUser user) {
+ onUnlockUser(user.getUserInfo());
}
/**
- * @deprecated subclasses should extend {@link #onSwitchUser(UserInfo, UserInfo)} instead
+ * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead
* (which by default calls this method).
+ *
+ * @hide
*/
@Deprecated
- public void onSwitchUser(@UserIdInt int userId) {}
+ public void onSwitchUser(@UserIdInt int toUserId) {}
+
+ /**
+ * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead
+ * (which by default calls this method).
+ *
+ * @hide
+ */
+ @Deprecated
+ public void onSwitchUser(@Nullable UserInfo from, @NonNull UserInfo to) {
+ onSwitchUser(to.id);
+ }
/**
* Called when switching to a different foreground user, for system services that have
* special behavior for whichever user is currently in the foreground. This is called
* before any application processes are aware of the new user.
*
- * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} either
- * of the users ({@code from} or {@code to}).
+ * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+ * either of the users ({@code from} or {@code to}).
*
* <b>NOTE: </b> both {@code from} and {@code to} are "live" objects
* referenced by {@link UserManagerService} and hence should not be modified.
*
- * @param from The information about the user being switched from.
- * @param to The information about the user being switched from to.
+ * @param from the user switching from
+ * @param to the user switching to
*/
- public void onSwitchUser(@NonNull UserInfo from, @NonNull UserInfo to) {
- onSwitchUser(to.id);
+ public void onSwitchUser(@Nullable TargetUser from, @NonNull TargetUser to) {
+ onSwitchUser((from == null ? null : from.getUserInfo()), to.getUserInfo());
}
/**
- * @deprecated subclasses should extend {@link #onStopUser(UserInfo)} instead (which by default
- * calls this method).
+ * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead
+ * (which by default calls this method).
+ *
+ * @hide
*/
@Deprecated
public void onStopUser(@UserIdInt int userId) {}
/**
+ * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead
+ * (which by default calls this method).
+ *
+ * @hide
+ */
+ @Deprecated
+ public void onStopUser(@NonNull UserInfo user) {
+ onStopUser(user.id);
+
+ }
+
+ /**
* Called when an existing user is stopping, for system services to finalize any per-user
* state they maintain for running users. This is called prior to sending the SHUTDOWN
* broadcast to the user; it is a good place to stop making use of any resources of that
* user (such as binding to a service running in the user).
*
- * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
- * user.
+ * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+ * this user.
*
* <p>NOTE: This is the last callback where the callee may access the target user's CE storage.
*
- * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
- * referenced by {@link UserManagerService} and hence should not be modified.
+ * @param user target user
*/
- public void onStopUser(@NonNull UserInfo userInfo) {
- onStopUser(userInfo.id);
+ public void onStopUser(@NonNull TargetUser user) {
+ onStopUser(user.getUserInfo());
}
/**
- * @deprecated subclasses should extend {@link #onCleanupUser(UserInfo)} instead (which by
+ * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by
* default calls this method).
+ *
+ * @hide
*/
@Deprecated
public void onCleanupUser(@UserIdInt int userId) {}
/**
+ * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by
+ * default calls this method).
+ *
+ * @hide
+ */
+ @Deprecated
+ public void onCleanupUser(@NonNull UserInfo user) {
+ onCleanupUser(user.id);
+ }
+
+ /**
* Called when an existing user is stopping, for system services to finalize any per-user
* state they maintain for running users. This is called after all application process
* teardown of the user is complete.
*
- * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
- * user.
+ * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+ * this user.
*
* <p>NOTE: When this callback is called, the CE storage for the target user may not be
- * accessible already. Use {@link #onStopUser(UserInfo)} instead if you need to access the CE
+ * accessible already. Use {@link #onStopUser(TargetUser)} instead if you need to access the CE
* storage.
*
- * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
- * referenced by {@link UserManagerService} and hence should not be modified.
+ * @param user target user
*/
- public void onCleanupUser(@NonNull UserInfo userInfo) {
- onCleanupUser(userInfo.id);
+ public void onCleanupUser(@NonNull TargetUser user) {
+ onCleanupUser(user.getUserInfo());
}
/**
@@ -318,7 +450,7 @@
* @param name the name of the new service
* @param service the service object
*/
- protected final void publishBinderService(String name, IBinder service) {
+ protected final void publishBinderService(@NonNull String name, @NonNull IBinder service) {
publishBinderService(name, service, false);
}
@@ -330,7 +462,7 @@
* @param allowIsolated set to true to allow isolated sandboxed processes
* to access this service
*/
- protected final void publishBinderService(String name, IBinder service,
+ protected final void publishBinderService(@NonNull String name, @NonNull IBinder service,
boolean allowIsolated) {
publishBinderService(name, service, allowIsolated, DUMP_FLAG_PRIORITY_DEFAULT);
}
@@ -343,6 +475,8 @@
* @param allowIsolated set to true to allow isolated sandboxed processes
* to access this service
* @param dumpPriority supported dump priority levels as a bitmask
+ *
+ * @hide
*/
protected final void publishBinderService(String name, IBinder service,
boolean allowIsolated, int dumpPriority) {
@@ -351,6 +485,8 @@
/**
* Get a binder service by its name.
+ *
+ * @hide
*/
protected final IBinder getBinderService(String name) {
return ServiceManager.getService(name);
@@ -358,6 +494,8 @@
/**
* Publish the service so it is only accessible to the system process.
+ *
+ * @hide
*/
protected final <T> void publishLocalService(Class<T> type, T service) {
LocalServices.addService(type, service);
@@ -365,6 +503,8 @@
/**
* Get a local service by interface.
+ *
+ * @hide
*/
protected final <T> T getLocalService(Class<T> type) {
return LocalServices.getService(type);
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index c715798..c0f43a8 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -27,6 +27,7 @@
import android.os.UserManagerInternal;
import android.util.Slog;
+import com.android.server.SystemService.TargetUser;
import com.android.server.utils.TimingsTraceAndSlog;
import java.io.File;
@@ -264,26 +265,26 @@
@UserIdInt int curUserId, @UserIdInt int prevUserId) {
t.traceBegin("ssm." + onWhat + "User-" + curUserId);
Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId);
- final UserInfo curUserInfo = getUserInfo(curUserId);
- final UserInfo prevUserInfo = prevUserId == UserHandle.USER_NULL ? null
- : getUserInfo(prevUserId);
+ final TargetUser curUser = new TargetUser(getUserInfo(curUserId));
+ final TargetUser prevUser = prevUserId == UserHandle.USER_NULL ? null
+ : new TargetUser(getUserInfo(prevUserId));
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
final String serviceName = service.getClass().getName();
- boolean supported = service.isSupported(curUserInfo);
+ boolean supported = service.isSupportedUser(curUser);
// Must check if either curUser or prevUser is supported (for example, if switching from
// unsupported to supported, we still need to notify the services)
- if (!supported && prevUserInfo != null) {
- supported = service.isSupported(prevUserInfo);
+ if (!supported && prevUser != null) {
+ supported = service.isSupportedUser(prevUser);
}
if (!supported) {
if (DEBUG) {
Slog.d(TAG, "Skipping " + onWhat + "User-" + curUserId + " on service "
+ serviceName + " because it's not supported (curUser: "
- + curUserInfo + ", prevUser:" + prevUserInfo + ")");
+ + curUser + ", prevUser:" + prevUser + ")");
} else {
Slog.i(TAG, "Skipping " + onWhat + "User-" + curUserId + " on "
+ serviceName);
@@ -295,25 +296,25 @@
try {
switch (onWhat) {
case SWITCH:
- service.onSwitchUser(prevUserInfo, curUserInfo);
+ service.onSwitchUser(prevUser, curUser);
break;
case START:
- service.onStartUser(curUserInfo);
+ service.onStartUser(curUser);
break;
case UNLOCK:
- service.onUnlockUser(curUserInfo);
+ service.onUnlockUser(curUser);
break;
case STOP:
- service.onStopUser(curUserInfo);
+ service.onStopUser(curUser);
break;
case CLEANUP:
- service.onCleanupUser(curUserInfo);
+ service.onCleanupUser(curUser);
break;
default:
throw new IllegalArgumentException(onWhat + " what?");
}
} catch (Exception ex) {
- Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUserInfo
+ Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUser
+ " to service " + serviceName, ex);
}
warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index c27b0da..ed3bab9 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -218,7 +218,7 @@
// Has to be in TestNetworkAgent to ensure all teardown codepaths properly clean up
// resources, even for binder death or unwanted calls.
synchronized (mTestNetworkTracker) {
- mTestNetworkTracker.remove(netId);
+ mTestNetworkTracker.remove(network.netId);
}
}
}
@@ -337,7 +337,7 @@
callingUid,
binder);
- mTestNetworkTracker.put(agent.netId, agent);
+ mTestNetworkTracker.put(agent.network.netId, agent);
}
} catch (SocketException e) {
throw new UncheckedIOException(e);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a372fca0..debc2a1 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2129,16 +2129,6 @@
}
@Override
- public void removeAccount(IAccountManagerResponse response, Account account,
- boolean expectActivityLaunch) {
- removeAccountAsUser(
- response,
- account,
- expectActivityLaunch,
- UserHandle.getCallingUserId());
- }
-
- @Override
public void removeAccountAsUser(IAccountManagerResponse response, Account account,
boolean expectActivityLaunch, int userId) {
final int callingUid = Binder.getCallingUid();
@@ -4454,12 +4444,6 @@
@Override
@NonNull
- public Account[] getAccounts(String type, String opPackageName) {
- return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
- }
-
- @Override
- @NonNull
public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
int callingUid = Binder.getCallingUid();
if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c21adb0..ea238f2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9108,13 +9108,14 @@
final Resources res = mContext.getResources();
mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
com.android.internal.R.string.config_appsNotReportingCrashes));
- mUserController.mUserSwitchUiEnabled = !res.getBoolean(
+ final boolean userSwitchUiEnabled = !res.getBoolean(
com.android.internal.R.bool.config_customUserSwitchUi);
- mUserController.mMaxRunningUsers = res.getInteger(
+ final int maxRunningUsers = res.getInteger(
com.android.internal.R.integer.config_multiuserMaxRunningUsers);
- mUserController.mDelayUserDataLocking = res.getBoolean(
+ final boolean delayUserDataLocking = res.getBoolean(
com.android.internal.R.bool.config_multiuserDelayUserDataLocking);
-
+ mUserController.setInitialConfig(userSwitchUiEnabled, maxRunningUsers,
+ delayUserDataLocking);
mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
mPssDeferralTime = pssDeferralMs;
}
@@ -16408,6 +16409,22 @@
}
@Override
+ public boolean updateMccMncConfiguration(String mcc, String mnc) {
+ int mccInt, mncInt;
+ try {
+ mccInt = Integer.parseInt(mcc);
+ mncInt = Integer.parseInt(mnc);
+ } catch (NumberFormatException | StringIndexOutOfBoundsException ex) {
+ Slog.e(TAG, "Error parsing mcc: " + mcc + " mnc: " + mnc + ". ex=" + ex);
+ return false;
+ }
+ Configuration config = new Configuration();
+ config.mcc = mccInt;
+ config.mnc = mncInt == 0 ? Configuration.MNC_ZERO : mncInt;
+ return mActivityTaskManager.updateConfiguration(config);
+ }
+
+ @Override
public int getLaunchedFromUid(IBinder activityToken) {
return mActivityTaskManager.getLaunchedFromUid(activityToken);
}
@@ -17926,7 +17943,31 @@
@Override
public int stopUser(final int userId, boolean force, final IStopUserCallback callback) {
- return mUserController.stopUser(userId, force, callback, null /* keyEvictedCallback */);
+ return mUserController.stopUser(userId, force, /* allowDelayedLocking= */ false,
+ /* callback= */ callback, /* keyEvictedCallback= */ null);
+ }
+
+ /**
+ * Stops user but allow delayed locking. Delayed locking keeps user unlocked even after
+ * stopping only if {@code config_multiuserDelayUserDataLocking} overlay is set true.
+ *
+ * <p>When delayed locking is not enabled through the overlay, this call becomes the same
+ * with {@link #stopUser(int, boolean, IStopUserCallback)} call.
+ *
+ * @param userId User id to stop.
+ * @param force Force stop the user even if the user is related with system user or current
+ * user.
+ * @param callback Callback called when user has stopped.
+ *
+ * @return {@link ActivityManager#USER_OP_SUCCESS} when user is stopped successfully. Returns
+ * other {@code ActivityManager#USER_OP_*} codes for failure.
+ *
+ */
+ @Override
+ public int stopUserWithDelayedLocking(final int userId, boolean force,
+ final IStopUserCallback callback) {
+ return mUserController.stopUser(userId, force, /* allowDelayedLocking= */ true,
+ /* callback= */ callback, /* keyEvictedCallback= */ null);
}
@Override
@@ -18332,7 +18373,7 @@
@Override
public int getMaxRunningUsers() {
- return mUserController.mMaxRunningUsers;
+ return mUserController.getMaxRunningUsers();
}
@Override
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b9f2e76..e11008c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1562,8 +1562,7 @@
throw e.rethrowAsRuntimeException();
}
int numGids = 3;
- if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER
- || mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+ if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
numGids++;
}
/*
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 2b20782..f3a2e70 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -93,7 +93,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -161,7 +160,8 @@
* <p>Note: Current and system user (and their related profiles) are never stopped when
* switching users. Due to that, the actual number of running users can exceed mMaxRunningUsers
*/
- int mMaxRunningUsers;
+ @GuardedBy("mLock")
+ private int mMaxRunningUsers;
// Lock for internal state.
private final Object mLock = new Object();
@@ -213,7 +213,8 @@
private final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
= new RemoteCallbackList<>();
- boolean mUserSwitchUiEnabled = true;
+ @GuardedBy("mLock")
+ private boolean mUserSwitchUiEnabled = true;
/**
* Currently active user switch callbacks.
@@ -246,10 +247,11 @@
/**
* In this mode, user is always stopped when switched out but locking of user data is
* postponed until total number of unlocked users in the system reaches mMaxRunningUsers.
- * Once total number of unlocked users reach mMaxRunningUsers, least recentely used user
+ * Once total number of unlocked users reach mMaxRunningUsers, least recently used user
* will be locked.
*/
- boolean mDelayUserDataLocking;
+ @GuardedBy("mLock")
+ private boolean mDelayUserDataLocking;
/**
* Keep track of last active users for mDelayUserDataLocking.
* The latest stopped user is placed in front while the least recently stopped user in back.
@@ -275,6 +277,33 @@
updateStartedUserArrayLU();
}
+ void setInitialConfig(boolean userSwitchUiEnabled, int maxRunningUsers,
+ boolean delayUserDataLocking) {
+ synchronized (mLock) {
+ mUserSwitchUiEnabled = userSwitchUiEnabled;
+ mMaxRunningUsers = maxRunningUsers;
+ mDelayUserDataLocking = delayUserDataLocking;
+ }
+ }
+
+ private boolean isUserSwitchUiEnabled() {
+ synchronized (mLock) {
+ return mUserSwitchUiEnabled;
+ }
+ }
+
+ int getMaxRunningUsers() {
+ synchronized (mLock) {
+ return mMaxRunningUsers;
+ }
+ }
+
+ private boolean isDelayUserDataLockingEnabled() {
+ synchronized (mLock) {
+ return mDelayUserDataLocking;
+ }
+ }
+
void finishUserSwitch(UserState uss) {
// This call holds the AM lock so we post to the handler.
mHandler.post(() -> {
@@ -321,7 +350,11 @@
// Owner/System user and current user can't be stopped
continue;
}
- if (stopUsersLU(userId, false, null, null) == USER_OP_SUCCESS) {
+ // allowDelayedLocking set here as stopping user is done without any explicit request
+ // from outside.
+ if (stopUsersLU(userId, /* force= */ false, /* allowDelayedLocking= */ true,
+ /* stopUserCallback= */ null, /* keyEvictedCallback= */ null)
+ == USER_OP_SUCCESS) {
iterator.remove();
}
}
@@ -567,8 +600,8 @@
// intialize it; it should be stopped right away as it's not really a "real" user.
// TODO(b/143092698): in the long-term, it might be better to add a onCreateUser()
// callback on SystemService instead.
- stopUser(userInfo.id, /* force= */ true, /* stopUserCallback= */ null,
- /* keyEvictedCallback= */ null);
+ stopUser(userInfo.id, /* force= */ true, /* allowDelayedLocking= */ false,
+ /* stopUserCallback= */ null, /* keyEvictedCallback= */ null);
return;
}
@@ -611,7 +644,8 @@
}
int restartUser(final int userId, final boolean foreground) {
- return stopUser(userId, /* force */ true, null, new KeyEvictedCallback() {
+ return stopUser(userId, /* force= */ true, /* allowDelayedLocking= */ false,
+ /* stopUserCallback= */ null, new KeyEvictedCallback() {
@Override
public void keyEvicted(@UserIdInt int userId) {
// Post to the same handler that this callback is called from to ensure the user
@@ -621,15 +655,16 @@
});
}
- int stopUser(final int userId, final boolean force, final IStopUserCallback stopUserCallback,
- KeyEvictedCallback keyEvictedCallback) {
+ int stopUser(final int userId, final boolean force, boolean allowDelayedLocking,
+ final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) {
checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "stopUser");
if (userId < 0 || userId == UserHandle.USER_SYSTEM) {
throw new IllegalArgumentException("Can't stop system user " + userId);
}
enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
synchronized (mLock) {
- return stopUsersLU(userId, force, stopUserCallback, keyEvictedCallback);
+ return stopUsersLU(userId, force, allowDelayedLocking, stopUserCallback,
+ keyEvictedCallback);
}
}
@@ -638,7 +673,7 @@
* {@link #getUsersToStopLU(int)} to determine the list of users that should be stopped.
*/
@GuardedBy("mLock")
- private int stopUsersLU(final int userId, boolean force,
+ private int stopUsersLU(final int userId, boolean force, boolean allowDelayedLocking,
final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) {
if (userId == UserHandle.USER_SYSTEM) {
return USER_OP_ERROR_IS_SYSTEM;
@@ -657,7 +692,8 @@
if (force) {
Slog.i(TAG,
"Force stop user " + userId + ". Related users will not be stopped");
- stopSingleUserLU(userId, stopUserCallback, keyEvictedCallback);
+ stopSingleUserLU(userId, allowDelayedLocking, stopUserCallback,
+ keyEvictedCallback);
return USER_OP_SUCCESS;
}
return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
@@ -665,21 +701,64 @@
}
if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
for (int userIdToStop : usersToStop) {
- stopSingleUserLU(userIdToStop,
+ stopSingleUserLU(userIdToStop, allowDelayedLocking,
userIdToStop == userId ? stopUserCallback : null,
userIdToStop == userId ? keyEvictedCallback : null);
}
return USER_OP_SUCCESS;
}
+ /**
+ * Stops a single User. This can also trigger locking user data out depending on device's
+ * config ({@code mDelayUserDataLocking}) and arguments.
+ * User will be unlocked when
+ * - {@code mDelayUserDataLocking} is not set.
+ * - {@code mDelayUserDataLocking} is set and {@code keyEvictedCallback} is non-null.
+ * -
+ *
+ * @param userId User Id to stop and lock the data.
+ * @param allowDelayedLocking When set, do not lock user after stopping. Locking can happen
+ * later when number of unlocked users reaches
+ * {@code mMaxRunnngUsers}. Note that this is respected only when
+ * {@code mDelayUserDataLocking} is set and {@keyEvictedCallback} is
+ * null. Otherwise the user will be locked.
+ * @param stopUserCallback Callback to notify that user has stopped.
+ * @param keyEvictedCallback Callback to notify that user has been unlocked.
+ */
@GuardedBy("mLock")
- private void stopSingleUserLU(final int userId, final IStopUserCallback stopUserCallback,
+ private void stopSingleUserLU(final int userId, boolean allowDelayedLocking,
+ final IStopUserCallback stopUserCallback,
KeyEvictedCallback keyEvictedCallback) {
if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId);
final UserState uss = mStartedUsers.get(userId);
- if (uss == null) {
- // User is not started, nothing to do... but we do need to
- // callback if requested.
+ if (uss == null) { // User is not started
+ // If mDelayUserDataLocking is set and allowDelayedLocking is not set, we need to lock
+ // the requested user as the client wants to stop and lock the user. On the other hand,
+ // having keyEvictedCallback set will lead into locking user if mDelayUserDataLocking
+ // is set as that means client wants to lock the user immediately.
+ // If mDelayUserDataLocking is not set, the user was already locked when it was stopped
+ // and no further action is necessary.
+ if (mDelayUserDataLocking) {
+ if (allowDelayedLocking && keyEvictedCallback != null) {
+ Slog.wtf(TAG, "allowDelayedLocking set with KeyEvictedCallback, ignore it"
+ + " and lock user:" + userId, new RuntimeException());
+ allowDelayedLocking = false;
+ }
+ if (!allowDelayedLocking) {
+ if (mLastActiveUsers.remove(Integer.valueOf(userId))) {
+ // should lock the user, user is already gone
+ final ArrayList<KeyEvictedCallback> keyEvictedCallbacks;
+ if (keyEvictedCallback != null) {
+ keyEvictedCallbacks = new ArrayList<>(1);
+ keyEvictedCallbacks.add(keyEvictedCallback);
+ } else {
+ keyEvictedCallbacks = null;
+ }
+ dispatchUserLocking(userId, keyEvictedCallbacks);
+ }
+ }
+ }
+ // We do need to post the stopped callback even though user is already stopped.
if (stopUserCallback != null) {
mHandler.post(() -> {
try {
@@ -704,6 +783,7 @@
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
updateStartedUserArrayLU();
+ final boolean allowDelayyLockingCopied = allowDelayedLocking;
// Post to handler to obtain amLock
mHandler.post(() -> {
// We are going to broadcast ACTION_USER_STOPPING and then
@@ -718,7 +798,8 @@
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
- mHandler.post(() -> finishUserStopping(userId, uss));
+ mHandler.post(() -> finishUserStopping(userId, uss,
+ allowDelayyLockingCopied));
}
};
@@ -734,7 +815,8 @@
}
}
- void finishUserStopping(final int userId, final UserState uss) {
+ void finishUserStopping(final int userId, final UserState uss,
+ final boolean allowDelayedLocking) {
Slog.d(TAG, "UserController event: finishUserStopping(" + userId + ")");
// On to the next.
final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
@@ -746,7 +828,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- finishUserStopped(uss);
+ finishUserStopped(uss, allowDelayedLocking);
}
});
}
@@ -773,7 +855,7 @@
Binder.getCallingPid(), userId);
}
- void finishUserStopped(UserState uss) {
+ void finishUserStopped(UserState uss, boolean allowDelayedLocking) {
final int userId = uss.mHandle.getIdentifier();
Slog.d(TAG, "UserController event: finishUserStopped(" + userId + ")");
final boolean stopped;
@@ -792,7 +874,13 @@
mStartedUsers.remove(userId);
mUserLru.remove(Integer.valueOf(userId));
updateStartedUserArrayLU();
- userIdToLock = updateUserToLockLU(userId);
+ if (allowDelayedLocking && !keyEvictedCallbacks.isEmpty()) {
+ Slog.wtf(TAG,
+ "Delayed locking enabled while KeyEvictedCallbacks not empty, userId:"
+ + userId + " callbacks:" + keyEvictedCallbacks);
+ allowDelayedLocking = false;
+ }
+ userIdToLock = updateUserToLockLU(userId, allowDelayedLocking);
if (userIdToLock == UserHandle.USER_NULL) {
lockUser = false;
}
@@ -826,31 +914,36 @@
if (!lockUser) {
return;
}
- final int userIdToLockF = userIdToLock;
- // Evict the user's credential encryption key. Performed on FgThread to make it
- // serialized with call to UserManagerService.onBeforeUnlockUser in finishUserUnlocking
- // to prevent data corruption.
- FgThread.getHandler().post(() -> {
- synchronized (mLock) {
- if (mStartedUsers.get(userIdToLockF) != null) {
- Slog.w(TAG, "User was restarted, skipping key eviction");
- return;
- }
- }
- try {
- mInjector.getStorageManager().lockUserKey(userIdToLockF);
- } catch (RemoteException re) {
- throw re.rethrowAsRuntimeException();
- }
- if (userIdToLockF == userId) {
- for (final KeyEvictedCallback callback : keyEvictedCallbacks) {
- callback.keyEvicted(userId);
- }
- }
- });
+ dispatchUserLocking(userIdToLock, keyEvictedCallbacks);
}
}
+ private void dispatchUserLocking(@UserIdInt int userId,
+ @Nullable List<KeyEvictedCallback> keyEvictedCallbacks) {
+ // Evict the user's credential encryption key. Performed on FgThread to make it
+ // serialized with call to UserManagerService.onBeforeUnlockUser in finishUserUnlocking
+ // to prevent data corruption.
+ FgThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ if (mStartedUsers.get(userId) != null) {
+ Slog.w(TAG, "User was restarted, skipping key eviction");
+ return;
+ }
+ }
+ try {
+ mInjector.getStorageManager().lockUserKey(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowAsRuntimeException();
+ }
+ if (keyEvictedCallbacks == null) {
+ return;
+ }
+ for (int i = 0; i < keyEvictedCallbacks.size(); i++) {
+ keyEvictedCallbacks.get(i).keyEvicted(userId);
+ }
+ });
+ }
+
/**
* For mDelayUserDataLocking mode, storage once unlocked is kept unlocked.
* Total number of unlocked user storage is limited by mMaxRunningUsers.
@@ -861,9 +954,9 @@
* @return user id to lock. UserHandler.USER_NULL will be returned if no user should be locked.
*/
@GuardedBy("mLock")
- private int updateUserToLockLU(@UserIdInt int userId) {
+ private int updateUserToLockLU(@UserIdInt int userId, boolean allowDelayedLocking) {
int userIdToLock = userId;
- if (mDelayUserDataLocking && !getUserInfo(userId).isEphemeral()
+ if (mDelayUserDataLocking && allowDelayedLocking && !getUserInfo(userId).isEphemeral()
&& !hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, userId)) {
mLastActiveUsers.remove((Integer) userId); // arg should be object, not index
mLastActiveUsers.add(0, userId);
@@ -945,7 +1038,8 @@
if (userInfo.isGuest() || userInfo.isEphemeral()) {
// This is a user to be stopped.
synchronized (mLock) {
- stopUsersLU(oldUserId, true, null, null);
+ stopUsersLU(oldUserId, /* force= */ true, /* allowDelayedLocking= */ false,
+ null, null);
}
}
}
@@ -977,7 +1071,7 @@
}
final int profilesToStartSize = profilesToStart.size();
int i = 0;
- for (; i < profilesToStartSize && i < (mMaxRunningUsers - 1); ++i) {
+ for (; i < profilesToStartSize && i < (getMaxRunningUsers() - 1); ++i) {
startUser(profilesToStart.get(i).id, /* foreground= */ false);
}
if (i < profilesToStartSize) {
@@ -1094,7 +1188,7 @@
return false;
}
- if (foreground && mUserSwitchUiEnabled) {
+ if (foreground && isUserSwitchUiEnabled()) {
t.traceBegin("startFreezingScreen");
mInjector.getWindowManager().startFreezingScreen(
R.anim.screen_user_exit, R.anim.screen_user_enter);
@@ -1142,9 +1236,11 @@
if (foreground) {
// Make sure the old user is no longer considering the display to be on.
mInjector.reportGlobalUsageEventLocked(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
+ boolean userSwitchUiEnabled;
synchronized (mLock) {
mCurrentUserId = userId;
mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
+ userSwitchUiEnabled = mUserSwitchUiEnabled;
}
mInjector.updateUserConfiguration();
updateCurrentProfileIds();
@@ -1152,7 +1248,7 @@
mInjector.reportCurWakefulnessUsageEvent();
// Once the internal notion of the active user has switched, we lock the device
// with the option to show the user switcher on the keyguard.
- if (mUserSwitchUiEnabled) {
+ if (userSwitchUiEnabled) {
mInjector.getWindowManager().setSwitchingUser(true);
mInjector.getWindowManager().lockNow(null);
}
@@ -1391,10 +1487,12 @@
Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
return false;
}
+ boolean userSwitchUiEnabled;
synchronized (mLock) {
mTargetUserId = targetUserId;
+ userSwitchUiEnabled = mUserSwitchUiEnabled;
}
- if (mUserSwitchUiEnabled) {
+ if (userSwitchUiEnabled) {
UserInfo currentUserInfo = getUserInfo(currentUserId);
Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo);
mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG);
@@ -1458,14 +1556,15 @@
}
// If running in background is disabled or mDelayUserDataLocking mode, stop the user.
boolean disallowRunInBg = hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND,
- oldUserId) || mDelayUserDataLocking;
+ oldUserId) || isDelayUserDataLockingEnabled();
if (!disallowRunInBg) {
return;
}
synchronized (mLock) {
if (DEBUG_MU) Slog.i(TAG, "stopBackgroundUsersIfEnforced stopping " + oldUserId
+ " and related users");
- stopUsersLU(oldUserId, false, null, null);
+ stopUsersLU(oldUserId, /* force= */ false, /* allowDelayedLocking= */ true,
+ null, null);
}
}
@@ -1551,7 +1650,7 @@
void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
Slog.d(TAG, "Continue user switch oldUser #" + oldUserId + ", newUser #" + newUserId);
- if (mUserSwitchUiEnabled) {
+ if (isUserSwitchUiEnabled()) {
mInjector.getWindowManager().stopFreezingScreen();
}
uss.switching = false;
@@ -1645,10 +1744,9 @@
final boolean allow;
final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, targetUserId);
if (mInjector.isCallerRecents(callingUid)
- && callingUserId == getCurrentUserId()
- && isSameProfileGroup) {
- // If the caller is Recents and it is running in the current user, we then allow it
- // to access its profiles.
+ && isSameProfileGroup(callingUserId, targetUserId)) {
+ // If the caller is Recents and the caller has ownership of the profile group,
+ // we then allow it to access its profiles.
allow = true;
} else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index a7593c7..e3b761e 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1759,8 +1759,9 @@
? opNames.toArray(new String[opNames.size()]) : null;
// Must not hold the appops lock
- mHistoricalRegistry.getHistoricalOps(uid, packageName, featureId, opNamesArray, filter,
- beginTimeMillis, endTimeMillis, flags, callback);
+ mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
+ mHistoricalRegistry, uid, packageName, featureId, opNamesArray, filter,
+ beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
}
@Override
@@ -1778,8 +1779,9 @@
? opNames.toArray(new String[opNames.size()]) : null;
// Must not hold the appops lock
- mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, featureId, opNamesArray,
- filter, beginTimeMillis, endTimeMillis, flags, callback);
+ mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw,
+ mHistoricalRegistry, uid, packageName, featureId, opNamesArray,
+ filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
}
@Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cd2272a..eedeeea 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2193,8 +2193,8 @@
}
private void enforceModifyAudioRoutingPermission() {
- if (mContext.checkCallingPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Missing MODIFY_AUDIO_ROUTING permission");
}
@@ -6427,7 +6427,7 @@
return false;
}
boolean suppress = false;
- if (resolvedStream == DEFAULT_VOL_STREAM_NO_PLAYBACK && mController != null) {
+ if (resolvedStream != AudioSystem.STREAM_MUSIC && mController != null) {
final long now = SystemClock.uptimeMillis();
if ((flags & AudioManager.FLAG_SHOW_UI) != 0 && !mVisible) {
// ui will become visible
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index e1a9f3b..8dd3242 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1400,7 +1400,8 @@
if (mCurrentAuthSession.mTokenEscrow != null) {
mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
}
- mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+ mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(
+ Utils.getAuthenticationTypeForResult(reason));
break;
case BiometricPrompt.DISMISSED_REASON_NEGATIVE:
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 19f5358..389763b 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -22,6 +22,7 @@
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
@@ -210,4 +211,30 @@
}
return biometricManagerCode;
}
+
+ /**
+ * Converts a {@link BiometricPrompt} dismissal reason to an authentication type at the level of
+ * granularity supported by {@link BiometricPrompt.AuthenticationResult}.
+ *
+ * @param reason The reason that the {@link BiometricPrompt} was dismissed. Must be one of:
+ * {@link BiometricPrompt#DISMISSED_REASON_CREDENTIAL_CONFIRMED},
+ * {@link BiometricPrompt#DISMISSED_REASON_BIOMETRIC_CONFIRMED}, or
+ * {@link BiometricPrompt#DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED}
+ * @return An integer representing the authentication type for {@link
+ * BiometricPrompt.AuthenticationResult}.
+ * @throws IllegalArgumentException if given an invalid dismissal reason.
+ */
+ public static @AuthenticationResultType int getAuthenticationTypeForResult(int reason) {
+ switch (reason) {
+ case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED:
+ return BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL;
+
+ case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED:
+ case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED:
+ return BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC;
+
+ default:
+ throw new IllegalArgumentException("Unsupported dismissal reason: " + reason);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index f15d999..8d26176 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -367,6 +367,8 @@
CompatConfig config = new CompatConfig(androidBuildClassifier, context);
config.initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "etc", "compatconfig"));
+ config.initConfigFromLib(Environment.buildPath(
+ Environment.getRootDirectory(), "system_ext", "etc", "compatconfig"));
return config;
}
diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
index 1b1c546..5010e46 100644
--- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java
+++ b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
@@ -16,6 +16,9 @@
package com.android.server.connectivity;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -24,6 +27,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -91,7 +95,10 @@
boolean visible = (simReadyOrUnknown || isCdma()) // we only check the sim state for GSM
&& hasService()
&& mDataState == TelephonyManager.DATA_CONNECTED;
- int networkType = mServiceState.getDataNetworkType();
+ NetworkRegistrationInfo regInfo =
+ mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
+ int networkType = regInfo == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN
+ : regInfo.getAccessNetworkTechnology();
if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible",
networkType, visible ? "" : "not "));
try {
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index aea6d8d..f636d67 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -116,7 +116,8 @@
&& !lp.hasIpv4Address();
// If the network tells us it doesn't use clat, respect that.
- final boolean skip464xlat = (nai.netMisc() != null) && nai.netMisc().skip464xlat;
+ final boolean skip464xlat = (nai.netAgentConfig() != null)
+ && nai.netAgentConfig().skip464xlat;
return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 5e085ca..c1ab551 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -23,9 +23,9 @@
import android.net.INetworkMonitor;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
import android.net.NetworkMonitorManager;
import android.net.NetworkRequest;
import android.net.NetworkScore;
@@ -127,7 +127,7 @@
// This should only be modified by ConnectivityService, via setNetworkCapabilities().
// TODO: make this private with a getter.
public NetworkCapabilities networkCapabilities;
- public final NetworkMisc networkMisc;
+ public final NetworkAgentConfig networkAgentConfig;
// Indicates if netd has been told to create this Network. From this point on the appropriate
// routing rules are setup and routes are added so packets can begin flowing over the Network.
// This is a sticky bit; once set it is never cleared.
@@ -261,7 +261,7 @@
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, @NonNull NetworkScore ns, Context context,
- Handler handler, NetworkMisc misc, ConnectivityService connService, INetd netd,
+ Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) {
this.messenger = messenger;
asyncChannel = ac;
@@ -274,7 +274,7 @@
mConnService = connService;
mContext = context;
mHandler = handler;
- networkMisc = misc;
+ networkAgentConfig = config;
this.factorySerialNumber = factorySerialNumber;
}
@@ -309,8 +309,8 @@
return mConnService;
}
- public NetworkMisc netMisc() {
- return networkMisc;
+ public NetworkAgentConfig netAgentConfig() {
+ return networkAgentConfig;
}
public Handler handler() {
@@ -487,7 +487,8 @@
// selected and we're trying to see what its score could be. This ensures that we don't tear
// down an explicitly selected network before the user gets a chance to prefer it when
// a higher-scoring network (e.g., Ethernet) is available.
- if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
+ if (networkAgentConfig.explicitlySelected
+ && (networkAgentConfig.acceptUnvalidated || pretendValidated)) {
return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
}
@@ -533,7 +534,8 @@
synchronized (this) {
// Network objects are outwardly immutable so there is no point in duplicating.
// Duplicating also precludes sharing socket factories and connection pools.
- final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null;
+ final String subscriberId = (networkAgentConfig != null)
+ ? networkAgentConfig.subscriberId : null;
return new NetworkState(new NetworkInfo(networkInfo),
new LinkProperties(linkProperties),
new NetworkCapabilities(networkCapabilities), network, subscriberId, null);
@@ -641,13 +643,13 @@
+ "nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} "
+ "everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} "
+ "created{" + created + "} lingering{" + isLingering() + "} "
- + "explicitlySelected{" + networkMisc.explicitlySelected + "} "
- + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} "
+ + "explicitlySelected{" + networkAgentConfig.explicitlySelected + "} "
+ + "acceptUnvalidated{" + networkAgentConfig.acceptUnvalidated + "} "
+ "everCaptivePortalDetected{" + everCaptivePortalDetected + "} "
+ "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} "
+ "captivePortalValidationPending{" + captivePortalValidationPending + "} "
+ "partialConnectivity{" + partialConnectivity + "} "
- + "acceptPartialConnectivity{" + networkMisc.acceptPartialConnectivity + "} "
+ + "acceptPartialConnectivity{" + networkAgentConfig.acceptPartialConnectivity + "} "
+ "clat{" + clatd + "} "
+ "}";
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6b70e5f..1d2a905 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -53,11 +53,11 @@
import android.net.LocalSocketAddress;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
-import android.net.NetworkFactory;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkMisc;
+import android.net.NetworkProvider;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.VpnService;
@@ -848,7 +848,7 @@
}
public int getNetId() {
- return mNetworkAgent != null ? mNetworkAgent.netId : NETID_UNSET;
+ return mNetworkAgent != null ? mNetworkAgent.network.netId : NETID_UNSET;
}
private LinkProperties makeLinkProperties() {
@@ -914,7 +914,7 @@
* has certain changes, in which case this method would just return {@code false}.
*/
private boolean updateLinkPropertiesInPlaceIfPossible(NetworkAgent agent, VpnConfig oldConfig) {
- // NetworkMisc cannot be updated without registering a new NetworkAgent.
+ // NetworkAgentConfig cannot be updated without registering a new NetworkAgent.
if (oldConfig.allowBypass != mConfig.allowBypass) {
Log.i(TAG, "Handover not possible due to changes to allowBypass");
return false;
@@ -947,8 +947,8 @@
mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null);
- NetworkMisc networkMisc = new NetworkMisc();
- networkMisc.allowBypass = mConfig.allowBypass && !mLockdown;
+ NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
+ networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown;
mNetworkCapabilities.setEstablishingVpnAppUid(Binder.getCallingUid());
mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle,
@@ -957,8 +957,8 @@
try {
mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */,
mNetworkInfo, mNetworkCapabilities, lp,
- ConnectivityConstants.VPN_DEFAULT_SCORE, networkMisc,
- NetworkFactory.SerialNumber.VPN) {
+ ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig,
+ NetworkProvider.ID_VPN) {
@Override
public void unwanted() {
// We are user controlled, not driven by NetworkRequest.
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index c58000f..8206fef 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -41,7 +41,6 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -713,8 +712,8 @@
}
/**
- * Gets a list of all supported users (i.e., those that pass the {@link #isSupported(UserInfo)}
- * check).
+ * Gets a list of all supported users (i.e., those that pass the
+ * {@link #isSupportedUser(TargetUser)}check).
*/
@NonNull
protected List<UserInfo> getSupportedUsers() {
@@ -723,7 +722,7 @@
final List<UserInfo> supportedUsers = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
final UserInfo userInfo = allUsers[i];
- if (isSupported(userInfo)) {
+ if (isSupportedUser(new TargetUser(userInfo))) {
supportedUsers.add(userInfo);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index c40e8af..9c42152 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -68,8 +68,21 @@
* @param autofillId {@link AutofillId} of currently focused field.
* @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request object.
*/
- public abstract void onCreateInlineSuggestionsRequest(ComponentName componentName,
- AutofillId autofillId, IInlineSuggestionsRequestCallback cb);
+ public abstract void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
+ ComponentName componentName, AutofillId autofillId,
+ IInlineSuggestionsRequestCallback cb);
+
+ /**
+ * Force switch to the enabled input method by {@code imeId} for current user. If the input
+ * method with {@code imeId} is not enabled or not installed, do nothing.
+ *
+ * @param imeId The input method ID to be switched to.
+ * @param userId The user ID to be queried.
+ * @return {@code true} if the current input method was successfully switched to the input
+ * method by {@code imeId}; {@code false} the input method with {@code imeId} is not available
+ * to be switched.
+ */
+ public abstract boolean switchToInputMethod(String imeId, @UserIdInt int userId);
/**
* Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing.
@@ -95,8 +108,14 @@
}
@Override
- public void onCreateInlineSuggestionsRequest(ComponentName componentName,
- AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
+ public void onCreateInlineSuggestionsRequest(int userId,
+ ComponentName componentName, AutofillId autofillId,
+ IInlineSuggestionsRequestCallback cb) {
+ }
+
+ @Override
+ public boolean switchToInputMethod(String imeId, int userId) {
+ return false;
}
};
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8b6b614..0bf65bd 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -111,6 +111,7 @@
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillId;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionInspector;
@@ -142,6 +143,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethod;
import com.android.internal.view.IInputMethodClient;
@@ -1790,15 +1792,18 @@
}
@GuardedBy("mMethodMap")
- private void onCreateInlineSuggestionsRequestLocked(ComponentName componentName,
- AutofillId autofillId, IInlineSuggestionsRequestCallback callback) {
-
+ private void onCreateInlineSuggestionsRequestLocked(@UserIdInt int userId,
+ ComponentName componentName, AutofillId autofillId,
+ IInlineSuggestionsRequestCallback callback) {
final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
try {
- if (imi != null && imi.isInlineSuggestionsEnabled() && mCurMethod != null) {
+ if (userId == mSettings.getCurrentUserId() && imi != null
+ && imi.isInlineSuggestionsEnabled() && mCurMethod != null) {
executeOrSendMessage(mCurMethod,
mCaller.obtainMessageOOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod,
- componentName, autofillId, callback));
+ componentName, autofillId,
+ new InlineSuggestionsRequestCallbackDecorator(callback,
+ imi.getPackageName())));
} else {
callback.onInlineSuggestionsUnsupported();
}
@@ -1808,6 +1813,42 @@
}
/**
+ * The decorator which validates the host package name in the
+ * {@link InlineSuggestionsRequest} argument to make sure it matches the IME package name.
+ */
+ private static final class InlineSuggestionsRequestCallbackDecorator
+ extends IInlineSuggestionsRequestCallback.Stub {
+ @NonNull
+ private final IInlineSuggestionsRequestCallback mCallback;
+ @NonNull
+ private final String mImePackageName;
+
+ InlineSuggestionsRequestCallbackDecorator(
+ @NonNull IInlineSuggestionsRequestCallback callback,
+ @NonNull String imePackageName) {
+ mCallback = callback;
+ mImePackageName = imePackageName;
+ }
+
+ @Override
+ public void onInlineSuggestionsUnsupported() throws RemoteException {
+ mCallback.onInlineSuggestionsUnsupported();
+ }
+
+ @Override
+ public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
+ IInlineSuggestionsResponseCallback callback) throws RemoteException {
+ if (!mImePackageName.equals(request.getHostPackageName())) {
+ throw new SecurityException(
+ "Host package name in the provide request=[" + request.getHostPackageName()
+ + "] doesn't match the IME package name=[" + mImePackageName
+ + "].");
+ }
+ mCallback.onInlineSuggestionsRequest(request, callback);
+ }
+ }
+
+ /**
* @param imiId if null, returns enabled subtypes for the current imi
* @return enabled subtypes of the specified imi
*/
@@ -4471,10 +4512,43 @@
}
}
- private void onCreateInlineSuggestionsRequest(ComponentName componentName,
- AutofillId autofillId, IInlineSuggestionsRequestCallback callback) {
+ private void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
+ ComponentName componentName, AutofillId autofillId,
+ IInlineSuggestionsRequestCallback callback) {
synchronized (mMethodMap) {
- onCreateInlineSuggestionsRequestLocked(componentName, autofillId, callback);
+ onCreateInlineSuggestionsRequestLocked(userId, componentName, autofillId, callback);
+ }
+ }
+
+ private boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
+ synchronized (mMethodMap) {
+ if (userId == mSettings.getCurrentUserId()) {
+ if (!mMethodMap.containsKey(imeId)
+ || !mSettings.getEnabledInputMethodListLocked()
+ .contains(mMethodMap.get(imeId))) {
+ return false; // IME is not is found or not enabled.
+ }
+ setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
+ return true;
+ }
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+ final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+ new ArrayMap<>();
+ AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+ queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
+ methodMap, methodList);
+ final InputMethodSettings settings = new InputMethodSettings(
+ mContext.getResources(), mContext.getContentResolver(), methodMap,
+ userId, false);
+ if (!methodMap.containsKey(imeId)
+ || !settings.getEnabledInputMethodListLocked()
+ .contains(methodMap.get(imeId))) {
+ return false; // IME is not is found or not enabled.
+ }
+ settings.putSelectedInputMethod(imeId);
+ settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+ return true;
}
}
@@ -4510,9 +4584,14 @@
}
@Override
- public void onCreateInlineSuggestionsRequest(ComponentName componentName,
+ public void onCreateInlineSuggestionsRequest(int userId, ComponentName componentName,
AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
- mService.onCreateInlineSuggestionsRequest(componentName, autofillId, cb);
+ mService.onCreateInlineSuggestionsRequest(userId, componentName, autofillId, cb);
+ }
+
+ @Override
+ public boolean switchToInputMethod(String imeId, int userId) {
+ return mService.switchToInputMethod(imeId, userId);
}
}
@@ -5065,31 +5144,7 @@
if (!userHasDebugPriv(userId, shellCommand)) {
continue;
}
- boolean failedToSelectUnknownIme = false;
- if (userId == mSettings.getCurrentUserId()) {
- if (mMethodMap.containsKey(imeId)) {
- setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
- } else {
- failedToSelectUnknownIme = true;
- }
- } else {
- final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
- final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
- final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
- new ArrayMap<>();
- AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
- queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
- methodMap, methodList);
- final InputMethodSettings settings = new InputMethodSettings(
- mContext.getResources(), mContext.getContentResolver(), methodMap,
- userId, false);
- if (methodMap.containsKey(imeId)) {
- settings.putSelectedInputMethod(imeId);
- settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
- } else {
- failedToSelectUnknownIme = true;
- }
- }
+ boolean failedToSelectUnknownIme = !switchToInputMethod(imeId, userId);
if (failedToSelectUnknownIme) {
error.print("Unknown input method ");
error.print(imeId);
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 1f9379c..f09795f 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -191,8 +191,9 @@
}
@Override
- public void onCreateInlineSuggestionsRequest(ComponentName componentName,
- AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
+ public void onCreateInlineSuggestionsRequest(int userId,
+ ComponentName componentName, AutofillId autofillId,
+ IInlineSuggestionsRequestCallback cb) {
try {
//TODO(b/137800469): support multi client IMEs.
cb.onInlineSuggestionsUnsupported();
@@ -200,6 +201,12 @@
Slog.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
}
}
+
+ @Override
+ public boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
+ reportNotSupported();
+ return false;
+ }
});
}
diff --git a/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java b/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java
index e555e3e..4bf8fe8 100644
--- a/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java
+++ b/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java
@@ -27,30 +27,37 @@
*/
public class BitTrackedInputStream extends BitInputStream {
- private static int sReadBitsCount;
+ private int mReadBitsCount;
/** Constructor with byte array. */
public BitTrackedInputStream(byte[] inputStream) {
super(inputStream);
- sReadBitsCount = 0;
+ mReadBitsCount = 0;
}
/** Constructor with input stream. */
public BitTrackedInputStream(InputStream inputStream) {
super(inputStream);
- sReadBitsCount = 0;
+ mReadBitsCount = 0;
}
/** Obtains an integer value of the next {@code numOfBits}. */
@Override
public int getNext(int numOfBits) throws IOException {
- sReadBitsCount += numOfBits;
+ mReadBitsCount += numOfBits;
return super.getNext(numOfBits);
}
/** Returns the current cursor position showing the number of bits that are read. */
public int getReadBitsCount() {
- return sReadBitsCount;
+ return mReadBitsCount;
+ }
+
+ /**
+ * Returns true if we can read more rules by checking whether the end index is not reached yet.
+ */
+ public boolean canReadMoreRules(int endIndexBytes) {
+ return mReadBitsCount < endIndexBytes * 8;
}
/**
@@ -59,11 +66,11 @@
* Note that the integer parameter specifies the location in bytes -- not bits.
*/
public void setCursorToByteLocation(int byteLocation) throws IOException {
- int bitCountToRead = byteLocation * 8 - sReadBitsCount;
+ int bitCountToRead = byteLocation * 8 - mReadBitsCount;
if (bitCountToRead < 0) {
throw new IllegalStateException("The byte position is already read.");
}
super.getNext(bitCountToRead);
- sReadBitsCount = byteLocation * 8;
+ mReadBitsCount = byteLocation * 8;
}
}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index e744326..2f28563 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -105,8 +105,7 @@
bitTrackedInputStream.setCursorToByteLocation(range.getStartIndex());
// Read the rules until we reach the end index.
- while (bitTrackedInputStream.hasNext()
- && bitTrackedInputStream.getReadBitsCount() < range.getEndIndex()) {
+ while (bitTrackedInputStream.canReadMoreRules(range.getEndIndex())) {
if (bitTrackedInputStream.getNext(SIGNAL_BIT) == 1) {
parsedRules.add(parseRule(bitTrackedInputStream));
}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
index 8c8450e..453fa5d 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
@@ -23,28 +23,28 @@
* RuleIndexingController}.
*/
public class RuleIndexRange {
- private static int sStartIndex;
- private static int sEndIndex;
+ private int mStartIndex;
+ private int mEndIndex;
/** Constructor with start and end indexes. */
public RuleIndexRange(int startIndex, int endIndex) {
- this.sStartIndex = startIndex;
- this.sEndIndex = endIndex;
+ this.mStartIndex = startIndex;
+ this.mEndIndex = endIndex;
}
/** Returns the startIndex. */
public int getStartIndex() {
- return sStartIndex;
+ return mStartIndex;
}
/** Returns the end index. */
public int getEndIndex() {
- return sEndIndex;
+ return mEndIndex;
}
@Override
public boolean equals(@Nullable Object object) {
- return sStartIndex == ((RuleIndexRange) object).getStartIndex()
- && sEndIndex == ((RuleIndexRange) object).getEndIndex();
+ return mStartIndex == ((RuleIndexRange) object).getStartIndex()
+ && mEndIndex == ((RuleIndexRange) object).getEndIndex();
}
}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
index c971322..03392ab 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
@@ -56,7 +56,7 @@
* read and evaluated.
*/
public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
- ArrayList<RuleIndexRange> indexRanges = new ArrayList();
+ List<RuleIndexRange> indexRanges = new ArrayList<>();
// Add the range for package name indexes rules.
indexRanges.add(
@@ -102,7 +102,7 @@
.collect(Collectors.toCollection(TreeSet::new));
String minIndex = keyTreeSet.floor(searchedKey);
- String maxIndex = keyTreeSet.ceiling(searchedKey);
+ String maxIndex = keyTreeSet.higher(searchedKey);
return new RuleIndexRange(
indexMap.get(minIndex == null ? START_INDEXING_KEY : minIndex),
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 f964d4c..8f53be7 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -48,10 +48,11 @@
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.TreeMap;
+import java.util.stream.Collectors;
/** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
public class RuleBinarySerializer implements RuleSerializer {
@@ -79,31 +80,37 @@
throws RuleSerializeException {
try {
// Determine the indexing groups and the order of the rules within each indexed group.
- Map<Integer, TreeMap<String, List<Rule>>> indexedRules =
+ Map<Integer, Map<String, List<Rule>>> indexedRules =
RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
+ // Serialize the rules.
ByteTrackedOutputStream ruleFileByteTrackedOutputStream =
new ByteTrackedOutputStream(rulesFileOutputStream);
-
serializeRuleFileMetadata(formatVersion, ruleFileByteTrackedOutputStream);
-
- Map<String, Integer> packageNameIndexes =
- serializeRuleList(indexedRules.get(PACKAGE_NAME_INDEXED),
+ LinkedHashMap<String, Integer> packageNameIndexes =
+ serializeRuleList(
+ indexedRules.get(PACKAGE_NAME_INDEXED),
ruleFileByteTrackedOutputStream);
- indexingFileOutputStream.write(
- serializeIndexes(packageNameIndexes, /* isIndexed= */true));
-
- Map<String, Integer> appCertificateIndexes =
- serializeRuleList(indexedRules.get(APP_CERTIFICATE_INDEXED),
+ LinkedHashMap<String, Integer> appCertificateIndexes =
+ serializeRuleList(
+ indexedRules.get(APP_CERTIFICATE_INDEXED),
ruleFileByteTrackedOutputStream);
- indexingFileOutputStream.write(
- serializeIndexes(appCertificateIndexes, /* isIndexed= */true));
-
- Map<String, Integer> unindexedRulesIndexes =
- serializeRuleList(indexedRules.get(NOT_INDEXED),
+ LinkedHashMap<String, Integer> unindexedRulesIndexes =
+ serializeRuleList(
+ indexedRules.get(NOT_INDEXED),
ruleFileByteTrackedOutputStream);
- indexingFileOutputStream.write(
- serializeIndexes(unindexedRulesIndexes, /* isIndexed= */false));
+
+ // Serialize their indexes.
+ BitOutputStream indexingBitOutputStream = new BitOutputStream();
+ serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */true);
+ serializeIndexGroup(appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */
+ true);
+ serializeIndexGroup(unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */
+ false);
+ // TODO(b/147609625): This dummy bit is set for fixing the padding issue. Remove when
+ // the issue is fixed and correct the tests that does this padding too.
+ indexingBitOutputStream.setNext();
+ indexingFileOutputStream.write(indexingBitOutputStream.toByteArray());
} catch (Exception e) {
throw new RuleSerializeException(e.getMessage(), e);
}
@@ -119,24 +126,25 @@
outputStream.write(bitOutputStream.toByteArray());
}
- private Map<String, Integer> serializeRuleList(TreeMap<String, List<Rule>> rulesMap,
- ByteTrackedOutputStream outputStream)
+ private LinkedHashMap<String, Integer> serializeRuleList(
+ Map<String, List<Rule>> rulesMap, ByteTrackedOutputStream outputStream)
throws IOException {
Preconditions.checkArgument(rulesMap != null,
"serializeRuleList should never be called with null rule list.");
BitOutputStream bitOutputStream = new BitOutputStream();
- Map<String, Integer> indexMapping = new TreeMap();
- int indexTracker = 0;
-
+ LinkedHashMap<String, Integer> indexMapping = new LinkedHashMap();
indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount());
- for (Map.Entry<String, List<Rule>> entry : rulesMap.entrySet()) {
+
+ List<String> sortedKeys = rulesMap.keySet().stream().sorted().collect(Collectors.toList());
+ int indexTracker = 0;
+ for (String key : sortedKeys) {
if (indexTracker >= INDEXING_BLOCK_SIZE) {
- indexMapping.put(entry.getKey(), outputStream.getWrittenBytesCount());
+ indexMapping.put(key, outputStream.getWrittenBytesCount());
indexTracker = 0;
}
- for (Rule rule : entry.getValue()) {
+ for (Rule rule : rulesMap.get(key)) {
bitOutputStream.clear();
serializeRule(rule, bitOutputStream);
outputStream.write(bitOutputStream.toByteArray());
@@ -220,12 +228,14 @@
}
}
- private byte[] serializeIndexes(Map<String, Integer> indexes, boolean isIndexed) {
- BitOutputStream bitOutputStream = new BitOutputStream();
+ private void serializeIndexGroup(
+ LinkedHashMap<String, Integer> indexes,
+ BitOutputStream bitOutputStream,
+ boolean isIndexed) {
// Output the starting location of this indexing group.
- serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */false,
- bitOutputStream);
+ serializeStringValue(
+ START_INDEXING_KEY, /* isHashedValue= */false, bitOutputStream);
serializeIntValue(indexes.get(START_INDEXING_KEY), bitOutputStream);
// If the group is indexed, output the locations of the indexes.
@@ -243,8 +253,6 @@
// Output the end location of this indexing group.
serializeStringValue(END_INDEXING_KEY, /*isHashedValue= */ false, bitOutputStream);
serializeIntValue(indexes.get(END_INDEXING_KEY), bitOutputStream);
-
- return bitOutputStream.toByteArray();
}
private void serializeStringValue(
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java
index dd871e2..2cbd4ede 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java
@@ -28,6 +28,8 @@
static final int PACKAGE_NAME_INDEXED = 1;
static final int APP_CERTIFICATE_INDEXED = 2;
+ static final String DEFAULT_RULE_KEY = "N/A";
+
/** Represents which indexed file the rule should be located. */
@IntDef(
value = {
@@ -45,7 +47,7 @@
/** Constructor without a ruleKey for {@code NOT_INDEXED}. */
RuleIndexingDetails(@IndexType int indexType) {
this.mIndexType = indexType;
- this.mRuleKey = null;
+ this.mRuleKey = DEFAULT_RULE_KEY;
}
/** Constructor with a ruleKey for indexed rules. */
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
index cbc365e..7d9a901 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
@@ -30,30 +30,27 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.TreeMap;
/** A helper class for identifying the indexing type and key of a given rule. */
class RuleIndexingDetailsIdentifier {
- private static final String DEFAULT_RULE_KEY = "N/A";
-
/**
* Splits a given rule list into three indexing categories. Each rule category is returned as a
* TreeMap that is sorted by their indexing keys -- where keys correspond to package name for
* PACKAGE_NAME_INDEXED rules, app certificate for APP_CERTIFICATE_INDEXED rules and N/A for
* NOT_INDEXED rules.
*/
- public static Map<Integer, TreeMap<String, List<Rule>>> splitRulesIntoIndexBuckets(
+ public static Map<Integer, Map<String, List<Rule>>> splitRulesIntoIndexBuckets(
List<Rule> rules) {
if (rules == null) {
throw new IllegalArgumentException(
"Index buckets cannot be created for null rule list.");
}
- Map<Integer, TreeMap<String, List<Rule>>> typeOrganizedRuleMap = new HashMap();
- typeOrganizedRuleMap.put(NOT_INDEXED, new TreeMap());
- typeOrganizedRuleMap.put(PACKAGE_NAME_INDEXED, new TreeMap());
- typeOrganizedRuleMap.put(APP_CERTIFICATE_INDEXED, new TreeMap());
+ Map<Integer, Map<String, List<Rule>>> typeOrganizedRuleMap = new HashMap();
+ typeOrganizedRuleMap.put(NOT_INDEXED, new HashMap());
+ typeOrganizedRuleMap.put(PACKAGE_NAME_INDEXED, new HashMap<>());
+ typeOrganizedRuleMap.put(APP_CERTIFICATE_INDEXED, new HashMap<>());
// Split the rules into the appropriate indexed pattern. The Tree Maps help us to keep the
// entries sorted by their index key.
@@ -66,21 +63,14 @@
String.format("Malformed rule identified. [%s]", rule.toString()));
}
- String ruleKey =
- indexingDetails.getIndexType() != NOT_INDEXED
- ? indexingDetails.getRuleKey()
- : DEFAULT_RULE_KEY;
+ int ruleIndexType = indexingDetails.getIndexType();
+ String ruleKey = indexingDetails.getRuleKey();
- if (!typeOrganizedRuleMap.get(indexingDetails.getIndexType()).containsKey(ruleKey)) {
- typeOrganizedRuleMap
- .get(indexingDetails.getIndexType())
- .put(ruleKey, new ArrayList());
+ if (!typeOrganizedRuleMap.get(ruleIndexType).containsKey(ruleKey)) {
+ typeOrganizedRuleMap.get(ruleIndexType).put(ruleKey, new ArrayList());
}
- typeOrganizedRuleMap
- .get(indexingDetails.getIndexType())
- .get(ruleKey)
- .add(rule);
+ typeOrganizedRuleMap.get(ruleIndexType).get(ruleKey).add(rule);
}
return typeOrganizedRuleMap;
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
index 4194432..8f164e6 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
@@ -35,7 +35,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.TreeMap;
+import java.util.stream.Collectors;
/** A helper class to serialize rules from the {@link Rule} model to Xml representation. */
public class RuleXmlSerializer implements RuleSerializer {
@@ -90,7 +90,7 @@
throws RuleSerializeException {
try {
// Determine the indexing groups and the order of the rules within each indexed group.
- Map<Integer, TreeMap<String, List<Rule>>> indexedRules =
+ Map<Integer, Map<String, List<Rule>>> indexedRules =
RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
// Write the XML formatted rules in order.
@@ -107,11 +107,12 @@
}
}
- private void serializeRuleList(TreeMap<String, List<Rule>> rulesMap,
- XmlSerializer xmlSerializer)
+ private void serializeRuleList(Map<String, List<Rule>> rulesMap, XmlSerializer xmlSerializer)
throws IOException {
- for (Map.Entry<String, List<Rule>> entry : rulesMap.entrySet()) {
- for (Rule rule : entry.getValue()) {
+ List<String> sortedKeyList =
+ rulesMap.keySet().stream().sorted().collect(Collectors.toList());
+ for (String key : sortedKeyList) {
+ for (Rule rule : rulesMap.get(key)) {
serializeRule(rule, xmlSerializer);
}
}
diff --git a/services/core/java/com/android/server/location/NtpTimeHelper.java b/services/core/java/com/android/server/location/NtpTimeHelper.java
index 67841ac..d2296ea 100644
--- a/services/core/java/com/android/server/location/NtpTimeHelper.java
+++ b/services/core/java/com/android/server/location/NtpTimeHelper.java
@@ -130,7 +130,8 @@
// force refresh NTP cache when outdated
boolean refreshSuccess = true;
- if (mNtpTime.getCacheAge() >= NTP_INTERVAL) {
+ NtpTrustedTime.TimeResult ntpResult = mNtpTime.getCachedTimeResult();
+ if (ntpResult == null || ntpResult.getAgeMillis() >= NTP_INTERVAL) {
// Blocking network operation.
refreshSuccess = mNtpTime.forceRefresh();
}
@@ -140,17 +141,17 @@
// only update when NTP time is fresh
// If refreshSuccess is false, cacheAge does not drop down.
- if (mNtpTime.getCacheAge() < NTP_INTERVAL) {
- long time = mNtpTime.getCachedNtpTime();
- long timeReference = mNtpTime.getCachedNtpTimeReference();
- long certainty = mNtpTime.getCacheCertainty();
+ ntpResult = mNtpTime.getCachedTimeResult();
+ if (ntpResult != null && ntpResult.getAgeMillis() < NTP_INTERVAL) {
+ long time = ntpResult.getTimeMillis();
+ long timeReference = ntpResult.getElapsedRealtimeMillis();
+ long certainty = ntpResult.getCertaintyMillis();
if (DEBUG) {
long now = System.currentTimeMillis();
Log.d(TAG, "NTP server returned: "
- + time + " (" + new Date(time)
- + ") reference: " + timeReference
- + " certainty: " + certainty
+ + time + " (" + new Date(time) + ")"
+ + " ntpResult: " + ntpResult
+ " system time offset: " + (time - now));
}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
new file mode 100644
index 0000000..5191833
--- /dev/null
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -0,0 +1,321 @@
+/*
+ * 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.media;
+
+import android.annotation.NonNull;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.MediaRoute2Info;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+class BluetoothRouteProvider {
+ private static final String TAG = "BTRouteProvider";
+ private static BluetoothRouteProvider sInstance;
+
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ BluetoothA2dp mA2dpProfile;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ BluetoothHearingAid mHearingAidProfile;
+
+ private final Context mContext;
+ private final BluetoothAdapter mBluetoothAdapter;
+ private final BluetoothRoutesUpdatedListener mListener;
+ private final Map<String, BluetoothEventReceiver> mEventReceiverMap = new HashMap<>();
+ private final IntentFilter mIntentFilter = new IntentFilter();
+ private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
+ private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();
+
+ private BluetoothDevice mActiveDevice = null;
+
+ static synchronized BluetoothRouteProvider getInstance(@NonNull Context context,
+ @NonNull BluetoothRoutesUpdatedListener listener) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(listener);
+
+ if (sInstance == null) {
+ BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (btAdapter == null) {
+ return null;
+ }
+ sInstance = new BluetoothRouteProvider(context, btAdapter, listener);
+ }
+ return sInstance;
+ }
+
+ private BluetoothRouteProvider(Context context, BluetoothAdapter btAdapter,
+ BluetoothRoutesUpdatedListener listener) {
+ mContext = context;
+ mBluetoothAdapter = btAdapter;
+ mListener = listener;
+ buildBluetoothRoutes();
+
+ mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
+ mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
+
+ // Bluetooth on/off broadcasts
+ addEventReceiver(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedReceiver());
+
+ // Pairing broadcasts
+ addEventReceiver(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedReceiver());
+
+ DeviceStateChangedRecevier deviceStateChangedReceiver = new DeviceStateChangedRecevier();
+ addEventReceiver(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED, deviceStateChangedReceiver);
+ addEventReceiver(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, deviceStateChangedReceiver);
+ addEventReceiver(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,
+ deviceStateChangedReceiver);
+ addEventReceiver(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED,
+ deviceStateChangedReceiver);
+
+ mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, null);
+ }
+
+ private void addEventReceiver(String action, BluetoothEventReceiver eventReceiver) {
+ mEventReceiverMap.put(action, eventReceiver);
+ mIntentFilter.addAction(action);
+ }
+
+ private void buildBluetoothRoutes() {
+ mBluetoothRoutes.clear();
+ for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
+ if (device.isConnected()) {
+ BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
+ mBluetoothRoutes.put(device.getAddress(), newBtRoute);
+ }
+ }
+ }
+
+ @NonNull List<MediaRoute2Info> getBluetoothRoutes() {
+ ArrayList<MediaRoute2Info> routes = new ArrayList<>();
+ for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
+ routes.add(btRoute.route);
+ }
+ return routes;
+ }
+
+ private void notifyBluetoothRoutesUpdated() {
+ if (mListener != null) {
+ mListener.onBluetoothRoutesUpdated(getBluetoothRoutes());
+ }
+ }
+
+ private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
+ BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo();
+ newBtRoute.btDevice = device;
+ newBtRoute.route = new MediaRoute2Info.Builder(device.getAddress(), device.getName())
+ .addFeature(SystemMediaRoute2Provider.TYPE_LIVE_AUDIO)
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
+ .setDescription(mContext.getResources().getText(
+ R.string.bluetooth_a2dp_audio_route_name).toString())
+ .build();
+ newBtRoute.connectedProfiles = new SparseBooleanArray();
+ return newBtRoute;
+ }
+
+ private void setRouteConnectionStateForDevice(BluetoothDevice device,
+ @MediaRoute2Info.ConnectionState int state) {
+ if (device == null) {
+ Log.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null");
+ return;
+ }
+ BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
+ if (btRoute == null) {
+ Log.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null");
+ return;
+ }
+ if (btRoute.route.getConnectionState() != state) {
+ btRoute.route = new MediaRoute2Info.Builder(btRoute.route)
+ .setConnectionState(state).build();
+ }
+ }
+
+ interface BluetoothRoutesUpdatedListener {
+ void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes);
+ }
+
+ private class BluetoothRouteInfo {
+ public BluetoothDevice btDevice;
+ public MediaRoute2Info route;
+ public SparseBooleanArray connectedProfiles;
+ }
+
+ // These callbacks run on the main thread.
+ private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener {
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dpProfile = (BluetoothA2dp) proxy;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAidProfile = (BluetoothHearingAid) proxy;
+ break;
+ default:
+ return;
+ }
+ for (BluetoothDevice device : proxy.getConnectedDevices()) {
+ BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
+ if (btRoute == null) {
+ btRoute = createBluetoothRoute(device);
+ mBluetoothRoutes.put(device.getAddress(), btRoute);
+ }
+ btRoute.connectedProfiles.put(profile, true);
+ }
+ }
+
+ public void onServiceDisconnected(int profile) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dpProfile = null;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAidProfile = null;
+ break;
+ default:
+ return;
+ }
+ }
+ }
+ private class BluetoothBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+ BluetoothEventReceiver receiver = mEventReceiverMap.get(action);
+ if (receiver != null) {
+ receiver.onReceive(context, intent, device);
+ }
+ }
+ }
+
+ private interface BluetoothEventReceiver {
+ void onReceive(Context context, Intent intent, BluetoothDevice device);
+ }
+
+ private class AdapterStateChangedReceiver implements BluetoothEventReceiver {
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+ if (state == BluetoothAdapter.STATE_OFF
+ || state == BluetoothAdapter.STATE_TURNING_OFF) {
+ mBluetoothRoutes.clear();
+ notifyBluetoothRoutesUpdated();
+ } else if (state == BluetoothAdapter.STATE_ON) {
+ buildBluetoothRoutes();
+ if (!mBluetoothRoutes.isEmpty()) {
+ notifyBluetoothRoutesUpdated();
+ }
+ }
+ }
+ }
+
+ private class BondStateChangedReceiver implements BluetoothEventReceiver {
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.ERROR);
+ BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
+ if (bondState == BluetoothDevice.BOND_BONDED && btRoute == null) {
+ btRoute = createBluetoothRoute(device);
+ if (mA2dpProfile != null && mA2dpProfile.getConnectedDevices().contains(device)) {
+ btRoute.connectedProfiles.put(BluetoothProfile.A2DP, true);
+ }
+ if (mHearingAidProfile != null
+ && mHearingAidProfile.getConnectedDevices().contains(device)) {
+ btRoute.connectedProfiles.put(BluetoothProfile.HEARING_AID, true);
+ }
+ mBluetoothRoutes.put(device.getAddress(), btRoute);
+ notifyBluetoothRoutesUpdated();
+ } else if (bondState == BluetoothDevice.BOND_NONE
+ && mBluetoothRoutes.remove(device.getAddress()) != null) {
+ notifyBluetoothRoutesUpdated();
+ }
+ }
+ }
+
+ private class DeviceStateChangedRecevier implements BluetoothEventReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ switch (intent.getAction()) {
+ case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
+ String prevActiveDeviceAddress =
+ (mActiveDevice == null) ? null : mActiveDevice.getAddress();
+ String curActiveDeviceAddress =
+ (device == null) ? null : device.getAddress();
+ if (!TextUtils.equals(prevActiveDeviceAddress, curActiveDeviceAddress)) {
+ if (mActiveDevice != null) {
+ setRouteConnectionStateForDevice(mActiveDevice,
+ MediaRoute2Info.CONNECTION_STATE_DISCONNECTED);
+ }
+ if (device != null) {
+ setRouteConnectionStateForDevice(device,
+ MediaRoute2Info.CONNECTION_STATE_CONNECTED);
+ }
+ notifyBluetoothRoutesUpdated();
+ mActiveDevice = device;
+ }
+ break;
+ case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
+ handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device);
+ break;
+ }
+ }
+
+ private void handleConnectionStateChanged(int profile, Intent intent,
+ BluetoothDevice device) {
+ int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+ BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ if (btRoute == null) {
+ btRoute = createBluetoothRoute(device);
+ mBluetoothRoutes.put(device.getAddress(), btRoute);
+ btRoute.connectedProfiles.put(profile, true);
+ notifyBluetoothRoutesUpdated();
+ } else {
+ btRoute.connectedProfiles.put(profile, true);
+ }
+ } else if (state == BluetoothProfile.STATE_DISCONNECTING
+ || state == BluetoothProfile.STATE_DISCONNECTED) {
+ btRoute.connectedProfiles.delete(profile);
+ if (btRoute.connectedProfiles.size() == 0) {
+ mBluetoothRoutes.remove(device.getAddress());
+ if (mActiveDevice != null
+ && TextUtils.equals(mActiveDevice.getAddress(), device.getAddress())) {
+ mActiveDevice = null;
+ }
+ notifyBluetoothRoutesUpdated();
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 9c9a412..408c1c9 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -21,20 +21,24 @@
import android.content.ComponentName;
import android.content.Intent;
import android.media.MediaRoute2ProviderInfo;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
+
+import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Objects;
abstract class MediaRoute2Provider {
final ComponentName mComponentName;
final String mUniqueId;
+ final Object mLock = new Object();
Callback mCallback;
private volatile MediaRoute2ProviderInfo mProviderInfo;
- private volatile List<RouteSessionInfo> mSessionInfos = Collections.emptyList();
+
+ @GuardedBy("mLock")
+ final List<RoutingSessionInfo> mSessionInfos = new ArrayList<>();
MediaRoute2Provider(@NonNull ComponentName componentName) {
mComponentName = Objects.requireNonNull(componentName, "Component name must not be null.");
@@ -68,12 +72,13 @@
}
@NonNull
- public List<RouteSessionInfo> getSessionInfos() {
- return mSessionInfos;
+ public List<RoutingSessionInfo> getSessionInfos() {
+ synchronized (mLock) {
+ return mSessionInfos;
+ }
}
- void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo,
- List<RouteSessionInfo> sessionInfos) {
+ void setProviderState(MediaRoute2ProviderInfo providerInfo) {
if (providerInfo == null) {
mProviderInfo = null;
} else {
@@ -81,20 +86,19 @@
.setUniqueId(mUniqueId)
.build();
}
- List<RouteSessionInfo> sessionInfoWithProviderId = new ArrayList<RouteSessionInfo>();
- for (RouteSessionInfo sessionInfo : sessionInfos) {
- sessionInfoWithProviderId.add(
- new RouteSessionInfo.Builder(sessionInfo)
- .setProviderId(mUniqueId)
- .build());
- }
- mSessionInfos = sessionInfoWithProviderId;
+ }
+ void notifyProviderState() {
if (mCallback != null) {
mCallback.onProviderStateChanged(this);
}
}
+ void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo) {
+ setProviderState(providerInfo);
+ notifyProviderState();
+ }
+
public boolean hasComponentName(String packageName, String className) {
return mComponentName.getPackageName().equals(packageName)
&& mComponentName.getClassName().equals(className);
@@ -103,12 +107,11 @@
public interface Callback {
void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
void onSessionCreated(@NonNull MediaRoute2Provider provider,
- @Nullable RouteSessionInfo sessionInfo, long requestId);
- // TODO: Remove this when MediaRouter2ServiceImpl notifies clients of session changes.
- void onSessionInfoChanged(@NonNull MediaRoute2Provider provider,
- @NonNull RouteSessionInfo sessionInfo);
- // TODO: Call this when service actually notifies of session release.
+ @Nullable RoutingSessionInfo sessionInfo, long requestId);
+ void onSessionCreationFailed(@NonNull MediaRoute2Provider provider, long requestId);
+ void onSessionUpdated(@NonNull MediaRoute2Provider provider,
+ @NonNull RoutingSessionInfo sessionInfo);
void onSessionReleased(@NonNull MediaRoute2Provider provider,
- @NonNull RouteSessionInfo sessionInfo);
+ @NonNull RoutingSessionInfo sessionInfo);
}
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 6359835..3840d02 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -17,7 +17,6 @@
package com.android.server.media;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -26,7 +25,7 @@
import android.media.IMediaRoute2ProviderClient;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
@@ -37,8 +36,6 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-import java.util.Collections;
-import java.util.List;
import java.util.Objects;
/**
@@ -270,44 +267,126 @@
}
private void onProviderStateUpdated(Connection connection,
- MediaRoute2ProviderInfo providerInfo, List<RouteSessionInfo> sessionInfos) {
+ MediaRoute2ProviderInfo providerInfo) {
if (mActiveConnection != connection) {
return;
}
if (DEBUG) {
Slog.d(TAG, this + ": State changed ");
}
- setAndNotifyProviderState(providerInfo, sessionInfos);
+ setAndNotifyProviderState(providerInfo);
}
- private void onSessionCreated(Connection connection, @Nullable RouteSessionInfo sessionInfo,
+ private void onSessionCreated(Connection connection, RoutingSessionInfo sessionInfo,
long requestId) {
if (mActiveConnection != connection) {
return;
}
- if (sessionInfo != null) {
- sessionInfo = new RouteSessionInfo.Builder(sessionInfo)
- .setProviderId(getUniqueId())
- .build();
+
+ if (sessionInfo == null) {
+ Slog.w(TAG, "onSessionCreated: Ignoring null sessionInfo sent from " + mComponentName);
+ return;
}
+
+ sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
+ .setProviderId(getUniqueId())
+ .build();
+
+ boolean duplicateSessionAlreadyExists = false;
+ synchronized (mLock) {
+ for (int i = 0; i < mSessionInfos.size(); i++) {
+ if (mSessionInfos.get(i).getId().equals(sessionInfo.getId())) {
+ duplicateSessionAlreadyExists = true;
+ break;
+ }
+ }
+ mSessionInfos.add(sessionInfo);
+ }
+
+ if (duplicateSessionAlreadyExists) {
+ Slog.w(TAG, "onSessionCreated: Duplicate session already exists. Ignoring.");
+ return;
+ }
+
mCallback.onSessionCreated(this, sessionInfo, requestId);
}
- private void onSessionInfoChanged(Connection connection, RouteSessionInfo sessionInfo) {
+ private void onSessionCreationFailed(Connection connection, long requestId) {
+ if (mActiveConnection != connection) {
+ return;
+ }
+
+ if (requestId == MediaRoute2ProviderService.REQUEST_ID_UNKNOWN) {
+ Slog.w(TAG, "onSessionCreationFailed: Ignoring requestId REQUEST_ID_UNKNOWN");
+ return;
+ }
+
+ mCallback.onSessionCreationFailed(this, requestId);
+ }
+
+ private void onSessionUpdated(Connection connection, RoutingSessionInfo sessionInfo) {
if (mActiveConnection != connection) {
return;
}
if (sessionInfo == null) {
- Slog.w(TAG, "onSessionInfoChanged: Ignoring null sessionInfo sent from "
+ Slog.w(TAG, "onSessionUpdated: Ignoring null sessionInfo sent from "
+ mComponentName);
return;
}
- sessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+ sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
.setProviderId(getUniqueId())
.build();
- mCallback.onSessionInfoChanged(this, sessionInfo);
+ boolean found = false;
+ synchronized (mLock) {
+ for (int i = 0; i < mSessionInfos.size(); i++) {
+ if (mSessionInfos.get(i).getId().equals(sessionInfo.getId())) {
+ mSessionInfos.set(i, sessionInfo);
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ Slog.w(TAG, "onSessionUpdated: Matching session info not found");
+ return;
+ }
+
+ mCallback.onSessionUpdated(this, sessionInfo);
+ }
+
+ private void onSessionReleased(Connection connection, RoutingSessionInfo sessionInfo) {
+ if (mActiveConnection != connection) {
+ return;
+ }
+ if (sessionInfo == null) {
+ Slog.w(TAG, "onSessionReleased: Ignoring null sessionInfo sent from " + mComponentName);
+ return;
+ }
+
+ sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
+ .setProviderId(getUniqueId())
+ .build();
+
+ boolean found = false;
+ synchronized (mLock) {
+ for (int i = 0; i < mSessionInfos.size(); i++) {
+ if (mSessionInfos.get(i).getId().equals(sessionInfo.getId())) {
+ mSessionInfos.remove(i);
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ Slog.w(TAG, "onSessionReleased: Matching session info not found");
+ return;
+ }
+
+ mCallback.onSessionReleased(this, sessionInfo);
}
private void disconnect() {
@@ -315,7 +394,7 @@
mConnectionReady = false;
mActiveConnection.dispose();
mActiveConnection = null;
- setAndNotifyProviderState(null, Collections.emptyList());
+ setAndNotifyProviderState(null);
}
}
@@ -421,19 +500,24 @@
mHandler.post(() -> onConnectionDied(Connection.this));
}
- void postProviderStateUpdated(MediaRoute2ProviderInfo providerInfo,
- List<RouteSessionInfo> sessionInfos) {
- mHandler.post(() -> onProviderStateUpdated(Connection.this,
- providerInfo, sessionInfos));
+ void postProviderStateUpdated(MediaRoute2ProviderInfo providerInfo) {
+ mHandler.post(() -> onProviderStateUpdated(Connection.this, providerInfo));
}
- void postSessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) {
- mHandler.post(() -> onSessionCreated(Connection.this, sessionInfo,
- requestId));
+ void postSessionCreated(RoutingSessionInfo sessionInfo, long requestId) {
+ mHandler.post(() -> onSessionCreated(Connection.this, sessionInfo, requestId));
}
- void postSessionInfoChanged(RouteSessionInfo sessionInfo) {
- mHandler.post(() -> onSessionInfoChanged(Connection.this, sessionInfo));
+ void postSessionCreationFailed(long requestId) {
+ mHandler.post(() -> onSessionCreationFailed(Connection.this, requestId));
+ }
+
+ void postSessionUpdated(RoutingSessionInfo sessionInfo) {
+ mHandler.post(() -> onSessionUpdated(Connection.this, sessionInfo));
+ }
+
+ void postSessionReleased(RoutingSessionInfo sessionInfo) {
+ mHandler.post(() -> onSessionReleased(Connection.this, sessionInfo));
}
}
@@ -449,16 +533,15 @@
}
@Override
- public void updateState(MediaRoute2ProviderInfo providerInfo,
- List<RouteSessionInfo> sessionInfos) {
+ public void updateState(MediaRoute2ProviderInfo providerInfo) {
Connection connection = mConnectionRef.get();
if (connection != null) {
- connection.postProviderStateUpdated(providerInfo, sessionInfos);
+ connection.postProviderStateUpdated(providerInfo);
}
}
@Override
- public void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) {
+ public void notifySessionCreated(RoutingSessionInfo sessionInfo, long requestId) {
Connection connection = mConnectionRef.get();
if (connection != null) {
connection.postSessionCreated(sessionInfo, requestId);
@@ -466,10 +549,26 @@
}
@Override
- public void notifySessionInfoChanged(RouteSessionInfo sessionInfo) {
+ public void notifySessionCreationFailed(long requestId) {
Connection connection = mConnectionRef.get();
if (connection != null) {
- connection.postSessionInfoChanged(sessionInfo);
+ connection.postSessionCreationFailed(requestId);
+ }
+ }
+
+ @Override
+ public void notifySessionUpdated(RoutingSessionInfo sessionInfo) {
+ Connection connection = mConnectionRef.get();
+ if (connection != null) {
+ connection.postSessionUpdated(sessionInfo);
+ }
+ }
+
+ @Override
+ public void notifySessionReleased(RoutingSessionInfo sessionInfo) {
+ Connection connection = mConnectionRef.get();
+ if (connection != null) {
+ connection.postSessionReleased(sessionInfo);
}
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 771edb2..d940e35 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -22,7 +22,6 @@
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;
@@ -31,8 +30,9 @@
import android.media.IMediaRouter2Manager;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
+import android.media.MediaRoute2ProviderService;
import android.media.RouteDiscoveryPreference;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -370,7 +370,7 @@
}
@NonNull
- public List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
+ public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -653,7 +653,7 @@
}
}
- private List<RouteSessionInfo> getActiveSessionsLocked(IMediaRouter2Manager manager) {
+ private List<RoutingSessionInfo> getActiveSessionsLocked(IMediaRouter2Manager manager) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -661,7 +661,7 @@
return Collections.emptyList();
}
- List<RouteSessionInfo> sessionInfos = new ArrayList<>();
+ List<RoutingSessionInfo> sessionInfos = new ArrayList<>();
for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mMediaProviders) {
sessionInfos.addAll(provider.getSessionInfos());
}
@@ -876,20 +876,27 @@
@Override
public void onSessionCreated(@NonNull MediaRoute2Provider provider,
- @Nullable RouteSessionInfo sessionInfo, long requestId) {
+ @NonNull RoutingSessionInfo sessionInfo, long requestId) {
sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionCreatedOnHandler,
this, provider, sessionInfo, requestId));
}
@Override
- public void onSessionInfoChanged(@NonNull MediaRoute2Provider provider,
- @NonNull RouteSessionInfo sessionInfo) {
+ public void onSessionCreationFailed(@NonNull MediaRoute2Provider provider, long requestId) {
+ sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionCreationFailedOnHandler,
+ this, provider, requestId));
+ }
+
+ @Override
+ public void onSessionUpdated(@NonNull MediaRoute2Provider provider,
+ @NonNull RoutingSessionInfo sessionInfo) {
sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionInfoChangedOnHandler,
this, provider, sessionInfo));
}
@Override
- public void onSessionReleased(MediaRoute2Provider provider, RouteSessionInfo sessionInfo) {
+ public void onSessionReleased(MediaRoute2Provider provider,
+ RoutingSessionInfo sessionInfo) {
sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionReleasedOnHandler,
this, provider, sessionInfo));
}
@@ -1131,7 +1138,14 @@
}
private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider,
- @Nullable RouteSessionInfo sessionInfo, long requestId) {
+ @NonNull RoutingSessionInfo sessionInfo, long requestId) {
+
+ if (requestId == MediaRoute2ProviderService.REQUEST_ID_UNKNOWN) {
+ // The session is created without any matching request.
+ // TODO: Tell managers for the session creation
+ return;
+ }
+
SessionCreationRequest matchingRequest = null;
for (SessionCreationRequest request : mSessionCreationRequests) {
@@ -1181,8 +1195,32 @@
// TODO: Tell managers for the session creation
}
+ private void onSessionCreationFailedOnHandler(@NonNull MediaRoute2Provider provider,
+ long requestId) {
+ SessionCreationRequest matchingRequest = null;
+
+ for (SessionCreationRequest request : mSessionCreationRequests) {
+ if (request.mRequestId == requestId
+ && TextUtils.equals(
+ request.mRoute.getProviderId(), provider.getUniqueId())) {
+ matchingRequest = request;
+ break;
+ }
+ }
+
+ if (matchingRequest == null) {
+ Slog.w(TAG, "Ignoring session creation failed result for unknown request. "
+ + "requestId=" + requestId);
+ return;
+ }
+
+ mSessionCreationRequests.remove(matchingRequest);
+ notifySessionCreationFailed(matchingRequest.mClientRecord,
+ toClientRequestId(requestId));
+ }
+
private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
- @NonNull RouteSessionInfo sessionInfo) {
+ @NonNull RoutingSessionInfo sessionInfo) {
Client2Record client2Record = mSessionToClientMap.get(
sessionInfo.getId());
@@ -1196,7 +1234,7 @@
}
private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider,
- @NonNull RouteSessionInfo sessionInfo) {
+ @NonNull RoutingSessionInfo sessionInfo) {
Client2Record client2Record = mSessionToClientMap.get(sessionInfo.getId());
if (client2Record == null) {
@@ -1208,8 +1246,8 @@
// TODO: Tell managers for the session release
}
- private void notifySessionCreated(Client2Record clientRecord, RouteSessionInfo sessionInfo,
- int requestId) {
+ private void notifySessionCreated(Client2Record clientRecord,
+ RoutingSessionInfo sessionInfo, int requestId) {
try {
clientRecord.mClient.notifySessionCreated(sessionInfo, requestId);
} catch (RemoteException ex) {
@@ -1228,7 +1266,7 @@
}
private void notifySessionInfoChanged(Client2Record clientRecord,
- RouteSessionInfo sessionInfo) {
+ RoutingSessionInfo sessionInfo) {
try {
clientRecord.mClient.notifySessionInfoChanged(sessionInfo);
} catch (RemoteException ex) {
@@ -1238,7 +1276,7 @@
}
private void notifySessionReleased(Client2Record clientRecord,
- RouteSessionInfo sessionInfo) {
+ RoutingSessionInfo sessionInfo) {
try {
clientRecord.mClient.notifySessionReleased(sessionInfo);
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 2dbaab4..b7aa484 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -40,7 +40,7 @@
import android.media.RemoteDisplayState;
import android.media.RemoteDisplayState.RemoteDisplayInfo;
import android.media.RouteDiscoveryPreference;
-import android.media.RouteSessionInfo;
+import android.media.RoutingSessionInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -552,7 +552,7 @@
// Binder call
@Override
- public List<RouteSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
+ public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
return mService2.getActiveSessions(manager);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index df115d0..1905571 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -135,6 +135,7 @@
private int mMaxVolume = 0;
private int mCurrentVolume = 0;
private int mOptimisticVolume = -1;
+ private String mVolumeControlId;
// End volume handling fields
private boolean mIsActive = false;
@@ -714,7 +715,7 @@
if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
int current = mOptimisticVolume != -1 ? mOptimisticVolume : mCurrentVolume;
return new PlaybackInfo(mVolumeType, mVolumeControlType, mMaxVolume, current,
- mAudioAttrs);
+ mAudioAttrs, mVolumeControlId);
}
volumeType = mVolumeType;
attributes = mAudioAttrs;
@@ -723,7 +724,7 @@
int max = mAudioManager.getStreamMaxVolume(stream);
int current = mAudioManager.getStreamVolume(stream);
return new PlaybackInfo(volumeType, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max,
- current, attributes);
+ current, attributes, null);
}
private final Runnable mClearOptimisticVolumeRunnable = new Runnable() {
@@ -886,6 +887,7 @@
synchronized (mLock) {
typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE;
mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL;
+ mVolumeControlId = null;
if (attributes != null) {
mAudioAttrs = attributes;
} else {
@@ -904,13 +906,15 @@
}
@Override
- public void setPlaybackToRemote(int control, int max) throws RemoteException {
+ public void setPlaybackToRemote(int control, int max, String controlId)
+ throws RemoteException {
boolean typeChanged;
synchronized (mLock) {
typeChanged = mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL;
mVolumeType = PlaybackInfo.PLAYBACK_TYPE_REMOTE;
mVolumeControlType = control;
mMaxVolume = max;
+ mVolumeControlId = controlId;
}
if (typeChanged) {
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index be0272c..6695227 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -30,12 +30,12 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.text.TextUtils;
import android.util.Log;
import com.android.internal.R;
import java.util.Collections;
+import java.util.List;
/**
* Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers.
@@ -55,6 +55,7 @@
private final IAudioService mAudioService;
private final Handler mHandler;
private final Context mContext;
+ private final BluetoothRouteProvider mBtRouteProvider;
private static ComponentName sComponentName = new ComponentName(
SystemMediaRoute2Provider.class.getPackageName$(),
@@ -62,7 +63,7 @@
//TODO: Clean up these when audio manager support multiple bt devices
MediaRoute2Info mDefaultRoute;
- MediaRoute2Info mBluetoothA2dpRoute;
+ @NonNull List<MediaRoute2Info> mBluetoothRoutes = Collections.EMPTY_LIST;
final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
@@ -87,6 +88,10 @@
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
+ mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> {
+ mBluetoothRoutes = routes;
+ publishRoutes();
+ });
initializeRoutes();
}
@@ -157,7 +162,15 @@
updateAudioRoutes(newAudioRoutes);
}
- publishRoutes();
+ mBluetoothRoutes = mBtRouteProvider.getBluetoothRoutes();
+
+ MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
+ builder.addRoute(mDefaultRoute);
+ for (MediaRoute2Info route : mBluetoothRoutes) {
+ builder.addRoute(route);
+ }
+ setProviderState(builder.build());
+ mHandler.post(() -> notifyProviderState());
}
void updateAudioRoutes(AudioRoutesInfo newRoutes) {
@@ -185,21 +198,6 @@
.addFeature(TYPE_LIVE_VIDEO)
.build();
- if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
- mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
- if (mCurAudioRoutesInfo.bluetoothName != null) {
- //TODO: mark as bluetooth once MediaRoute2Info has device type
- mBluetoothA2dpRoute = new MediaRoute2Info.Builder(BLUETOOTH_ROUTE_ID,
- mCurAudioRoutesInfo.bluetoothName)
- .setDescription(mContext.getResources().getText(
- R.string.bluetooth_a2dp_audio_route_name).toString())
- .addFeature(TYPE_LIVE_AUDIO)
- .build();
- } else {
- mBluetoothA2dpRoute = null;
- }
- }
-
publishRoutes();
}
@@ -207,15 +205,13 @@
* The first route should be the currently selected system route.
* For example, if there are two system routes (BT and device speaker),
* BT will be the first route in the list.
- *
- * TODO: Support multiple BT devices
*/
void publishRoutes() {
MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
- if (mBluetoothA2dpRoute != null) {
- builder.addRoute(mBluetoothA2dpRoute);
- }
builder.addRoute(mDefaultRoute);
- setAndNotifyProviderState(builder.build(), Collections.emptyList());
+ for (MediaRoute2Info route : mBluetoothRoutes) {
+ builder.addRoute(route);
+ }
+ setAndNotifyProviderState(builder.build());
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 7f650ee..b24a938 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -18,8 +18,10 @@
import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
+import android.annotation.NonNull;
import android.net.Network;
import android.net.NetworkTemplate;
+import android.net.netstats.provider.AbstractNetworkStatsProvider;
import android.telephony.SubscriptionPlan;
import java.util.Set;
@@ -126,4 +128,12 @@
*/
public abstract void setMeteredRestrictedPackagesAsync(
Set<String> packageNames, int userId);
+
+ /**
+ * Notifies that any of the {@link AbstractNetworkStatsProvider} has reached its quota
+ * which was set through {@link AbstractNetworkStatsProvider#setLimit(String, long)}.
+ *
+ * @param tag the human readable identifier of the custom network stats provider.
+ */
+ public abstract void onStatsProviderLimitReached(@NonNull String tag);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 4a45730..0959133 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -75,6 +75,7 @@
import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.net.netstats.provider.AbstractNetworkStatsProvider.QUOTA_UNLIMITED;
import static android.os.Trace.TRACE_TAG_NETWORK;
import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED;
import static android.provider.Settings.Global.NETPOLICY_QUOTA_ENABLED;
@@ -391,6 +392,7 @@
private static final int MSG_METERED_RESTRICTED_PACKAGES_CHANGED = 17;
private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18;
private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19;
+ private static final int MSG_STATS_PROVIDER_LIMIT_REACHED = 20;
private static final int UID_MSG_STATE_CHANGED = 100;
private static final int UID_MSG_GONE = 101;
@@ -4518,19 +4520,36 @@
mListeners.finishBroadcast();
return true;
}
- case MSG_LIMIT_REACHED: {
- final String iface = (String) msg.obj;
+ case MSG_STATS_PROVIDER_LIMIT_REACHED: {
+ mNetworkStats.forceUpdate();
synchronized (mNetworkPoliciesSecondLock) {
- if (mMeteredIfaces.contains(iface)) {
- // force stats update to make sure we have
- // numbers that caused alert to trigger.
- mNetworkStats.forceUpdate();
-
- updateNetworkEnabledNL();
- updateNotificationsNL();
+ // Some providers might hit the limit reached event prior to others. Thus,
+ // re-calculate and update interface quota for every provider is needed.
+ updateNetworkRulesNL();
+ updateNetworkEnabledNL();
+ updateNotificationsNL();
+ }
+ return true;
+ }
+ case MSG_LIMIT_REACHED: {
+ final String iface = (String) msg.obj;
+ synchronized (mNetworkPoliciesSecondLock) {
+ // fast return if not needed.
+ if (!mMeteredIfaces.contains(iface)) {
+ return true;
}
}
+
+ // force stats update to make sure the service have the numbers that caused
+ // alert to trigger.
+ mNetworkStats.forceUpdate();
+
+ synchronized (mNetworkPoliciesSecondLock) {
+ updateNetworkRulesNL();
+ updateNetworkEnabledNL();
+ updateNotificationsNL();
+ }
return true;
}
case MSG_RESTRICT_BACKGROUND_CHANGED: {
@@ -4573,14 +4592,18 @@
return true;
}
case MSG_UPDATE_INTERFACE_QUOTA: {
- removeInterfaceQuota((String) msg.obj);
+ final String iface = (String) msg.obj;
// int params need to be stitched back into a long
- setInterfaceQuota((String) msg.obj,
- ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL));
+ final long quota = ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL);
+ removeInterfaceQuota(iface);
+ setInterfaceQuota(iface, quota);
+ mNetworkStats.setStatsProviderLimit(iface, quota);
return true;
}
case MSG_REMOVE_INTERFACE_QUOTA: {
- removeInterfaceQuota((String) msg.obj);
+ final String iface = (String) msg.obj;
+ removeInterfaceQuota(iface);
+ mNetworkStats.setStatsProviderLimit(iface, QUOTA_UNLIMITED);
return true;
}
case MSG_RESET_FIREWALL_RULES_BY_UID: {
@@ -5235,6 +5258,12 @@
mHandler.obtainMessage(MSG_METERED_RESTRICTED_PACKAGES_CHANGED,
userId, 0, packageNames).sendToTarget();
}
+
+ @Override
+ public void onStatsProviderLimitReached(@NonNull String tag) {
+ Log.v(TAG, "onStatsProviderLimitReached: " + tag);
+ mHandler.obtainMessage(MSG_STATS_PROVIDER_LIMIT_REACHED).sendToTarget();
+ }
}
private void setMeteredRestrictedPackagesInternal(Set<String> packageNames, int userId) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
index 4843ede..6d72cb5 100644
--- a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
@@ -16,6 +16,7 @@
package com.android.server.net;
+import android.annotation.NonNull;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
@@ -34,4 +35,10 @@
/** Force update of statistics. */
public abstract void forceUpdate();
+
+ /**
+ * Set the quota limit to all registered custom network stats providers.
+ * Note that invocation of any interface will be sent to all providers.
+ */
+ public abstract void setStatsProviderLimit(@NonNull String iface, long quota);
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 7a6f297..1dcff07 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.content.Intent.ACTION_SHUTDOWN;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.ACTION_USER_REMOVED;
@@ -71,6 +72,7 @@
import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.usage.NetworkStatsManager;
@@ -97,6 +99,9 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
+import android.net.netstats.provider.INetworkStatsProvider;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProviderCallback;
import android.os.BestClock;
import android.os.Binder;
import android.os.DropBoxManager;
@@ -109,6 +114,7 @@
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -176,7 +182,7 @@
* This avoids firing the global alert too often on devices with high transfer speeds and
* high quota.
*/
- private static final int PERFORM_POLL_DELAY_MS = 1000;
+ private static final int DEFAULT_PERFORM_POLL_DELAY_MS = 1000;
private static final String TAG_NETSTATS_ERROR = "netstats_error";
@@ -220,6 +226,7 @@
*/
public interface NetworkStatsSettings {
public long getPollInterval();
+ public long getPollDelay();
public boolean getSampleEnabled();
public boolean getAugmentEnabled();
@@ -248,6 +255,7 @@
}
private final Object mStatsLock = new Object();
+ private final Object mStatsProviderLock = new Object();
/** Set of currently active ifaces. */
@GuardedBy("mStatsLock")
@@ -272,6 +280,9 @@
private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
new DropBoxNonMonotonicObserver();
+ private final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList =
+ new RemoteCallbackList<>();
+
@GuardedBy("mStatsLock")
private NetworkStatsRecorder mDevRecorder;
@GuardedBy("mStatsLock")
@@ -502,9 +513,9 @@
}
/**
- * Register for a global alert that is delivered through
- * {@link INetworkManagementEventObserver} once a threshold amount of data
- * has been transferred.
+ * Register for a global alert that is delivered through {@link INetworkManagementEventObserver}
+ * or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has
+ * been transferred.
*/
private void registerGlobalAlert() {
try {
@@ -514,6 +525,7 @@
} catch (RemoteException e) {
// ignored; service lives in system_server
}
+ invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setAlert(mGlobalAlertBytes));
}
@Override
@@ -803,8 +815,7 @@
@Override
public void incrementOperationCount(int uid, int tag, int operationCount) {
if (Binder.getCallingUid() != uid) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.UPDATE_DEVICE_STATS, TAG);
+ mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
}
if (operationCount < 0) {
@@ -1095,7 +1106,7 @@
/**
* Observer that watches for {@link INetworkManagementService} alerts.
*/
- private INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
+ private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
@Override
public void limitReached(String limitName, String iface) {
// only someone like NMS should be calling us
@@ -1106,7 +1117,7 @@
// such a call pending; UID stats are handled during normal polling interval.
if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
- PERFORM_POLL_DELAY_MS);
+ mSettings.getPollDelay());
}
}
}
@@ -1252,6 +1263,14 @@
xtSnapshot.combineAllValues(tetherSnapshot);
devSnapshot.combineAllValues(tetherSnapshot);
+ // Snapshot for dev/xt stats from all custom stats providers. Counts per-interface data
+ // from stats providers that isn't already counted by dev and XT stats.
+ Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotStatsProvider");
+ final NetworkStats providersnapshot = getNetworkStatsFromProviders(STATS_PER_IFACE);
+ Trace.traceEnd(TRACE_TAG_NETWORK);
+ xtSnapshot.combineAllValues(providersnapshot);
+ devSnapshot.combineAllValues(providersnapshot);
+
// For xt/dev, we pass a null VPN array because usage is aggregated by UID, so VPN traffic
// can't be reattributed to responsible apps.
Trace.traceBegin(TRACE_TAG_NETWORK, "recordDev");
@@ -1355,6 +1374,10 @@
performSampleLocked();
}
+ // request asynchronous stats update from all providers for next poll.
+ // TODO: request with a valid token.
+ invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */));
+
// finally, dispatch updated event to any listeners
final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -1476,6 +1499,12 @@
public void forceUpdate() {
NetworkStatsService.this.forceUpdate();
}
+
+ @Override
+ public void setStatsProviderLimit(@NonNull String iface, long quota) {
+ Slog.v(TAG, "setStatsProviderLimit(" + iface + "," + quota + ")");
+ invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setLimit(iface, quota));
+ }
}
@Override
@@ -1690,6 +1719,12 @@
uidSnapshot.combineAllValues(vtStats);
}
+ // get a stale copy of uid stats snapshot provided by providers.
+ final NetworkStats providerStats = getNetworkStatsFromProviders(STATS_PER_UID);
+ providerStats.filter(UID_ALL, ifaces, TAG_ALL);
+ mStatsFactory.apply464xlatAdjustments(uidSnapshot, providerStats, mUseBpfTrafficStats);
+ uidSnapshot.combineAllValues(providerStats);
+
uidSnapshot.combineAllValues(mUidOperations);
return uidSnapshot;
@@ -1726,6 +1761,152 @@
}
}
+ /**
+ * Registers a custom provider of {@link android.net.NetworkStats} to combine the network
+ * statistics that cannot be seen by the kernel to system. To unregister, invoke the
+ * {@code unregister()} of the returned callback.
+ *
+ * @param tag a human readable identifier of the custom network stats provider.
+ * @param provider the binder interface of
+ * {@link android.net.netstats.provider.AbstractNetworkStatsProvider} that
+ * needs to be registered to the system.
+ *
+ * @return a binder interface of
+ * {@link android.net.netstats.provider.NetworkStatsProviderCallback}, which can be
+ * used to report events to the system.
+ */
+ public @NonNull INetworkStatsProviderCallback registerNetworkStatsProvider(
+ @NonNull String tag, @NonNull INetworkStatsProvider provider) {
+ mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+ Objects.requireNonNull(provider, "provider is null");
+ Objects.requireNonNull(tag, "tag is null");
+ try {
+ NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
+ tag, provider, mAlertObserver, mStatsProviderCbList);
+ mStatsProviderCbList.register(callback);
+ Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
+ + getCallingUid() + "/" + getCallingPid());
+ return callback;
+ } catch (RemoteException e) {
+ Log.e(TAG, "registerNetworkStatsProvider failed", e);
+ }
+ return null;
+ }
+
+ // Collect stats from local cache of providers.
+ private @NonNull NetworkStats getNetworkStatsFromProviders(int how) {
+ final NetworkStats ret = new NetworkStats(0L, 0);
+ invokeForAllStatsProviderCallbacks((cb) -> ret.combineAllValues(cb.getCachedStats(how)));
+ return ret;
+ }
+
+ @FunctionalInterface
+ private interface ThrowingConsumer<S, T extends Throwable> {
+ void accept(S s) throws T;
+ }
+
+ private void invokeForAllStatsProviderCallbacks(
+ @NonNull ThrowingConsumer<NetworkStatsProviderCallbackImpl, RemoteException> task) {
+ synchronized (mStatsProviderCbList) {
+ final int length = mStatsProviderCbList.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ final NetworkStatsProviderCallbackImpl cb =
+ mStatsProviderCbList.getBroadcastItem(i);
+ try {
+ task.accept(cb);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Fail to broadcast to provider: " + cb.mTag, e);
+ }
+ }
+ } finally {
+ mStatsProviderCbList.finishBroadcast();
+ }
+ }
+ }
+
+ private static class NetworkStatsProviderCallbackImpl extends INetworkStatsProviderCallback.Stub
+ implements IBinder.DeathRecipient {
+ @NonNull final String mTag;
+ @NonNull private final Object mProviderStatsLock = new Object();
+ @NonNull final INetworkStatsProvider mProvider;
+ @NonNull final INetworkManagementEventObserver mAlertObserver;
+ @NonNull final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
+
+ @GuardedBy("mProviderStatsLock")
+ // STATS_PER_IFACE and STATS_PER_UID
+ private final NetworkStats mIfaceStats = new NetworkStats(0L, 0);
+ @GuardedBy("mProviderStatsLock")
+ private final NetworkStats mUidStats = new NetworkStats(0L, 0);
+
+ NetworkStatsProviderCallbackImpl(
+ @NonNull String tag, @NonNull INetworkStatsProvider provider,
+ @NonNull INetworkManagementEventObserver alertObserver,
+ @NonNull RemoteCallbackList<NetworkStatsProviderCallbackImpl> cbList)
+ throws RemoteException {
+ mTag = tag;
+ mProvider = provider;
+ mProvider.asBinder().linkToDeath(this, 0);
+ mAlertObserver = alertObserver;
+ mStatsProviderCbList = cbList;
+ }
+
+ @NonNull
+ public NetworkStats getCachedStats(int how) {
+ synchronized (mProviderStatsLock) {
+ NetworkStats stats;
+ switch (how) {
+ case STATS_PER_IFACE:
+ stats = mIfaceStats;
+ break;
+ case STATS_PER_UID:
+ stats = mUidStats;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid type: " + how);
+ }
+ // Return a defensive copy instead of local reference.
+ return stats.clone();
+ }
+ }
+
+ @Override
+ public void onStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
+ @Nullable NetworkStats uidStats) {
+ // TODO: 1. Use token to map ifaces to correct NetworkIdentity.
+ // 2. Store the difference and store it directly to the recorder.
+ synchronized (mProviderStatsLock) {
+ if (ifaceStats != null) mIfaceStats.combineAllValues(ifaceStats);
+ if (uidStats != null) mUidStats.combineAllValues(uidStats);
+ }
+ }
+
+ @Override
+ public void onAlertReached() throws RemoteException {
+ mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */);
+ }
+
+ @Override
+ public void onLimitReached() {
+ Log.d(TAG, mTag + ": onLimitReached");
+ LocalServices.getService(NetworkPolicyManagerInternal.class)
+ .onStatsProviderLimitReached(mTag);
+ }
+
+ @Override
+ public void binderDied() {
+ Log.d(TAG, mTag + ": binderDied");
+ mStatsProviderCbList.unregister(this);
+ }
+
+ @Override
+ public void unregister() {
+ Log.d(TAG, mTag + ": unregister");
+ mStatsProviderCbList.unregister(this);
+ }
+
+ }
+
@VisibleForTesting
static class HandlerCallback implements Handler.Callback {
private final NetworkStatsService mService;
@@ -1815,6 +1996,10 @@
return getGlobalLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
}
@Override
+ public long getPollDelay() {
+ return DEFAULT_PERFORM_POLL_DELAY_MS;
+ }
+ @Override
public long getGlobalAlertBytes(long def) {
return getGlobalLong(NETSTATS_GLOBAL_ALERT_BYTES, def);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index b782ca96..3c31f6a 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -83,6 +83,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -925,7 +926,7 @@
/**
* Updates the target packages' set of enabled overlays in PackageManager.
*/
- private void updateOverlayPaths(int userId, List<String> targetPackageNames) {
+ private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) {
try {
traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
if (DEBUG) {
@@ -955,6 +956,7 @@
}
}
+ final HashSet<String> updatedPackages = new HashSet<>();
final int n = targetPackageNames.size();
for (int i = 0; i < n; i++) {
final String targetPackageName = targetPackageNames.get(i);
@@ -965,11 +967,13 @@
}
if (!pm.setEnabledOverlayPackages(
- userId, targetPackageName, pendingChanges.get(targetPackageName))) {
+ userId, targetPackageName, pendingChanges.get(targetPackageName),
+ updatedPackages)) {
Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
targetPackageName, userId));
}
}
+ return new ArrayList<>(updatedPackages);
} finally {
traceEnd(TRACE_TAG_RRO);
}
@@ -980,10 +984,10 @@
}
private void updateAssets(final int userId, List<String> targetPackageNames) {
- updateOverlayPaths(userId, targetPackageNames);
final IActivityManager am = ActivityManager.getService();
try {
- am.scheduleApplicationInfoChanged(targetPackageNames, userId);
+ final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames);
+ am.scheduleApplicationInfoChanged(updatedPaths, userId);
} catch (RemoteException e) {
// Intentionally left empty.
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 019c952..9623542 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -685,7 +685,7 @@
// Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
if (targetPackage != null && overlayPackage != null
&& !("android".equals(targetPackageName)
- && overlayPackage.isStaticOverlayPackage())) {
+ && overlayPackage.isStaticOverlayPackage())) {
mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
}
@@ -703,9 +703,9 @@
if (currentState != newState) {
if (DEBUG) {
Slog.d(TAG, String.format("%s:%d: %s -> %s",
- overlayPackageName, userId,
- OverlayInfo.stateToString(currentState),
- OverlayInfo.stateToString(newState)));
+ overlayPackageName, userId,
+ OverlayInfo.stateToString(currentState),
+ OverlayInfo.stateToString(newState)));
}
modified |= mSettings.setState(overlayPackageName, userId, newState);
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index a009183..2807909 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -55,7 +55,6 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -376,6 +375,33 @@
}, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
}
+ private void populatePackageNameToApexModuleNameIfNeeded() {
+ synchronized (mLock) {
+ if (mPackageNameToApexModuleName != null) {
+ return;
+ }
+ try {
+ mPackageNameToApexModuleName = new ArrayMap<>();
+ final ApexInfo[] allPkgs = mApexService.getAllPackages();
+ for (int i = 0; i < allPkgs.length; i++) {
+ ApexInfo ai = allPkgs[i];
+ PackageParser.PackageLite pkgLite;
+ try {
+ File apexFile = new File(ai.modulePath);
+ pkgLite = PackageParser.parsePackageLite(apexFile, 0);
+ } catch (PackageParser.PackageParserException pe) {
+ throw new IllegalStateException("Unable to parse: "
+ + ai.modulePath, pe);
+ }
+ mPackageNameToApexModuleName.put(pkgLite.packageName, ai.moduleName);
+ }
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to retrieve packages from apexservice: ", re);
+ throw new RuntimeException(re);
+ }
+ }
+ }
+
private void populateAllPackagesCacheIfNeeded() {
synchronized (mLock) {
if (mAllPackagesCache != null) {
@@ -383,7 +409,6 @@
}
try {
mAllPackagesCache = new ArrayList<>();
- mPackageNameToApexModuleName = new HashMap<>();
HashSet<String> activePackagesSet = new HashSet<>();
HashSet<String> factoryPackagesSet = new HashSet<>();
final ApexInfo[] allPkgs = mApexService.getAllPackages();
@@ -409,7 +434,6 @@
final PackageInfo packageInfo =
PackageParser.generatePackageInfo(pkg, ai, flags);
mAllPackagesCache.add(packageInfo);
- mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
if (ai.isActive) {
if (activePackagesSet.contains(packageInfo.packageName)) {
throw new IllegalStateException(
@@ -612,8 +636,7 @@
@Override
List<String> getApksInApex(String apexPackageName) {
- // TODO(b/142712057): Avoid calling populateAllPackagesCacheIfNeeded during boot.
- populateAllPackagesCacheIfNeeded();
+ populatePackageNameToApexModuleNameIfNeeded();
synchronized (mLock) {
String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
if (moduleName == null) {
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index c4bcf80..3e76096 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -449,6 +449,7 @@
}
final PackageSetting callingPkgSetting;
final ArraySet<PackageSetting> callingSharedPkgSettings;
+ Trace.beginSection("callingSetting instanceof");
if (callingSetting instanceof PackageSetting) {
callingPkgSetting = (PackageSetting) callingSetting;
callingSharedPkgSettings = null;
@@ -456,6 +457,7 @@
callingPkgSetting = null;
callingSharedPkgSettings = ((SharedUserSetting) callingSetting).packages;
}
+ Trace.endSection();
if (callingPkgSetting != null) {
if (!mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
@@ -485,6 +487,7 @@
return true;
}
final String targetName = targetPkg.getPackageName();
+ Trace.beginSection("getAppId");
final int callingAppId;
if (callingPkgSetting != null) {
callingAppId = callingPkgSetting.appId;
@@ -492,6 +495,7 @@
callingAppId = callingSharedPkgSettings.valueAt(0).appId; // all should be the same
}
final int targetAppId = targetPkgSetting.appId;
+ Trace.endSection();
if (callingAppId == targetAppId) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "same app id");
@@ -499,38 +503,64 @@
return false;
}
- if (callingSetting.getPermissionsState().hasPermission(
- Manifest.permission.QUERY_ALL_PACKAGES, UserHandle.getUserId(callingUid))) {
- if (DEBUG_LOGGING) {
- log(callingSetting, targetPkgSetting, "has query-all permission");
+ try {
+ Trace.beginSection("hasPermission");
+ if (callingSetting.getPermissionsState().hasPermission(
+ Manifest.permission.QUERY_ALL_PACKAGES, UserHandle.getUserId(callingUid))) {
+ if (DEBUG_LOGGING) {
+ log(callingSetting, targetPkgSetting, "has query-all permission");
+ }
+ return false;
}
- return false;
+ } finally {
+ Trace.endSection();
}
- if (mForceQueryable.contains(targetAppId)) {
- if (DEBUG_LOGGING) {
- log(callingSetting, targetPkgSetting, "force queryable");
+ try {
+ Trace.beginSection("mForceQueryable");
+ if (mForceQueryable.contains(targetAppId)) {
+ if (DEBUG_LOGGING) {
+ log(callingSetting, targetPkgSetting, "force queryable");
+ }
+ return false;
}
- return false;
+ } finally {
+ Trace.endSection();
}
- if (mQueriesViaPackage.contains(callingAppId, targetAppId)) {
- // the calling package has explicitly declared the target package; allow
- if (DEBUG_LOGGING) {
- log(callingSetting, targetPkgSetting, "queries package");
+ try {
+ Trace.beginSection("mQueriesViaPackage");
+ if (mQueriesViaPackage.contains(callingAppId, targetAppId)) {
+ // the calling package has explicitly declared the target package; allow
+ if (DEBUG_LOGGING) {
+ log(callingSetting, targetPkgSetting, "queries package");
+ }
+ return false;
}
- return false;
- } else if (mQueriesViaIntent.contains(callingAppId, targetAppId)) {
- if (DEBUG_LOGGING) {
- log(callingSetting, targetPkgSetting, "queries intent");
+ } finally {
+ Trace.endSection();
+ }
+ try {
+ Trace.beginSection("mQueriesViaIntent");
+ if (mQueriesViaIntent.contains(callingAppId, targetAppId)) {
+ if (DEBUG_LOGGING) {
+ log(callingSetting, targetPkgSetting, "queries intent");
+ }
+ return false;
}
- return false;
+ } finally {
+ Trace.endSection();
}
- final int targetUid = UserHandle.getUid(userId, targetAppId);
- if (mImplicitlyQueryable.contains(callingUid, targetUid)) {
- if (DEBUG_LOGGING) {
- log(callingSetting, targetPkgSetting, "implicitly queryable for user");
+ try {
+ Trace.beginSection("mImplicitlyQueryable");
+ final int targetUid = UserHandle.getUid(userId, targetAppId);
+ if (mImplicitlyQueryable.contains(callingUid, targetUid)) {
+ if (DEBUG_LOGGING) {
+ log(callingSetting, targetPkgSetting, "implicitly queryable for user");
+ }
+ return false;
}
- return false;
+ } finally {
+ Trace.endSection();
}
if (callingPkgSetting != null) {
if (callingPkgInstruments(callingPkgSetting, targetPkgSetting, targetName)) {
@@ -576,17 +606,22 @@
private static boolean callingPkgInstruments(PackageSetting callingPkgSetting,
PackageSetting targetPkgSetting,
String targetName) {
- final List<ComponentParseUtils.ParsedInstrumentation> inst =
- callingPkgSetting.pkg.getInstrumentations();
- for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
- if (Objects.equals(inst.get(i).getTargetPackage(), targetName)) {
- if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "instrumentation");
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingPkgInstruments");
+ final List<ComponentParseUtils.ParsedInstrumentation> inst =
+ callingPkgSetting.pkg.getInstrumentations();
+ for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
+ if (Objects.equals(inst.get(i).getTargetPackage(), targetName)) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "instrumentation");
+ }
+ return true;
}
- return true;
}
+ return false;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- return false;
}
private static void log(SettingBase callingPkgSetting, PackageSetting targetPkgSetting,
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index ffcd6cf..bcfe577 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -338,9 +338,8 @@
}
@GuardedBy("mService.mLock")
- public void onPackageUninstalledLPw(@NonNull AndroidPackage pkg,
+ public void onPackageUninstalledLPw(@NonNull AndroidPackage pkg, @Nullable PackageSetting ps,
@NonNull int[] userIds) {
- PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
if (ps == null) {
return;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 17870eb..159b4e4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11602,6 +11602,23 @@
return false;
}
SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx);
+
+ // Remove the shared library overlays from its dependent packages.
+ for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
+ final List<VersionedPackage> dependents = getPackagesUsingSharedLibraryLPr(
+ libraryInfo, 0, currentUserId);
+ if (dependents == null) {
+ continue;
+ }
+ for (VersionedPackage dependentPackage : dependents) {
+ final PackageSetting ps = mSettings.mPackages.get(
+ dependentPackage.getPackageName());
+ if (ps != null) {
+ ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId);
+ }
+ }
+ }
+
versionedLib.remove(version);
if (versionedLib.size() <= 0) {
mSharedLibraries.remove(name);
@@ -15210,6 +15227,29 @@
// upcoming call to mSettings.writeLPr().
}
}
+
+ // Retrieve the overlays for shared libraries of the package.
+ if (pkg.getUsesLibraryInfos() != null) {
+ for (SharedLibraryInfo sharedLib : pkg.getUsesLibraryInfos()) {
+ for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
+ if (!sharedLib.isDynamic()) {
+ // TODO(146804378): Support overlaying static shared libraries
+ continue;
+ }
+ final PackageSetting libPs = mSettings.mPackages.get(
+ sharedLib.getPackageName());
+ if (libPs == null) {
+ continue;
+ }
+ final String[] overlayPaths = libPs.getOverlayPaths(currentUserId);
+ if (overlayPaths != null) {
+ ps.setOverlayPathsForLibrary(sharedLib.getName(),
+ Arrays.asList(overlayPaths), currentUserId);
+ }
+ }
+ }
+ }
+
// It's implied that when a user requests installation, they want the app to be
// installed and enabled.
if (userId != UserHandle.USER_ALL) {
@@ -17462,7 +17502,8 @@
synchronized (mLock) {
if (res) {
if (pkg != null) {
- mInstantAppRegistry.onPackageUninstalledLPw(pkg, info.removedUsers);
+ mInstantAppRegistry.onPackageUninstalledLPw(pkg, uninstalledPs,
+ info.removedUsers);
}
updateSequenceNumberLP(uninstalledPs, info.removedUsers);
updateInstantAppInstallerLocked(packageName);
@@ -23249,9 +23290,11 @@
@Override
public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
- @Nullable List<String> overlayPackageNames) {
+ @Nullable List<String> overlayPackageNames,
+ @NonNull Collection<String> outUpdatedPackageNames) {
synchronized (mLock) {
- if (targetPackageName == null || mPackages.get(targetPackageName) == null) {
+ final AndroidPackage targetPkg = mPackages.get(targetPackageName);
+ if (targetPackageName == null || targetPkg == null) {
Slog.e(TAG, "failed to find package " + targetPackageName);
return false;
}
@@ -23270,8 +23313,41 @@
}
}
+ ArraySet<String> updatedPackageNames = null;
+ if (targetPkg.getLibraryNames() != null) {
+ // Set the overlay paths for dependencies of the shared library.
+ updatedPackageNames = new ArraySet<>();
+ for (String libName : targetPkg.getLibraryNames()) {
+ final SharedLibraryInfo info = getSharedLibraryInfoLPr(libName,
+ SharedLibraryInfo.VERSION_UNDEFINED);
+ if (info == null) {
+ continue;
+ }
+ final List<VersionedPackage> dependents = getPackagesUsingSharedLibraryLPr(
+ info, 0, userId);
+ if (dependents == null) {
+ continue;
+ }
+ for (VersionedPackage dependent : dependents) {
+ final PackageSetting ps = mSettings.mPackages.get(
+ dependent.getPackageName());
+ if (ps == null) {
+ continue;
+ }
+ ps.setOverlayPathsForLibrary(libName, overlayPaths, userId);
+ updatedPackageNames.add(dependent.getPackageName());
+ }
+ }
+ }
+
final PackageSetting ps = mSettings.mPackages.get(targetPackageName);
ps.setOverlayPaths(overlayPaths, userId);
+
+ outUpdatedPackageNames.add(targetPackageName);
+ if (updatedPackageNames != null) {
+ outUpdatedPackageNames.addAll(updatedPackageNames);
+ }
+
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 0c0b93b..f1ac0af 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -41,6 +41,7 @@
import java.io.File;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -312,12 +313,21 @@
}
void setOverlayPaths(List<String> overlayPaths, int userId) {
- modifyUserState(userId).overlayPaths = overlayPaths == null ? null :
- overlayPaths.toArray(new String[overlayPaths.size()]);
+ modifyUserState(userId).setOverlayPaths(overlayPaths == null ? null :
+ overlayPaths.toArray(new String[overlayPaths.size()]));
}
String[] getOverlayPaths(int userId) {
- return readUserState(userId).overlayPaths;
+ return readUserState(userId).getOverlayPaths();
+ }
+
+ void setOverlayPathsForLibrary(String libName, List<String> overlayPaths, int userId) {
+ modifyUserState(userId).setSharedLibraryOverlayPaths(libName,
+ overlayPaths == null ? null : overlayPaths.toArray(new String[0]));
+ }
+
+ Map<String, String[]> getOverlayPathsForLibrary(int userId) {
+ return readUserState(userId).getSharedLibraryOverlayPaths();
}
/** Only use for testing. Do NOT use in production code. */
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ec84b51..3e665c3 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4742,6 +4742,22 @@
}
}
+ Map<String, String[]> sharedLibraryOverlayPaths =
+ ps.getOverlayPathsForLibrary(user.id);
+ if (sharedLibraryOverlayPaths != null) {
+ for (Map.Entry<String, String[]> libOverlayPaths :
+ sharedLibraryOverlayPaths.entrySet()) {
+ if (libOverlayPaths.getValue() == null) {
+ continue;
+ }
+ pw.print(prefix); pw.print(" ");
+ pw.print(libOverlayPaths.getKey()); pw.println(" overlay paths:");
+ for (String path : libOverlayPaths.getValue()) {
+ pw.print(prefix); pw.print(" "); pw.println(path);
+ }
+ }
+ }
+
String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
if (lastDisabledAppCaller != null) {
pw.print(prefix); pw.print(" lastDisabledCaller: ");
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 6d6ec25..46893b2 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -592,12 +592,6 @@
getDefaultSystemHandlerActivityPackageForCategory(Intent.CATEGORY_APP_MAPS, userId),
userId, ALWAYS_LOCATION_PERMISSIONS);
- // Gallery
- grantPermissionsToSystemPackage(
- getDefaultSystemHandlerActivityPackageForCategory(
- Intent.CATEGORY_APP_GALLERY, userId),
- userId, STORAGE_PERMISSIONS);
-
// Email
grantPermissionsToSystemPackage(
getDefaultSystemHandlerActivityPackageForCategory(
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index 5271493..6daf516 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -743,8 +743,8 @@
} else if (TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
// Airplane mode can be changed after ECM exits if airplane toggle button
// is pressed during ECM mode
- if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
- mIsWaitingForEcmExit) {
+ if (!(intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false))
+ && mIsWaitingForEcmExit) {
mIsWaitingForEcmExit = false;
changeAirplaneModeSystemSetting(true);
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
new file mode 100644
index 0000000..b19e2ed
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+
+/**
+ * A factory for creating instances of {@link ISoundTriggerHw}.
+ *
+ * @hide
+ */
+public interface HalFactory {
+ /**
+ * @return An instance of {@link ISoundTriggerHw}.
+ */
+ ISoundTriggerHw create();
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
index 9d51b65..9f4b09a 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
@@ -78,18 +78,17 @@
}
/**
- * Most generic constructor - gets an array of HAL driver instances.
+ * Constructor - gets an array of HAL driver factories.
*/
- public SoundTriggerMiddlewareImpl(@NonNull ISoundTriggerHw[] halServices,
+ public SoundTriggerMiddlewareImpl(@NonNull HalFactory[] halFactories,
@NonNull AudioSessionProvider audioSessionProvider) {
- List<SoundTriggerModule> modules = new ArrayList<>(halServices.length);
+ List<SoundTriggerModule> modules = new ArrayList<>(halFactories.length);
- for (int i = 0; i < halServices.length; ++i) {
- ISoundTriggerHw service = halServices[i];
+ for (int i = 0; i < halFactories.length; ++i) {
try {
- modules.add(new SoundTriggerModule(service, audioSessionProvider));
+ modules.add(new SoundTriggerModule(halFactories[i], audioSessionProvider));
} catch (Exception e) {
- Log.e(TAG, "Failed to a SoundTriggerModule instance", e);
+ Log.e(TAG, "Failed to add a SoundTriggerModule instance", e);
}
}
@@ -97,11 +96,11 @@
}
/**
- * Convenience constructor - gets a single HAL driver instance.
+ * Convenience constructor - gets a single HAL factory.
*/
- public SoundTriggerMiddlewareImpl(@NonNull ISoundTriggerHw halService,
+ public SoundTriggerMiddlewareImpl(@NonNull HalFactory factory,
@NonNull AudioSessionProvider audioSessionProvider) {
- this(new ISoundTriggerHw[]{halService}, audioSessionProvider);
+ this(new HalFactory[]{factory}, audioSessionProvider);
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index 5a06a2c..12f9fd9 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -113,9 +113,9 @@
public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareService.Stub {
static private final String TAG = "SoundTriggerMiddlewareService";
- final ISoundTriggerMiddlewareService mDelegate;
- final Context mContext;
- Set<Integer> mModuleHandles;
+ private final ISoundTriggerMiddlewareService mDelegate;
+ private final Context mContext;
+ private Set<Integer> mModuleHandles;
/**
* Constructor for internal use only. Could be exposed for testing purposes in the future.
@@ -280,7 +280,7 @@
}
/** Activity state. */
- public Activity activityState = Activity.LOADED;
+ Activity activityState = Activity.LOADED;
/**
* A map of known parameter support. A missing key means we don't know yet whether the
@@ -294,7 +294,7 @@
*
* @param modelParam The parameter key.
*/
- public void checkSupported(int modelParam) {
+ void checkSupported(int modelParam) {
if (!parameterSupport.containsKey(modelParam)) {
throw new IllegalStateException("Parameter has not been checked for support.");
}
@@ -311,7 +311,7 @@
* @param modelParam The parameter key.
* @param value The value.
*/
- public void checkSupported(int modelParam, int value) {
+ void checkSupported(int modelParam, int value) {
if (!parameterSupport.containsKey(modelParam)) {
throw new IllegalStateException("Parameter has not been checked for support.");
}
@@ -329,7 +329,7 @@
* @param modelParam The parameter key.
* @param range The parameter value range, or null if not supported.
*/
- public void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
+ void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
parameterSupport.put(modelParam, range);
}
}
@@ -338,27 +338,26 @@
* Entry-point to this module: exposes the module as a {@link SystemService}.
*/
public static final class Lifecycle extends SystemService {
- private SoundTriggerMiddlewareService mService;
-
public Lifecycle(Context context) {
super(context);
}
@Override
public void onStart() {
- ISoundTriggerHw[] services;
- try {
- services = new ISoundTriggerHw[]{ISoundTriggerHw.getService(true)};
- Log.d(TAG, "Connected to default ISoundTriggerHw");
- } catch (Exception e) {
- Log.e(TAG, "Failed to connect to default ISoundTriggerHw", e);
- services = new ISoundTriggerHw[0];
- }
+ HalFactory[] factories = new HalFactory[]{() -> {
+ try {
+ Log.d(TAG, "Connecting to default ISoundTriggerHw");
+ return ISoundTriggerHw.getService(true);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }};
- mService = new SoundTriggerMiddlewareService(
- new SoundTriggerMiddlewareImpl(services, new AudioSessionProviderImpl()),
- getContext());
- publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE, mService);
+ publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE,
+ new SoundTriggerMiddlewareService(
+ new SoundTriggerMiddlewareImpl(factories,
+ new AudioSessionProviderImpl()),
+ getContext()));
}
}
@@ -370,7 +369,7 @@
DeathRecipient {
private final ISoundTriggerCallback mCallback;
private ISoundTriggerModule mDelegate;
- private Map<Integer, ModelState> mLoadedModels = new HashMap<>();
+ private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
ModuleService(@NonNull ISoundTriggerCallback callback) {
mCallback = callback;
@@ -680,7 +679,7 @@
} catch (RemoteException e) {
// Dead client will be handled by binderDied() - no need to handle here.
// In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
+ Log.e(TAG, "Client callback exception.", e);
}
}
}
@@ -696,20 +695,33 @@
} catch (RemoteException e) {
// Dead client will be handled by binderDied() - no need to handle here.
// In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
+ Log.e(TAG, "Client callback exception.", e);
}
}
}
@Override
- public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
+ public void onRecognitionAvailabilityChange(boolean available) {
synchronized (this) {
try {
mCallback.onRecognitionAvailabilityChange(available);
} catch (RemoteException e) {
// Dead client will be handled by binderDied() - no need to handle here.
// In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
+ Log.e(TAG, "Client callback exception.", e);
+ }
+ }
+ }
+
+ @Override
+ public void onModuleDied() {
+ synchronized (this) {
+ try {
+ mCallback.onModuleDied();
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
}
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index f024ede..adf16fa 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -30,7 +30,9 @@
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
+import android.os.IHwBinder;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.util.Log;
import java.util.HashMap;
@@ -57,6 +59,7 @@
* gracefully handle driver malfunction and such behavior will result in undefined behavior. If this
* service is to used with an untrusted driver, the driver must be wrapped with validation / error
* recovery code.
+ * <li>Recovery from driver death is supported.</li>
* <li>RemoteExceptions thrown by the driver are treated as RuntimeExceptions - they are not
* considered recoverable faults and should not occur in a properly functioning system.
* <li>There is no binder instance associated with this implementation. Do not call asBinder().
@@ -79,27 +82,29 @@
*
* @hide
*/
-class SoundTriggerModule {
+class SoundTriggerModule implements IHwBinder.DeathRecipient {
static private final String TAG = "SoundTriggerModule";
- @NonNull private final ISoundTriggerHw2 mHalService;
+ @NonNull private HalFactory mHalFactory;
+ @NonNull private ISoundTriggerHw2 mHalService;
@NonNull private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider;
private final Set<Session> mActiveSessions = new HashSet<>();
private int mNumLoadedModels = 0;
- private SoundTriggerModuleProperties mProperties = null;
+ private SoundTriggerModuleProperties mProperties;
private boolean mRecognitionAvailable;
/**
* Ctor.
*
- * @param halService The underlying HAL driver.
+ * @param halFactory A factory for the underlying HAL driver.
*/
- SoundTriggerModule(@NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw halService,
+ SoundTriggerModule(@NonNull HalFactory halFactory,
@NonNull SoundTriggerMiddlewareImpl.AudioSessionProvider audioSessionProvider) {
- assert halService != null;
- mHalService = new SoundTriggerHw2Compat(halService);
+ assert halFactory != null;
+ mHalFactory = halFactory;
mAudioSessionProvider = audioSessionProvider;
- mProperties = ConversionUtil.hidl2aidlProperties(mHalService.getProperties());
+ attachToHal();
+ mProperties = ConversionUtil.hidl2aidlProperties(mHalService.getProperties());
// We conservatively assume that external capture is active until explicitly told otherwise.
mRecognitionAvailable = mProperties.concurrentCapture;
}
@@ -117,7 +122,7 @@
* @return The interface through which this module can be controlled.
*/
synchronized @NonNull
- Session attach(@NonNull ISoundTriggerCallback callback) {
+ ISoundTriggerModule attach(@NonNull ISoundTriggerCallback callback) {
Log.d(TAG, "attach()");
Session session = new Session(callback);
mActiveSessions.add(session);
@@ -145,6 +150,7 @@
*/
synchronized void setExternalCaptureState(boolean active) {
Log.d(TAG, String.format("setExternalCaptureState(active=%b)", active));
+
if (mProperties.concurrentCapture) {
// If we support concurrent capture, we don't care about any of this.
return;
@@ -162,6 +168,23 @@
}
}
+ @Override
+ public synchronized void serviceDied(long cookie) {
+ Log.w(TAG, String.format("Underlying HAL driver died."));
+ for (Session session : mActiveSessions) {
+ session.moduleDied();
+ }
+ attachToHal();
+ }
+
+ /**
+ * Attached to the HAL service via factory.
+ */
+ private void attachToHal() {
+ mHalService = new SoundTriggerHw2Compat(mHalFactory.create());
+ mHalService.linkToDeath(this, 0);
+ }
+
/**
* Remove session from the list of active sessions.
*
@@ -204,7 +227,11 @@
public void detach() {
Log.d(TAG, "detach()");
synchronized (SoundTriggerModule.this) {
+ if (mCallback == null) {
+ return;
+ }
removeSession(this);
+ mCallback = null;
}
}
@@ -212,6 +239,7 @@
public int loadModel(@NonNull SoundModel model) {
Log.d(TAG, String.format("loadModel(model=%s)", model));
synchronized (SoundTriggerModule.this) {
+ checkValid();
if (mNumLoadedModels == mProperties.maxSoundModels) {
throw new RecoverableException(Status.RESOURCE_CONTENTION,
"Maximum number of models loaded.");
@@ -227,6 +255,7 @@
public int loadPhraseModel(@NonNull PhraseSoundModel model) {
Log.d(TAG, String.format("loadPhraseModel(model=%s)", model));
synchronized (SoundTriggerModule.this) {
+ checkValid();
if (mNumLoadedModels == mProperties.maxSoundModels) {
throw new RecoverableException(Status.RESOURCE_CONTENTION,
"Maximum number of models loaded.");
@@ -243,6 +272,7 @@
public void unloadModel(int modelHandle) {
Log.d(TAG, String.format("unloadModel(handle=%d)", modelHandle));
synchronized (SoundTriggerModule.this) {
+ checkValid();
mLoadedModels.get(modelHandle).unload();
--mNumLoadedModels;
}
@@ -253,6 +283,7 @@
Log.d(TAG,
String.format("startRecognition(handle=%d, config=%s)", modelHandle, config));
synchronized (SoundTriggerModule.this) {
+ checkValid();
mLoadedModels.get(modelHandle).startRecognition(config);
}
}
@@ -269,26 +300,28 @@
public void forceRecognitionEvent(int modelHandle) {
Log.d(TAG, String.format("forceRecognitionEvent(handle=%d)", modelHandle));
synchronized (SoundTriggerModule.this) {
+ checkValid();
mLoadedModels.get(modelHandle).forceRecognitionEvent();
}
}
@Override
- public void setModelParameter(int modelHandle, int modelParam, int value)
- throws RemoteException {
+ public void setModelParameter(int modelHandle, int modelParam, int value) {
Log.d(TAG,
String.format("setModelParameter(handle=%d, param=%d, value=%d)", modelHandle,
modelParam, value));
synchronized (SoundTriggerModule.this) {
+ checkValid();
mLoadedModels.get(modelHandle).setParameter(modelParam, value);
}
}
@Override
- public int getModelParameter(int modelHandle, int modelParam) throws RemoteException {
+ public int getModelParameter(int modelHandle, int modelParam) {
Log.d(TAG, String.format("getModelParameter(handle=%d, param=%d)", modelHandle,
modelParam));
synchronized (SoundTriggerModule.this) {
+ checkValid();
return mLoadedModels.get(modelHandle).getParameter(modelParam);
}
}
@@ -299,6 +332,7 @@
Log.d(TAG, String.format("queryModelParameterSupport(handle=%d, param=%d)", modelHandle,
modelParam));
synchronized (SoundTriggerModule.this) {
+ checkValid();
return mLoadedModels.get(modelHandle).queryModelParameterSupport(modelParam);
}
}
@@ -322,6 +356,25 @@
}
}
+ /**
+ * The underlying module HAL is dead.
+ */
+ private void moduleDied() {
+ try {
+ mCallback.onModuleDied();
+ removeSession(this);
+ mCallback = null;
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ private void checkValid() {
+ if (mCallback == null) {
+ throw new ServiceSpecificException(Status.DEAD_OBJECT);
+ }
+ }
+
@Override
public @NonNull
IBinder asBinder() {
@@ -350,10 +403,6 @@
SoundTriggerModule.this.notifyAll();
}
- private void waitStateChange() throws InterruptedException {
- SoundTriggerModule.this.wait();
- }
-
private int load(@NonNull SoundModel model) {
mModelType = model.type;
ISoundTriggerHw.SoundModel hidlModel = ConversionUtil.aidl2hidlSoundModel(model);
diff --git a/services/core/java/com/android/server/stats/OWNERS b/services/core/java/com/android/server/stats/OWNERS
index 8d7f882..fc7fd22 100644
--- a/services/core/java/com/android/server/stats/OWNERS
+++ b/services/core/java/com/android/server/stats/OWNERS
@@ -1,9 +1,8 @@
-bookatz@google.com
-cjyu@google.com
-dwchen@google.com
+jeffreyhuang@google.com
joeo@google.com
+muhammadq@google.com
+ruchirr@google.com
singhtejinder@google.com
-stlafon@google.com
+tsaichristine@google.com
yaochen@google.com
-yanglu@google.com
-yro@google.com
\ No newline at end of file
+yro@google.com
diff --git a/services/core/java/com/android/server/stats/StatsPullAtomService.java b/services/core/java/com/android/server/stats/StatsPullAtomService.java
index a75f1c2..45dee98 100644
--- a/services/core/java/com/android/server/stats/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/StatsPullAtomService.java
@@ -186,6 +186,12 @@
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;
@@ -204,6 +210,18 @@
@Override
public void onStart() {
mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER);
+
+ // 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
@@ -327,11 +345,12 @@
}
private void registerWifiBytesTransfer() {
int tagId = StatsLog.WIFI_BYTES_TRANSFER;
- PullAtomMetadata metaData = PullAtomMetadata.newBuilder()
- .setAdditiveFields(new int[] {2, 3, 4, 5}).build();
+ PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ .setAdditiveFields(new int[] {2, 3, 4, 5})
+ .build();
mStatsManager.registerPullAtomCallback(
tagId,
- metaData,
+ metadata,
(atomTag, data) -> pullWifiBytesTransfer(atomTag, data),
Executors.newSingleThreadExecutor()
);
@@ -356,6 +375,7 @@
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);
}
@@ -382,36 +402,224 @@
}
}
- private void registerWifiBytesTransferBackground() {
- // No op.
+ /**
+ * 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 pullWifiBytesTransferBackground() {
- // No op.
+ 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() {
- // No op.
+ 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 void pullMobileBytesTransfer() {
- // No op.
+ 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() {
- // No op.
+ 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 void pullMobileBytesTransferBackground() {
- // No op.
+ 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() {
- // No op.
+ 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()
+ );
}
- private void pullBluetoothBytesTransfer() {
- // No op.
+ /**
+ * 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 void registerKernelWakelock() {
@@ -422,44 +630,162 @@
// No op.
}
+ 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() {
- // No op.
+ 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 void pullCpuTimePerFreq() {
- // No op.
+ 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() {
- // No op.
+ 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 void pullCpuTimePerUid() {
- // No op.
+ 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() {
- // No op.
+ // 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 void pullCpuTimeperUidFreq() {
- // No op.
+ 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() {
- // No op.
+ // 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 void pullCpuActiveTime() {
- // No op.
+ 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() {
- // No op.
+ // 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() {
- return 0;
+ 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() {
@@ -479,11 +805,31 @@
}
private void registerBluetoothActivityInfo() {
- // No op.
+ int tagId = StatsLog.BLUETOOTH_ACTIVITY_INFO;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ /* metadata */ null,
+ (atomTag, data) -> pullBluetoothActivityInfo(atomTag, data),
+ Executors.newSingleThreadExecutor()
+ );
}
- private void pullBluetoothActivityInfo() {
- // No op.
+ 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() {
@@ -695,11 +1041,26 @@
}
private void registerPowerProfile() {
- // No op.
+ int tagId = StatsLog.POWER_PROFILE;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ /* PullAtomMetadata */ null,
+ (atomTag, data) -> pullPowerProfile(atomTag, data),
+ Executors.newSingleThreadExecutor()
+ );
}
- private void pullPowerProfile() {
- // No op.
+ 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() {
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index f661b5e..468b806 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -21,7 +21,6 @@
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.PhoneTimeSuggestion;
-import android.content.Intent;
import android.os.TimestampedValue;
import java.io.PrintWriter;
@@ -62,10 +61,10 @@
/** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */
void acquireWakeLock();
- /** Returns the elapsedRealtimeMillis clock value. The WakeLock must be held. */
+ /** Returns the elapsedRealtimeMillis clock value. */
long elapsedRealtimeMillis();
- /** Returns the system clock value. The WakeLock must be held. */
+ /** Returns the system clock value. */
long systemClockMillis();
/** Sets the device system clock. The WakeLock must be held. */
@@ -73,9 +72,6 @@
/** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */
void releaseWakeLock();
-
- /** Send the supplied intent as a stick broadcast. */
- void sendStickyBroadcast(@NonNull Intent intent);
}
/** Initialize the strategy. */
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
index 42d59d5..19484db 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
@@ -20,11 +20,9 @@
import android.app.AlarmManager;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
@@ -90,13 +88,11 @@
@Override
public long elapsedRealtimeMillis() {
- checkWakeLockHeld();
return SystemClock.elapsedRealtime();
}
@Override
public long systemClockMillis() {
- checkWakeLockHeld();
return System.currentTimeMillis();
}
@@ -112,11 +108,6 @@
mWakeLock.release();
}
- @Override
- public void sendStickyBroadcast(@NonNull Intent intent) {
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
private void checkWakeLockHeld() {
if (!mWakeLock.isHeld()) {
Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held");
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index da848d8..e95fc4a 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -23,9 +23,7 @@
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.PhoneTimeSuggestion;
-import android.content.Intent;
import android.os.TimestampedValue;
-import android.telephony.TelephonyManager;
import android.util.LocalLog;
import android.util.Slog;
@@ -535,17 +533,6 @@
} else {
mLastAutoSystemClockTimeSet = null;
}
-
- // Historically, Android has sent a TelephonyManager.ACTION_NETWORK_SET_TIME broadcast only
- // when setting the time using NITZ.
- if (origin == ORIGIN_PHONE) {
- // Send a broadcast that telephony code used to send after setting the clock.
- // TODO Remove this broadcast as soon as there are no remaining listeners.
- Intent intent = new Intent(TelephonyManager.ACTION_NETWORK_SET_TIME);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra("time", newSystemClockMillis);
- mCallback.sendStickyBroadcast(intent);
- }
}
/**
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 5b58199..eb7c5ca 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -127,6 +127,9 @@
// A map from user id to UserState.
private final SparseArray<UserState> mUserStates = new SparseArray<>();
+ // A map from session id to session state saved in userstate
+ private final Map<String, SessionState> mSessionIdToSessionStateMap = new HashMap<>();
+
private final WatchLogHandler mWatchLogHandler;
public TvInputManagerService(Context context) {
@@ -632,7 +635,8 @@
UserState userState = getOrCreateUserStateLocked(userId);
SessionState sessionState = userState.sessionStateMap.get(sessionToken);
if (DEBUG) {
- Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.inputId + ")");
+ Slog.d(TAG, "createSessionInternalLocked(inputId="
+ + sessionState.inputId + ", sessionId=" + sessionState.sessionId + ")");
}
InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
@@ -643,9 +647,11 @@
// Create a session. When failed, send a null token immediately.
try {
if (sessionState.isRecordingSession) {
- service.createRecordingSession(callback, sessionState.inputId);
+ service.createRecordingSession(
+ callback, sessionState.inputId, sessionState.sessionId);
} else {
- service.createSession(channels[1], callback, sessionState.inputId);
+ service.createSession(
+ channels[1], callback, sessionState.inputId, sessionState.sessionId);
}
} catch (RemoteException e) {
Slog.e(TAG, "error in createSession", e);
@@ -715,6 +721,8 @@
}
}
+ mSessionIdToSessionStateMap.remove(sessionState.sessionId);
+
ServiceState serviceState = userState.serviceStateMap.get(sessionState.componentName);
if (serviceState != null) {
serviceState.sessionTokens.remove(sessionToken);
@@ -1156,9 +1164,11 @@
public void createSession(final ITvInputClient client, final String inputId,
boolean isRecordingSession, int seq, int userId) {
final int callingUid = Binder.getCallingUid();
- final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ final int callingPid = Binder.getCallingPid();
+ final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
userId, "createSession");
final long identity = Binder.clearCallingIdentity();
+ StringBuilder sessionId = new StringBuilder();
try {
synchronized (mLock) {
if (userId != mCurrentUserId && !isRecordingSession) {
@@ -1187,15 +1197,21 @@
return;
}
+ // Create a unique session id with pid, uid and resolved user id
+ sessionId.append(callingUid).append(callingPid).append(resolvedUserId);
+
// Create a new session token and a session state.
IBinder sessionToken = new Binder();
SessionState sessionState = new SessionState(sessionToken, info.getId(),
info.getComponent(), isRecordingSession, client, seq, callingUid,
- resolvedUserId);
+ callingPid, resolvedUserId, sessionId.toString());
// Add them to the global session state map of the current user.
userState.sessionStateMap.put(sessionToken, sessionState);
+ // Map the session id to the sessionStateMap in the user state
+ mSessionIdToSessionStateMap.put(sessionId.toString(), sessionState);
+
// Also, add them to the session state map of the current service.
serviceState.sessionTokens.add(sessionToken);
@@ -2003,6 +2019,43 @@
}
@Override
+ public int getClientPid(String sessionId) {
+ ensureTunerResourceAccessPermission();
+ final long identity = Binder.clearCallingIdentity();
+
+ int clientPid = TvInputManager.UNKNOWN_CLIENT_PID;
+ try {
+ synchronized (mLock) {
+ try {
+ clientPid = getClientPidLocked(sessionId);
+ } catch (ClientPidNotFoundException e) {
+ Slog.e(TAG, "error in getClientPid", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return clientPid;
+ }
+
+ private int getClientPidLocked(String sessionId)
+ throws IllegalStateException {
+ if (mSessionIdToSessionStateMap.get(sessionId) == null) {
+ throw new IllegalStateException("Client Pid not found with sessionId "
+ + sessionId);
+ }
+ return mSessionIdToSessionStateMap.get(sessionId).callingPid;
+ }
+
+ private void ensureTunerResourceAccessPermission() {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.TUNER_RESOURCE_ACCESS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires TUNER_RESOURCE_ACCESS permission");
+ }
+ }
+
+ @Override
@SuppressWarnings("resource")
protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -2094,9 +2147,11 @@
pw.increaseIndent();
pw.println("inputId: " + session.inputId);
+ pw.println("sessionId: " + session.sessionId);
pw.println("client: " + session.client);
pw.println("seq: " + session.seq);
pw.println("callingUid: " + session.callingUid);
+ pw.println("callingPid: " + session.callingPid);
pw.println("userId: " + session.userId);
pw.println("sessionToken: " + session.sessionToken);
pw.println("session: " + session.session);
@@ -2226,11 +2281,13 @@
private final class SessionState implements IBinder.DeathRecipient {
private final String inputId;
+ private final String sessionId;
private final ComponentName componentName;
private final boolean isRecordingSession;
private final ITvInputClient client;
private final int seq;
private final int callingUid;
+ private final int callingPid;
private final int userId;
private final IBinder sessionToken;
private ITvInputSession session;
@@ -2240,7 +2297,7 @@
private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
- int userId) {
+ int callingPid, int userId, String sessionId) {
this.sessionToken = sessionToken;
this.inputId = inputId;
this.componentName = componentName;
@@ -2248,7 +2305,9 @@
this.client = client;
this.seq = seq;
this.callingUid = callingUid;
+ this.callingPid = callingPid;
this.userId = userId;
+ this.sessionId = sessionId;
}
@Override
@@ -2962,4 +3021,10 @@
super(name);
}
}
+
+ private static class ClientPidNotFoundException extends IllegalArgumentException {
+ public ClientPidNotFoundException(String name) {
+ super(name);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/utils/quota/QuotaTracker.java b/services/core/java/com/android/server/utils/quota/QuotaTracker.java
index ef1f426..a8cf9f6 100644
--- a/services/core/java/com/android/server/utils/quota/QuotaTracker.java
+++ b/services/core/java/com/android/server/utils/quota/QuotaTracker.java
@@ -172,6 +172,16 @@
// Exposed API to users.
+ /** Remove all saved events from the tracker. */
+ public void clear() {
+ synchronized (mLock) {
+ mInQuotaAlarmListener.clearLocked();
+ mFreeQuota.clear();
+
+ dropEverythingLocked();
+ }
+ }
+
/**
* @return true if the UPTC is within quota, false otherwise.
* @throws IllegalStateException if given categorizer returns a Category that's not recognized.
@@ -245,10 +255,7 @@
mIsEnabled = enable;
if (!mIsEnabled) {
- mInQuotaAlarmListener.clearLocked();
- mFreeQuota.clear();
-
- dropEverythingLocked();
+ clear();
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 26d76a8d..320be2d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5934,7 +5934,11 @@
mAnimationBoundsLayer = createAnimationBoundsLayer(t);
// Crop to stack bounds.
- t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
+ if (!WindowManagerService.sHierarchicalAnimations) {
+ // For Hierarchical animation, we don't need to set window crop since the leash
+ // surface size has already same as the animating container.
+ t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
+ }
t.setLayer(mAnimationBoundsLayer, layer);
// Reparent leash to animation bounds layer.
@@ -5982,9 +5986,10 @@
return;
}
clearThumbnail();
+ final Transaction transaction = getAnimatingContainer().getPendingTransaction();
mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory,
- getPendingTransaction(), this, thumbnailHeader);
- mThumbnail.startAnimation(getPendingTransaction(), loadThumbnailAnimation(thumbnailHeader));
+ transaction, getAnimatingContainer(), thumbnailHeader);
+ mThumbnail.startAnimation(transaction, loadThumbnailAnimation(thumbnailHeader));
}
/**
@@ -6011,13 +6016,13 @@
if (thumbnail == null) {
return;
}
+ final Transaction transaction = getAnimatingContainer().getPendingTransaction();
mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory,
- getPendingTransaction(), this, thumbnail);
+ transaction, getAnimatingContainer(), thumbnail);
final Animation animation =
getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
win.getFrameLw());
- mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left,
- frame.top));
+ mThumbnail.startAnimation(transaction, animation, new Point(frame.left, frame.top));
}
private Animation loadThumbnailAnimation(GraphicBuffer thumbnailHeader) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index f3880fa..c38868a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -42,6 +42,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.logWithStack;
+import static com.android.server.wm.WindowManagerService.sHierarchicalAnimations;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
@@ -1855,7 +1856,7 @@
// TODO: Remove this and use #getBounds() instead once we set an app transition animation
// on TaskStack.
Rect getAnimationBounds(int appStackClipMode) {
- return getBounds();
+ return getDisplayedBounds();
}
/**
@@ -1929,7 +1930,11 @@
// Separate position and size for use in animators.
mTmpRect.set(getAnimationBounds(appStackClipMode));
- mTmpPoint.set(mTmpRect.left, mTmpRect.top);
+ if (sHierarchicalAnimations) {
+ getRelativeDisplayedPosition(mTmpPoint);
+ } else {
+ mTmpPoint.set(mTmpRect.left, mTmpRect.top);
+ }
mTmpRect.offsetTo(0, 0);
final RemoteAnimationController controller =
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 74fdba1..e3b593e9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4602,13 +4602,13 @@
if (newFocus != null) {
ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Gaining focus: %s", newFocus);
- newFocus.reportFocusChangedSerialized(true, mInTouchMode);
+ newFocus.reportFocusChangedSerialized(true);
notifyFocusChanged();
}
if (lastFocus != null) {
ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing focus: %s", lastFocus);
- lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
+ lastFocus.reportFocusChangedSerialized(false);
}
break;
}
@@ -4626,7 +4626,7 @@
for (int i = 0; i < N; i++) {
ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing delayed focus: %s",
losers.get(i));
- losers.get(i).reportFocusChangedSerialized(false, mInTouchMode);
+ losers.get(i).reportFocusChangedSerialized(false);
}
break;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ba40f62..c2eb0e4 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3339,11 +3339,7 @@
* Report a focus change. Must be called with no locks held, and consistently
* from the same serialized thread (such as dispatched from a handler).
*/
- void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) {
- try {
- mClient.windowFocusChanged(focused, inTouchMode);
- } catch (RemoteException e) {
- }
+ void reportFocusChangedSerialized(boolean focused) {
if (mFocusCallbacks != null) {
final int N = mFocusCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index acb6bea..00436bb 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -204,6 +204,7 @@
// Must match the value from GnssMeasurement.java
static const uint32_t ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
+static const uint32_t SVID_FLAGS_HAS_BASEBAND_CN0 = (1<<4);
sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
sp<IGnss_V1_0> gnssHal = nullptr;
@@ -634,6 +635,16 @@
template<class T>
Return<void> gnssSvStatusCbImpl(const T& svStatus);
+ template<class T>
+ uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) {
+ return 0;
+ }
+
+ template<class T>
+ double getBasebandCn0DbHz(const T& svStatus, size_t i) {
+ return 0.0;
+ }
+
uint32_t getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) {
return svStatus.numSvs;
}
@@ -658,8 +669,6 @@
const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
- // TODO(b/144850155): fill baseband CN0 after it's available in Java object.
- ALOGD("getGnssSvInfoOfIndex %d: baseband C/N0: %f", (int) i, svInfoList[i].basebandCN0DbHz);
return svInfoList[i].v2_0.v1_0;
}
@@ -721,6 +730,18 @@
return Void();
}
+template<>
+uint32_t GnssCallback::getHasBasebandCn0DbHzFlag(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>&
+ svStatus) {
+ return SVID_FLAGS_HAS_BASEBAND_CN0;
+}
+
+template<>
+double GnssCallback::getBasebandCn0DbHz(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList,
+ size_t i) {
+ return svInfoList[i].basebandCN0DbHz;
+}
+
template<class T>
Return<void> GnssCallback::gnssSvStatusCbImpl(const T& svStatus) {
JNIEnv* env = getJniEnv();
@@ -758,8 +779,8 @@
elev[i] = info.elevationDegrees;
azim[i] = info.azimuthDegrees;
carrierFreq[i] = info.carrierFrequencyHz;
- // TODO(b/144850155): fill svidWithFlags with hasBasebandCn0DbHz based on HAL versions
- basebandCn0s[i] = 0.0;
+ svidWithFlags[i] |= getHasBasebandCn0DbHzFlag(svStatus);
+ basebandCn0s[i] = getBasebandCn0DbHz(svStatus, i);
}
env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
@@ -1185,8 +1206,8 @@
const IGnssMeasurementCallback_V2_1::GnssMeasurement* measurement_V2_1,
JavaObject& object) {
translateSingleGnssMeasurement(&(measurement_V2_1->v2_0), object);
- // TODO(b/144850155): fill baseband CN0 after it's available in Java object
- ALOGD("baseband CN0DbHz = %f\n", measurement_V2_1->basebandCN0DbHz);
+
+ SET(BasebandCn0DbHz, measurement_V2_1->basebandCN0DbHz);
}
template<class T>
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 4696dd0..0275f3e 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -33,7 +33,6 @@
#include "bpf/BpfUtils.h"
#include "netdbpf/BpfNetworkStats.h"
-using android::bpf::Stats;
using android::bpf::bpfGetUidStats;
using android::bpf::bpfGetIfaceStats;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3592e5f..485899e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -23,7 +23,6 @@
import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
-import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED;
import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.CODE_HAS_DEVICE_OWNER;
@@ -129,6 +128,7 @@
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DeviceStateCache;
+import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.NetworkEvent;
import android.app.admin.PasswordMetrics;
import android.app.admin.PasswordPolicy;
@@ -268,6 +268,7 @@
import com.android.internal.widget.PasswordValidationError;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
+import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
@@ -1006,6 +1007,8 @@
private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL =
"cross-profile-calendar-packages-null";
private static final String TAG_CROSS_PROFILE_PACKAGES = "cross-profile-packages";
+ private static final String TAG_FACTORY_RESET_PROTECTION_POLICY =
+ "factory_reset_protection_policy";
DeviceAdminInfo info;
@@ -1016,6 +1019,9 @@
@NonNull
PasswordPolicy mPasswordPolicy = new PasswordPolicy();
+ @Nullable
+ FactoryResetProtectionPolicy mFactoryResetProtectionPolicy = null;
+
static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0;
long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK;
@@ -1351,6 +1357,11 @@
mCrossProfileCalendarPackages);
}
writePackageListToXml(out, TAG_CROSS_PROFILE_PACKAGES, mCrossProfilePackages);
+ if (mFactoryResetProtectionPolicy != null) {
+ out.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
+ mFactoryResetProtectionPolicy.writeToXml(out);
+ out.endTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
+ }
}
void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
@@ -1584,6 +1595,9 @@
mCrossProfileCalendarPackages = null;
} else if (TAG_CROSS_PROFILE_PACKAGES.equals(tag)) {
mCrossProfilePackages = readPackageList(parser, tag);
+ } else if (TAG_FACTORY_RESET_PROTECTION_POLICY.equals(tag)) {
+ mFactoryResetProtectionPolicy = FactoryResetProtectionPolicy.readFromXml(
+ parser);
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -2036,6 +2050,10 @@
return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));
}
+ PersistentDataBlockManagerInternal getPersistentDataBlockManagerInternal() {
+ return LocalServices.getService(PersistentDataBlockManagerInternal.class);
+ }
+
LockSettingsInternal getLockSettingsInternal() {
return LocalServices.getService(LockSettingsInternal.class);
}
@@ -4099,6 +4117,12 @@
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, userHandle)) {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false, userHandle);
}
+ // When a device owner is set, the system automatically restricts adding a managed profile.
+ // Remove this restriction when the device owner is cleared.
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, userHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
+ userHandle);
+ }
}
/**
@@ -6736,6 +6760,67 @@
}
@Override
+ public void setFactoryResetProtectionPolicy(ComponentName who,
+ @Nullable FactoryResetProtectionPolicy policy) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+
+ final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow();
+ final int userId = mInjector.userHandleGetCallingUserId();
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+ admin.mFactoryResetProtectionPolicy = policy;
+ saveSettingsLocked(userId);
+ }
+
+ mInjector.binderWithCleanCallingIdentity(() -> mContext.sendBroadcastAsUser(
+ new Intent(DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
+ UserHandle.getUserHandleForUid(frpManagementAgentUid)));
+
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_FACTORY_RESET_PROTECTION)
+ .setAdmin(who)
+ .write();
+ }
+
+ @Override
+ public FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(
+ @Nullable ComponentName who) {
+ if (!mHasFeature) {
+ return null;
+ }
+
+ final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow();
+ ActiveAdmin admin;
+ synchronized (getLockObject()) {
+ if (who == null) {
+ if ((frpManagementAgentUid != mInjector.binderGetCallingUid())) {
+ throw new SecurityException(
+ "Must be called by the FRP management agent on device");
+ }
+ admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.getUserId(frpManagementAgentUid));
+ } else {
+ admin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+ }
+ }
+ return admin != null ? admin.mFactoryResetProtectionPolicy : null;
+ }
+
+ private int getFrpManagementAgentUidOrThrow() {
+ PersistentDataBlockManagerInternal pdb = mInjector.getPersistentDataBlockManagerInternal();
+ if ((pdb == null) || (pdb.getAllowedUid() == -1)) {
+ throw new UnsupportedOperationException(
+ "The persistent data block service is not supported on this device");
+ }
+ return pdb.getAllowedUid();
+ }
+
+ @Override
public void getRemoveWarning(ComponentName comp, final RemoteCallback result, int userHandle) {
if (!mHasFeature) {
return;
@@ -7976,10 +8061,19 @@
updateDeviceOwnerLocked();
setDeviceOwnerSystemPropertyLocked();
- // TODO Send to system too?
- mInjector.binderWithCleanCallingIdentity(
- () -> sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED,
- userId));
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ // Restrict adding a managed profile when a device owner is set on the device.
+ // That is to prevent the co-existence of a managed profile and a device owner
+ // on the same device.
+ // Instead, the device may be provisioned with an organization-owned managed
+ // profile, such that the admin on that managed profile has extended management
+ // capabilities that can affect the entire device (but not access private data
+ // on the primary profile).
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
+ UserHandle.of(userId));
+ // TODO Send to system too?
+ sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
+ });
mDeviceAdminServiceController.startServiceForOwner(
admin.getPackageName(), userId, "set-device-owner");
@@ -8131,6 +8225,14 @@
return null;
}
+ ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(int userId) {
+ ActiveAdmin admin = getDeviceOwnerAdminLocked();
+ if (admin == null) {
+ admin = getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
+ }
+ return admin;
+ }
+
@Override
public void clearDeviceOwner(String packageName) {
Objects.requireNonNull(packageName, "packageName is null");
@@ -8234,6 +8336,17 @@
throw new IllegalArgumentException("Not active admin: " + who);
}
+ final int parentUserId = getProfileParentId(userHandle);
+ // When trying to set a profile owner on a new user, it may be that this user is
+ // a profile - but it may not be a managed profile if there's a restriction on the
+ // parent to add managed profiles (e.g. if the device has a device owner).
+ if (parentUserId != userHandle && mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+ UserHandle.of(parentUserId))) {
+ Slog.i(LOG_TAG, "Cannot set profile owner because of restriction.");
+ return false;
+ }
+
if (isAdb()) {
// Log profile owner provisioning was started using adb.
MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_PROFILE_OWNER);
@@ -12299,25 +12412,41 @@
final long ident = mInjector.binderClearCallingIdentity();
try {
final UserHandle callingUserHandle = UserHandle.of(callingUserId);
- final ComponentName ownerAdmin = getOwnerComponent(packageName, callingUserId);
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
- callingUserHandle)) {
- // An admin can initiate provisioning if it has set the restriction.
- if (ownerAdmin == null || isAdminAffectedByRestriction(ownerAdmin,
- UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserId)) {
- return CODE_ADD_MANAGED_PROFILE_DISALLOWED;
- }
+ final boolean hasDeviceOwner;
+ synchronized (getLockObject()) {
+ hasDeviceOwner = getDeviceOwnerAdminLocked() != null;
}
- boolean canRemoveProfile = true;
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
- callingUserHandle)) {
- // We can remove a profile if the admin itself has set the restriction.
- if (ownerAdmin == null || isAdminAffectedByRestriction(ownerAdmin,
- UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
- callingUserId)) {
- canRemoveProfile = false;
- }
+
+ final boolean addingProfileRestricted = mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle);
+
+ UserInfo parentUser = mUserManager.getProfileParent(callingUserId);
+ final boolean addingProfileRestrictedOnParent = (parentUser != null)
+ && mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+ UserHandle.of(parentUser.id));
+
+ Slog.i(LOG_TAG, String.format(
+ "When checking for managed profile provisioning: Has device owner? %b, adding"
+ + " profile restricted? %b, adding profile restricted on parent? %b",
+ hasDeviceOwner, addingProfileRestricted, addingProfileRestrictedOnParent));
+
+ // If there's a device owner, the restriction on adding a managed profile must be set
+ // somewhere.
+ if (hasDeviceOwner && !addingProfileRestricted && !addingProfileRestrictedOnParent) {
+ Slog.wtf(LOG_TAG, "Has a device owner but no restriction on adding a profile.");
}
+
+ // Do not allow adding a managed profile if there's a restriction, either on the current
+ // user or its parent user.
+ if (addingProfileRestricted || addingProfileRestrictedOnParent) {
+ return CODE_CANNOT_ADD_MANAGED_PROFILE;
+ }
+ // If there's a restriction on removing the managed profile then we have to take it
+ // into account when checking whether more profiles can be added.
+ boolean canRemoveProfile =
+ !mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+ callingUserHandle);
if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) {
return CODE_CANNOT_ADD_MANAGED_PROFILE;
}
diff --git a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
index a8a258f..9c5d4ad 100644
--- a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
+++ b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
@@ -3,6 +3,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import android.os.Looper;
import android.os.SystemClock;
@@ -52,8 +53,10 @@
@Test
public void handleInjectNtpTime_cachedAgeLow_injectTime() throws InterruptedException {
- doReturn(NtpTimeHelper.NTP_INTERVAL - 1).when(mMockNtpTrustedTime).getCacheAge();
- doReturn(MOCK_NTP_TIME).when(mMockNtpTrustedTime).getCachedNtpTime();
+ NtpTrustedTime.TimeResult result = mock(NtpTrustedTime.TimeResult.class);
+ doReturn(NtpTimeHelper.NTP_INTERVAL - 1).when(result).getAgeMillis();
+ doReturn(MOCK_NTP_TIME).when(result).getTimeMillis();
+ doReturn(result).when(mMockNtpTrustedTime).getCachedTimeResult();
mNtpTimeHelper.retrieveAndInjectNtpTime();
@@ -64,7 +67,9 @@
@Test
public void handleInjectNtpTime_injectTimeFailed_injectTimeDelayed()
throws InterruptedException {
- doReturn(NtpTimeHelper.NTP_INTERVAL + 1).when(mMockNtpTrustedTime).getCacheAge();
+ NtpTrustedTime.TimeResult result1 = mock(NtpTrustedTime.TimeResult.class);
+ doReturn(NtpTimeHelper.NTP_INTERVAL + 1).when(result1).getAgeMillis();
+ doReturn(result1).when(mMockNtpTrustedTime).getCachedTimeResult();
doReturn(false).when(mMockNtpTrustedTime).forceRefresh();
mNtpTimeHelper.retrieveAndInjectNtpTime();
@@ -72,8 +77,10 @@
assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isFalse();
doReturn(true).when(mMockNtpTrustedTime).forceRefresh();
- doReturn(1L).when(mMockNtpTrustedTime).getCacheAge();
- doReturn(MOCK_NTP_TIME).when(mMockNtpTrustedTime).getCachedNtpTime();
+ NtpTrustedTime.TimeResult result2 = mock(NtpTrustedTime.TimeResult.class);
+ doReturn(1L).when(result2).getAgeMillis();
+ doReturn(MOCK_NTP_TIME).when(result2).getTimeMillis();
+ doReturn(result2).when(mMockNtpTrustedTime).getCachedTimeResult();
SystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL);
waitForTasksToBePostedOnHandlerAndRunThem();
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 556f96a..6a5de84 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -345,8 +345,8 @@
}
/**
- * Lowers quotas to make testing feasible.
- * Careful while calling as this will replace any existing settings for the calling test.
+ * Lowers quotas to make testing feasible. Careful while calling as this will replace any
+ * existing settings for the calling test.
*/
private void setTestableQuotas() {
final StringBuilder constantsBuilder = new StringBuilder();
@@ -981,6 +981,25 @@
assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
}
+ @Test
+ public void alarmCountOnListenerBinderDied() {
+ final int numAlarms = 10;
+ final IAlarmListener[] listeners = new IAlarmListener[numAlarms];
+ for (int i = 0; i < numAlarms; i++) {
+ listeners[i] = new IAlarmListener.Stub() {
+ @Override
+ public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
+ }
+ };
+ setTestAlarmWithListener(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + i, listeners[i]);
+ }
+ assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ for (int i = 0; i < numAlarms; i++) {
+ mService.mListenerDeathRecipient.binderDied(listeners[i].asBinder());
+ assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ }
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 2fb2021..e609adc 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -16,7 +16,7 @@
package com.android.server;
-import static android.net.NetworkScoreManager.CACHE_FILTER_NONE;
+import static android.net.NetworkScoreManager.SCORE_FILTER_NONE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -306,7 +306,7 @@
bindToScorer(true /*callerIsScorer*/);
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
- mNetworkScoreCache, CACHE_FILTER_NONE);
+ mNetworkScoreCache, SCORE_FILTER_NONE);
mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
@@ -321,9 +321,9 @@
bindToScorer(true /*callerIsScorer*/);
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
- mNetworkScoreCache, CACHE_FILTER_NONE);
+ mNetworkScoreCache, SCORE_FILTER_NONE);
mNetworkScoreService.registerNetworkScoreCache(
- NetworkKey.TYPE_WIFI, mNetworkScoreCache2, CACHE_FILTER_NONE);
+ NetworkKey.TYPE_WIFI, mNetworkScoreCache2, SCORE_FILTER_NONE);
// updateScores should update both caches
mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK});
@@ -378,7 +378,7 @@
bindToScorer(true /*callerIsScorer*/);
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
- CACHE_FILTER_NONE);
+ SCORE_FILTER_NONE);
mNetworkScoreService.clearScores();
verify(mNetworkScoreCache).clearScores();
@@ -392,7 +392,7 @@
.thenReturn(PackageManager.PERMISSION_GRANTED);
mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache,
- CACHE_FILTER_NONE);
+ SCORE_FILTER_NONE);
mNetworkScoreService.clearScores();
verify(mNetworkScoreCache).clearScores();
@@ -472,7 +472,7 @@
try {
mNetworkScoreService.registerNetworkScoreCache(
- NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE);
+ NetworkKey.TYPE_WIFI, mNetworkScoreCache, SCORE_FILTER_NONE);
fail("SecurityException expected");
} catch (SecurityException e) {
// expected
@@ -615,7 +615,7 @@
new ArrayList<>(scoredNetworkList),
NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter);
- consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_NONE);
+ consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
verify(mNetworkScoreCache).updateScores(scoredNetworkList);
verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
@@ -656,7 +656,7 @@
Collections.emptyList(),
NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter);
- consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_NONE);
+ consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE);
verifyZeroInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
}
@@ -673,7 +673,7 @@
List<ScoredNetwork> filteredList = new ArrayList<>(scoredNetworkList);
filteredList.remove(SCORED_NETWORK);
when(mCurrentNetworkFilter.apply(scoredNetworkList)).thenReturn(filteredList);
- consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK);
+ consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK);
verify(mNetworkScoreCache).updateScores(filteredList);
verifyZeroInteractions(mScanResultsFilter);
@@ -691,7 +691,7 @@
List<ScoredNetwork> filteredList = new ArrayList<>(scoredNetworkList);
filteredList.remove(SCORED_NETWORK);
when(mScanResultsFilter.apply(scoredNetworkList)).thenReturn(filteredList);
- consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_SCAN_RESULTS);
+ consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_SCAN_RESULTS);
verify(mNetworkScoreCache).updateScores(filteredList);
verifyZeroInteractions(mCurrentNetworkFilter);
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 591c3a3..c223f13 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -804,6 +804,11 @@
}
@Override
+ public boolean switchToInputMethod(String imeId) {
+ return false;
+ }
+
+ @Override
public boolean isAccessibilityButtonAvailable() throws RemoteException {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 0f11566..39a3aae 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -212,7 +212,8 @@
String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE};
when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list);
- Account[] accounts = mAms.getAccounts(null, mContext.getOpPackageName());
+ Account[] accounts = mAms.getAccountsAsUser(null,
+ UserHandle.getCallingUserId(), mContext.getOpPackageName());
Arrays.sort(accounts, new AccountSorter());
assertEquals(6, accounts.length);
assertEquals(a11, accounts[0]);
@@ -222,8 +223,8 @@
assertEquals(a22, accounts[4]);
assertEquals(a32, accounts[5]);
- accounts = mAms.getAccounts(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
- mContext.getOpPackageName());
+ accounts = mAms.getAccountsAsUser(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+ UserHandle.getCallingUserId(), mContext.getOpPackageName());
Arrays.sort(accounts, new AccountSorter());
assertEquals(3, accounts.length);
assertEquals(a11, accounts[0]);
@@ -232,8 +233,8 @@
mAms.removeAccountInternal(a21);
- accounts = mAms.getAccounts(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
- mContext.getOpPackageName());
+ accounts = mAms.getAccountsAsUser(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+ UserHandle.getCallingUserId(), mContext.getOpPackageName());
Arrays.sort(accounts, new AccountSorter());
assertEquals(2, accounts.length);
assertEquals(a11, accounts[0]);
@@ -373,7 +374,8 @@
unlockSystemUser();
String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE};
when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list);
- Account[] accounts = mAms.getAccounts(null, mContext.getOpPackageName());
+ Account[] accounts = mAms.getAccountsAsUser(null, UserHandle.getCallingUserId(),
+ mContext.getOpPackageName());
assertEquals("1 account should be migrated", 1, accounts.length);
assertEquals(PreNTestDatabaseHelper.ACCOUNT_NAME, accounts[0].name);
assertEquals(PreNTestDatabaseHelper.ACCOUNT_PASSWORD, mAms.getPassword(accounts[0]));
@@ -2980,7 +2982,8 @@
Log.d(TAG, logPrefix + " getAccounts started");
long ti = System.currentTimeMillis();
try {
- Account[] accounts = mAms.getAccounts(null, mContext.getOpPackageName());
+ Account[] accounts = mAms.getAccountsAsUser(null,
+ UserHandle.getCallingUserId(), mContext.getOpPackageName());
if (accounts == null || accounts.length != 1
|| !AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1.equals(
accounts[0].type)) {
@@ -3051,7 +3054,8 @@
Log.d(TAG, logPrefix + " getAccounts started");
long ti = System.currentTimeMillis();
try {
- Account[] accounts = mAms.getAccounts(null, mContext.getOpPackageName());
+ Account[] accounts = mAms.getAccountsAsUser(null,
+ UserHandle.getCallingUserId(), mContext.getOpPackageName());
if (accounts == null || accounts.length != 1
|| !AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1.equals(
accounts[0].type)) {
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 79cc3db..f122014 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -31,6 +31,7 @@
import static com.google.android.collect.Lists.newArrayList;
import static com.google.android.collect.Sets.newHashSet;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
@@ -54,6 +55,7 @@
import static org.mockito.Mockito.when;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.app.IUserSwitchObserver;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -74,10 +76,10 @@
import android.platform.test.annotations.Presubmit;
import android.util.Log;
-
import androidx.test.filters.SmallTest;
import com.android.server.FgThread;
+import com.android.server.am.UserState.KeyEvictedCallback;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.WindowManagerService;
@@ -107,6 +109,7 @@
private static final int TEST_USER_ID = 100;
private static final int TEST_USER_ID1 = 101;
private static final int TEST_USER_ID2 = 102;
+ private static final int TEST_USER_ID3 = 103;
private static final int NONEXIST_USER_ID = 2;
private static final int TEST_PRE_CREATED_USER_ID = 103;
@@ -120,6 +123,8 @@
private TestInjector mInjector;
private final HashMap<Integer, UserState> mUserStates = new HashMap<>();
+ private final KeyEvictedCallback mKeyEvictedCallback = (userId) -> { /* ignore */ };
+
private static final List<String> START_FOREGROUND_USER_ACTIONS = newArrayList(
Intent.ACTION_USER_STARTED,
Intent.ACTION_USER_SWITCHED,
@@ -153,6 +158,7 @@
doNothing().when(mInjector).activityManagerOnUserStopped(anyInt());
doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
doNothing().when(mInjector).stackSupervisorRemoveUser(anyInt());
+ // All UserController params are set to default.
mUserController = new UserController(mInjector);
setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS);
setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true);
@@ -187,7 +193,9 @@
@Test
public void testStartUserUIDisabled() {
- mUserController.mUserSwitchUiEnabled = false;
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
mUserController.startUser(TEST_USER_ID, true /* foreground */);
verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
@@ -245,7 +253,9 @@
@Test
public void testFailedStartUserInForeground() {
- mUserController.mUserSwitchUiEnabled = false;
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
mUserController.startUserInForeground(NONEXIST_USER_ID);
verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
verify(mInjector.getWindowManager()).setSwitchingUser(false);
@@ -326,7 +336,9 @@
@Test
public void testContinueUserSwitchUIDisabled() throws RemoteException {
- mUserController.mUserSwitchUiEnabled = false;
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
// Start user -- this will update state of mUserController
mUserController.startUser(TEST_USER_ID, true);
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -389,11 +401,13 @@
* Test stopping of user from max running users limit.
*/
@Test
- public void testUserStoppingForMultipleUsersNormalMode()
+ public void testUserLockingFromUserSwitchingForMultipleUsersNonDelayedLocking()
throws InterruptedException, RemoteException {
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
setUpUser(TEST_USER_ID1, 0);
setUpUser(TEST_USER_ID2, 0);
- mUserController.mMaxRunningUsers = 3;
int numerOfUserSwitches = 1;
addForegroundUserAndContinueUserSwitch(TEST_USER_ID, UserHandle.USER_SYSTEM,
numerOfUserSwitches, false);
@@ -430,12 +444,13 @@
* all middle steps which takes too much work to mock.
*/
@Test
- public void testUserStoppingForMultipleUsersDelayedLockingMode()
+ public void testUserLockingFromUserSwitchingForMultipleUsersDelayedLockingMode()
throws InterruptedException, RemoteException {
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ true);
+
setUpUser(TEST_USER_ID1, 0);
setUpUser(TEST_USER_ID2, 0);
- mUserController.mMaxRunningUsers = 3;
- mUserController.mDelayUserDataLocking = true;
int numerOfUserSwitches = 1;
addForegroundUserAndContinueUserSwitch(TEST_USER_ID, UserHandle.USER_SYSTEM,
numerOfUserSwitches, false);
@@ -456,7 +471,7 @@
// Skip all other steps and test unlock delaying only
UserState uss = mUserStates.get(TEST_USER_ID);
uss.setState(UserState.STATE_SHUTDOWN); // necessary state change from skipped part
- mUserController.finishUserStopped(uss);
+ mUserController.finishUserStopped(uss, /* allowDelayedLocking= */ true);
// Cannot mock FgThread handler, so confirm that there is no posted message left before
// checking.
waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
@@ -473,12 +488,87 @@
mUserController.getRunningUsersLU());
UserState ussUser1 = mUserStates.get(TEST_USER_ID1);
ussUser1.setState(UserState.STATE_SHUTDOWN);
- mUserController.finishUserStopped(ussUser1);
+ mUserController.finishUserStopped(ussUser1, /* allowDelayedLocking= */ true);
waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
verify(mInjector.mStorageManagerMock, times(1))
.lockUserKey(TEST_USER_ID);
}
+ /**
+ * Test locking user with mDelayUserDataLocking false.
+ */
+ @Test
+ public void testUserLockingWithStopUserForNonDelayedLockingMode() throws Exception {
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
+ setUpAndStartUserInBackground(TEST_USER_ID);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID, /* delayedLocking= */ true,
+ /* keyEvictedCallback= */ null, /* expectLocking= */ true);
+
+ setUpAndStartUserInBackground(TEST_USER_ID1);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true,
+ /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true);
+
+ setUpAndStartUserInBackground(TEST_USER_ID2);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* delayedLocking= */ false,
+ /* keyEvictedCallback= */ null, /* expectLocking= */ true);
+
+ setUpAndStartUserInBackground(TEST_USER_ID3);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID3, /* delayedLocking= */ false,
+ /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true);
+ }
+
+ /**
+ * Test conditional delayed locking with mDelayUserDataLocking true.
+ */
+ @Test
+ public void testUserLockingForDelayedLockingMode() throws Exception {
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ true);
+
+ // delayedLocking set and no KeyEvictedCallback, so it should not lock.
+ setUpAndStartUserInBackground(TEST_USER_ID);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID, /* delayedLocking= */ true,
+ /* keyEvictedCallback= */ null, /* expectLocking= */ false);
+
+ setUpAndStartUserInBackground(TEST_USER_ID1);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true,
+ /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true);
+
+ setUpAndStartUserInBackground(TEST_USER_ID2);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* delayedLocking= */ false,
+ /* keyEvictedCallback= */ null, /* expectLocking= */ true);
+
+ setUpAndStartUserInBackground(TEST_USER_ID3);
+ assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID3, /* delayedLocking= */ false,
+ /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true);
+ }
+
+ private void setUpAndStartUserInBackground(int userId) throws Exception {
+ setUpUser(userId, 0);
+ mUserController.startUser(userId, /* foreground= */ false);
+ verify(mInjector.mStorageManagerMock, times(1))
+ .unlockUserKey(TEST_USER_ID, 0, null, null);
+ mUserStates.put(userId, mUserController.getStartedUserState(userId));
+ }
+
+ private void assertUserLockedOrUnlockedAfterStopping(int userId, boolean delayedLocking,
+ KeyEvictedCallback keyEvictedCallback, boolean expectLocking) throws Exception {
+ int r = mUserController.stopUser(userId, /* force= */ true, /* delayedLocking= */
+ delayedLocking, null, keyEvictedCallback);
+ assertThat(r).isEqualTo(ActivityManager.USER_OP_SUCCESS);
+ // fake all interim steps
+ UserState ussUser = mUserStates.get(userId);
+ ussUser.setState(UserState.STATE_SHUTDOWN);
+ // Passing delayedLocking invalidates incorrect internal data passing but currently there is
+ // no easy way to get that information passed through lambda.
+ mUserController.finishUserStopped(ussUser, delayedLocking);
+ waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
+ verify(mInjector.mStorageManagerMock, times(expectLocking ? 1 : 0))
+ .lockUserKey(userId);
+ }
+
private void addForegroundUserAndContinueUserSwitch(int newUserId, int expectedOldUserId,
int expectedNumberOfCalls, boolean expectOldUserStopping)
throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index d44476e..3de006c 100644
--- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -57,7 +57,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
@@ -523,7 +522,6 @@
* Test that {@link BackupManagerService#dump()} dumps system user information before non-system
* user information.
*/
-
@Test
public void testDump_systemUserFirst() {
String[] args = new String[0];
@@ -539,16 +537,31 @@
}
@Test
- @Ignore("b/147012496")
- public void testGetUserForAncestralSerialNumber() {
+ public void testGetUserForAncestralSerialNumber_forSystemUser() {
BackupManagerServiceTestable.sBackupDisabled = false;
BackupManagerService backupManagerService =
new BackupManagerServiceTestable(mContextMock, mUserServices);
+ when(mUserManagerMock.getProfileIds(UserHandle.getCallingUserId(), false))
+ .thenReturn(new int[] {UserHandle.USER_SYSTEM, NON_USER_SYSTEM});
when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L);
- assertThat(user).isEqualTo(UserHandle.of(1));
+ assertThat(user).isEqualTo(UserHandle.of(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testGetUserForAncestralSerialNumber_forNonSystemUser() {
+ BackupManagerServiceTestable.sBackupDisabled = false;
+ BackupManagerService backupManagerService =
+ new BackupManagerServiceTestable(mContextMock, mUserServices);
+ when(mUserManagerMock.getProfileIds(UserHandle.getCallingUserId(), false))
+ .thenReturn(new int[] {UserHandle.USER_SYSTEM, NON_USER_SYSTEM});
+ when(mNonSystemUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
+
+ UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L);
+
+ assertThat(user).isEqualTo(UserHandle.of(NON_USER_SYSTEM));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index f96d996..bec265e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -411,7 +411,8 @@
// HAT sent to keystore
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
// Send onAuthenticated to client
- verify(mReceiver1).onAuthenticationSucceeded();
+ verify(mReceiver1).onAuthenticationSucceeded(
+ BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
// Current session becomes null
assertNull(mBiometricService.mCurrentAuthSession);
}
@@ -461,7 +462,8 @@
BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
waitForIdle();
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
- verify(mReceiver1).onAuthenticationSucceeded();
+ verify(mReceiver1).onAuthenticationSucceeded(
+ BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index abe39f0..312ff2c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -223,4 +223,22 @@
Utils.biometricConstantsToBiometricManager(testCases[i][0]));
}
}
+
+ @Test
+ public void testGetAuthenticationTypeForResult_getsCorrectType() {
+ assertEquals(Utils.getAuthenticationTypeForResult(
+ BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED),
+ BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL);
+ assertEquals(Utils.getAuthenticationTypeForResult(
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED),
+ BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
+ assertEquals(Utils.getAuthenticationTypeForResult(
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED),
+ BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetAuthResultType_throwsForInvalidReason() {
+ Utils.getAuthenticationTypeForResult(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index ac555fd..3a8258b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -51,6 +51,7 @@
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import java.io.File;
@@ -223,6 +224,11 @@
}
@Override
+ PersistentDataBlockManagerInternal getPersistentDataBlockManagerInternal() {
+ return services.persistentDataBlockManagerInternal;
+ }
+
+ @Override
Looper getMyLooper() {
return Looper.getMainLooper();
}
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 20cb449..45729e5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -31,7 +31,10 @@
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
import static com.android.server.testutils.TestUtils.assertExpectException;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
@@ -62,6 +65,7 @@
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.PasswordMetrics;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
@@ -272,6 +276,29 @@
}).when(getServices().userManager).getApplicationRestrictions(
anyString(), any(UserHandle.class));
+ // Emulate UserManager.setUserRestriction/getUserRestrictions
+ final Map<UserHandle, Bundle> userRestrictions = new HashMap<>();
+
+ doAnswer((Answer<Void>) invocation -> {
+ String key = (String) invocation.getArguments()[0];
+ boolean value = (Boolean) invocation.getArguments()[1];
+ UserHandle user = (UserHandle) invocation.getArguments()[2];
+ Bundle userBundle = userRestrictions.getOrDefault(user, new Bundle());
+ userBundle.putBoolean(key, value);
+
+ userRestrictions.put(user, userBundle);
+ return null;
+ }).when(getServices().userManager).setUserRestriction(
+ anyString(), anyBoolean(), any(UserHandle.class));
+
+ doAnswer((Answer<Boolean>) invocation -> {
+ String key = (String) invocation.getArguments()[0];
+ UserHandle user = (UserHandle) invocation.getArguments()[1];
+ Bundle userBundle = userRestrictions.getOrDefault(user, new Bundle());
+ return userBundle.getBoolean(key);
+ }).when(getServices().userManager).hasUserRestriction(
+ anyString(), any(UserHandle.class));
+
// Add the first secondary user.
getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0,
UserManager.USER_TYPE_FULL_SECONDARY);
@@ -819,10 +846,8 @@
final int MANAGED_PROFILE_ADMIN_UID =
UserHandle.getUid(MANAGED_PROFILE_USER_ID, DpmMockContext.SYSTEM_UID);
- // Setup device owner.
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
mContext.packageName = admin1.getPackageName();
- setupDeviceOwner();
// Add a managed profile belonging to the system user.
addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
@@ -830,18 +855,13 @@
// Change the parent user's password.
dpm.reportPasswordChanged(UserHandle.USER_SYSTEM);
- // Both the device owner and the managed profile owner should receive this broadcast.
+ // The managed profile owner should receive this broadcast.
final Intent intent = new Intent(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED);
intent.setComponent(admin1);
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(UserHandle.USER_SYSTEM));
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntent(intent),
- MockUtils.checkUserHandle(UserHandle.USER_SYSTEM),
- eq(null),
- any(Bundle.class));
- verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
- MockUtils.checkIntent(intent),
MockUtils.checkUserHandle(MANAGED_PROFILE_USER_ID),
eq(null),
any(Bundle.class));
@@ -861,12 +881,11 @@
final int MANAGED_PROFILE_ADMIN_UID =
UserHandle.getUid(MANAGED_PROFILE_USER_ID, DpmMockContext.SYSTEM_UID);
- // Setup device owner.
+ // Configure system as having separate profile challenge.
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
mContext.packageName = admin1.getPackageName();
doReturn(true).when(getServices().lockPatternUtils)
.isSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID);
- setupDeviceOwner();
// Add a managed profile belonging to the system user.
addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
@@ -951,6 +970,10 @@
verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
eq(admin1.getPackageName()));
+ verify(getServices().userManager, times(1)).setUserRestriction(
+ eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
+ eq(true), eq(UserHandle.SYSTEM));
+
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
@@ -2002,12 +2025,11 @@
assertNoDeviceOwnerRestrictions();
- // Initialize DPMS again and check that the user restriction wasn't enabled again.
reset(getServices().userManagerInternal);
- initializeDpms();
- assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName()));
- assertNotNull(dpms.getDeviceOwnerAdminLocked());
+ // Ensure the DISALLOW_REMOVE_MANAGED_PROFILES restriction doesn't show up as a
+ // restriction to the device owner.
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
assertNoDeviceOwnerRestrictions();
}
@@ -2022,6 +2044,116 @@
);
}
+ public void testSetFactoryResetProtectionPolicyWithDO() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+
+ when(getServices().persistentDataBlockManagerInternal.getAllowedUid()).thenReturn(
+ DpmMockContext.CALLER_UID);
+
+ FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+ .setFactoryResetProtectionAccounts(new ArrayList<>())
+ .setFactoryResetProtectionDisabled(true)
+ .build();
+ dpm.setFactoryResetProtectionPolicy(admin1, policy);
+
+ FactoryResetProtectionPolicy result = dpm.getFactoryResetProtectionPolicy(admin1);
+ assertThat(result).isEqualTo(policy);
+ assertPoliciesAreEqual(policy, result);
+
+ verify(mContext.spiedContext).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ public void testSetFactoryResetProtectionPolicyFailWithPO() throws Exception {
+ setupProfileOwner();
+
+ FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+ .setFactoryResetProtectionDisabled(true)
+ .build();
+
+ assertExpectException(SecurityException.class, null,
+ () -> dpm.setFactoryResetProtectionPolicy(admin1, policy));
+ }
+
+ public void testSetFactoryResetProtectionPolicyWithPOOfOrganizationOwnedDevice()
+ throws Exception {
+ setupProfileOwner();
+ configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+ when(getServices().persistentDataBlockManagerInternal.getAllowedUid()).thenReturn(
+ DpmMockContext.CALLER_UID);
+
+ List<String> accounts = new ArrayList<>();
+ accounts.add("Account 1");
+ accounts.add("Account 2");
+
+ FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+ .setFactoryResetProtectionAccounts(accounts)
+ .build();
+
+ dpm.setFactoryResetProtectionPolicy(admin1, policy);
+
+ FactoryResetProtectionPolicy result = dpm.getFactoryResetProtectionPolicy(admin1);
+ assertThat(result).isEqualTo(policy);
+ assertPoliciesAreEqual(policy, result);
+
+ verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+ verify(mContext.spiedContext).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+ verify(mContext.spiedContext).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ public void testGetFactoryResetProtectionPolicyWithFrpManagementAgent()
+ throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ when(getServices().persistentDataBlockManagerInternal.getAllowedUid()).thenReturn(
+ DpmMockContext.CALLER_UID);
+
+ FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+ .setFactoryResetProtectionAccounts(new ArrayList<>())
+ .setFactoryResetProtectionDisabled(true)
+ .build();
+
+ dpm.setFactoryResetProtectionPolicy(admin1, policy);
+
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ dpm.setActiveAdmin(admin1, /*replace=*/ false);
+ FactoryResetProtectionPolicy result = dpm.getFactoryResetProtectionPolicy(null);
+ assertThat(result).isEqualTo(policy);
+ assertPoliciesAreEqual(policy, result);
+
+ verify(mContext.spiedContext).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
+ MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+ }
+
+ private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy,
+ FactoryResetProtectionPolicy actualPolicy) {
+ assertThat(actualPolicy.isFactoryResetProtectionDisabled()).isEqualTo(
+ expectedPolicy.isFactoryResetProtectionDisabled());
+ assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(),
+ actualPolicy.getFactoryResetProtectionAccounts());
+ }
+
+ private void assertAccountsAreEqual(List<String> expectedAccounts,
+ List<String> actualAccounts) {
+ assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts);
+ }
+
public void testGetMacAddress() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
@@ -2993,7 +3125,6 @@
setup_nonSplitUser_withDo_primaryUser();
final int MANAGED_PROFILE_USER_ID = 18;
final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 1308);
- addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM,
false /* we can't remove a managed profile */)).thenReturn(false);
when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM,
@@ -3045,41 +3176,16 @@
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, false);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_FINANCED_DEVICE, false);
- // COMP mode is allowed.
+ // COMP mode NOT is allowed.
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_OK);
- assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
- // And other DPCs can also provision a managed profile (DO + BYOD case).
+ // And other DPCs can NOT provision a managed profile.
assertCheckProvisioningPreCondition(
DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
DpmMockContext.ANOTHER_PACKAGE_NAME,
- DevicePolicyManager.CODE_OK);
- assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true,
- DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
- }
-
- public void testProvisioning_nonSplitUser_withDo_primaryUser_restrictedByDo() throws Exception {
- setup_nonSplitUser_withDo_primaryUser();
- mContext.packageName = admin1.getPackageName();
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
- // The DO should be allowed to initiate provisioning if it set the restriction itself, but
- // other packages should be forbidden.
- when(getServices().userManager.hasUserRestriction(
- eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
- eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
- .thenReturn(true);
- when(getServices().userManager.getUserRestrictionSource(
- eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
- eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
- .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
- assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_OK);
- assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
- assertCheckProvisioningPreCondition(
- DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DpmMockContext.ANOTHER_PACKAGE_NAME,
- DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED);
+ DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
}
@@ -3100,31 +3206,46 @@
eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
.thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED);
+ DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
assertCheckProvisioningPreCondition(
DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
DpmMockContext.ANOTHER_PACKAGE_NAME,
- DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED);
+ DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
}
- public void testCheckProvisioningPreCondition_nonSplitUser_comp() throws Exception {
+ public void testCheckCannotSetProfileOwnerWithDeviceOwner() throws Exception {
+ setup_nonSplitUser_withDo_primaryUser();
+ final int managedProfileUserId = 18;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 1308);
+
+ final int userId = UserHandle.getUserId(managedProfileAdminUid);
+ getServices().addUser(userId, 0, UserManager.USER_TYPE_PROFILE_MANAGED,
+ UserHandle.USER_SYSTEM);
+ mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS);
+ setUpPackageManagerForFakeAdmin(admin1, managedProfileAdminUid, admin1);
+ dpm.setActiveAdmin(admin1, false, userId);
+ assertFalse(dpm.setProfileOwner(admin1, null, userId));
+ mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
+ }
+
+ public void testCheckProvisioningPreCondition_nonSplitUser_attemptingComp() throws Exception {
setup_nonSplitUser_withDo_primaryUser_ManagedProfile();
mContext.packageName = admin1.getPackageName();
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
// We can delete the managed profile to create a new one, so provisioning is allowed.
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_OK);
- assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
assertCheckProvisioningPreCondition(
DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
DpmMockContext.ANOTHER_PACKAGE_NAME,
- DevicePolicyManager.CODE_OK);
- assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true,
+ DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
}
@@ -3152,8 +3273,8 @@
// But the device owner can still do it because it has set the restriction itself.
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_OK);
- assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
}
private void setup_splitUser_firstBoot_systemUser() throws Exception {
@@ -3362,6 +3483,8 @@
when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
.thenReturn(true);
when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(getServices().userManager.getProfileParent(DpmMockContext.CALLER_USER_HANDLE))
+ .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
when(getServices().userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
true)).thenReturn(true);
setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE);
@@ -3374,7 +3497,7 @@
setup_provisionManagedProfileWithDeviceOwner_primaryUser();
setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
mContext.packageName = admin1.getPackageName();
- assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
}
public void testCheckProvisioningPreCondition_provisionManagedProfileWithDeviceOwner_primaryUser()
@@ -3382,9 +3505,9 @@
setup_provisionManagedProfileWithDeviceOwner_primaryUser();
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
- // COMP mode is allowed.
+ // COMP mode is NOT allowed.
assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
- DevicePolicyManager.CODE_OK);
+ DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
}
private void setup_provisionManagedProfileCantRemoveUser_primaryUser() throws Exception {
@@ -3901,11 +4024,6 @@
List<UserHandle> targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
MoreAsserts.assertEmpty(targetUsers);
- // Setup a managed profile managed by the same admin.
- final int MANAGED_PROFILE_USER_ID = 15;
- final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 20456);
- addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
-
// Add a secondary user, it should never talk with.
final int ANOTHER_USER_ID = 36;
getServices().addUser(ANOTHER_USER_ID, 0, UserManager.USER_TYPE_FULL_SECONDARY);
@@ -3915,30 +4033,11 @@
targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
MoreAsserts.assertEmpty(targetUsers);
- mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
- targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
- MoreAsserts.assertEmpty(targetUsers);
-
// Setting affiliation ids
final Set<String> userAffiliationIds = Collections.singleton("some.affiliation-id");
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
dpm.setAffiliationIds(admin1, userAffiliationIds);
- mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
- dpm.setAffiliationIds(admin1, userAffiliationIds);
-
- // Calling from device owner admin, the result list should just contain the managed
- // profile user id.
- mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
- targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
- MoreAsserts.assertContentsInAnyOrder(targetUsers, UserHandle.of(MANAGED_PROFILE_USER_ID));
-
- // Calling from managed profile admin, the result list should just contain the system
- // user id.
- mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
- targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
- MoreAsserts.assertContentsInAnyOrder(targetUsers, UserHandle.SYSTEM);
-
// Changing affiliation ids in one
dpm.setAffiliationIds(admin1, Collections.singleton("some-different-affiliation-id"));
@@ -3952,38 +4051,6 @@
MoreAsserts.assertEmpty(targetUsers);
}
- public void testGetBindDeviceAdminTargetUsers_differentPackage() throws Exception {
- // Setup a device owner.
- mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
- setupDeviceOwner();
-
- // Set up a managed profile managed by different package.
- final int MANAGED_PROFILE_USER_ID = 15;
- final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 20456);
- final ComponentName adminDifferentPackage =
- new ComponentName("another.package", "whatever.class");
- addManagedProfile(adminDifferentPackage, MANAGED_PROFILE_ADMIN_UID, admin2);
-
- // Setting affiliation ids
- final Set<String> userAffiliationIds = Collections.singleton("some-affiliation-id");
- dpm.setAffiliationIds(admin1, userAffiliationIds);
-
- mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
- dpm.setAffiliationIds(adminDifferentPackage, userAffiliationIds);
-
- // Calling from device owner admin, we should get zero bind device admin target users as
- // their packages are different.
- mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
- List<UserHandle> targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
- MoreAsserts.assertEmpty(targetUsers);
-
- // Calling from managed profile admin, we should still get zero target users for the same
- // reason.
- mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
- targetUsers = dpm.getBindDeviceAdminTargetUsers(adminDifferentPackage);
- MoreAsserts.assertEmpty(targetUsers);
- }
-
private void verifyLockTaskState(int userId) throws Exception {
verifyLockTaskState(userId, new String[0],
DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS);
@@ -4020,79 +4087,6 @@
() -> dpm.setLockTaskFeatures(who, flags));
}
- public void testLockTaskPolicyAllowedForAffiliatedUsers() throws Exception {
- // Setup a device owner.
- mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
- setupDeviceOwner();
- // Lock task policy is updated when loading user data.
- verifyLockTaskState(UserHandle.USER_SYSTEM);
-
- // Set up a managed profile managed by different package (package name shouldn't matter)
- final int MANAGED_PROFILE_USER_ID = 15;
- final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 20456);
- final ComponentName adminDifferentPackage =
- new ComponentName("another.package", "whatever.class");
- addManagedProfile(adminDifferentPackage, MANAGED_PROFILE_ADMIN_UID, admin2);
- verifyLockTaskState(MANAGED_PROFILE_USER_ID);
-
- // Setup a PO on the secondary user
- mContext.binder.callingUid = DpmMockContext.CALLER_UID;
- setAsProfileOwner(admin3);
- verifyLockTaskState(DpmMockContext.CALLER_USER_HANDLE);
-
- // The DO can still set lock task packages
- final String[] doPackages = {"doPackage1", "doPackage2"};
- final int flags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
- | DevicePolicyManager.LOCK_TASK_FEATURE_HOME
- | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
- verifyCanSetLockTask(DpmMockContext.CALLER_SYSTEM_USER_UID, UserHandle.USER_SYSTEM, admin1, doPackages, flags);
-
- final String[] secondaryPoPackages = {"secondaryPoPackage1", "secondaryPoPackage2"};
- final int secondaryPoFlags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
- | DevicePolicyManager.LOCK_TASK_FEATURE_HOME
- | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
- verifyCanNotSetLockTask(DpmMockContext.CALLER_UID, admin3, secondaryPoPackages, secondaryPoFlags);
-
- // Managed profile is unaffiliated - shouldn't be able to setLockTaskPackages.
- mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
- final String[] poPackages = {"poPackage1", "poPackage2"};
- final int poFlags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
- | DevicePolicyManager.LOCK_TASK_FEATURE_HOME
- | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
- verifyCanNotSetLockTask(MANAGED_PROFILE_ADMIN_UID, adminDifferentPackage, poPackages, poFlags);
-
- // Setting same affiliation ids
- final Set<String> userAffiliationIds = Collections.singleton("some-affiliation-id");
- mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
- dpm.setAffiliationIds(admin1, userAffiliationIds);
-
- mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
- dpm.setAffiliationIds(adminDifferentPackage, userAffiliationIds);
-
- // Now the managed profile can set lock task packages.
- dpm.setLockTaskPackages(adminDifferentPackage, poPackages);
- MoreAsserts.assertEquals(poPackages, dpm.getLockTaskPackages(adminDifferentPackage));
- assertTrue(dpm.isLockTaskPermitted("poPackage1"));
- assertFalse(dpm.isLockTaskPermitted("doPackage2"));
- // And it can set lock task features.
- dpm.setLockTaskFeatures(adminDifferentPackage, poFlags);
- verifyLockTaskState(MANAGED_PROFILE_USER_ID, poPackages, poFlags);
-
- // Unaffiliate the profile, lock task mode no longer available on the profile.
- dpm.setAffiliationIds(adminDifferentPackage, Collections.emptySet());
- assertFalse(dpm.isLockTaskPermitted("poPackage1"));
- // Lock task packages cleared when loading user data and when the user becomes unaffiliated.
- verify(getServices().iactivityManager, times(2)).updateLockTaskPackages(
- MANAGED_PROFILE_USER_ID, new String[0]);
- verify(getServices().iactivityTaskManager, times(2)).updateLockTaskFeatures(
- MANAGED_PROFILE_USER_ID, DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
-
- // Verify that lock task packages were not cleared for the DO
- mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
- assertTrue(dpm.isLockTaskPermitted("doPackage1"));
-
- }
-
public void testLockTaskPolicyForProfileOwner() throws Exception {
// Setup a PO
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
new file mode 100644
index 0000000..bc853c6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.devicepolicy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.FactoryResetProtectionPolicy;
+import android.os.Parcel;
+import android.util.Xml;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link android.app.admin.FactoryResetProtectionPolicy}.
+ *
+ * atest com.android.server.devicepolicy.FactoryResetProtectionPolicyTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class FactoryResetProtectionPolicyTest {
+
+ private static final String TAG_FACTORY_RESET_PROTECTION_POLICY =
+ "factory_reset_protection_policy";
+
+ @Test
+ public void testNonDefaultFactoryResetProtectionPolicyObject() throws Exception {
+ List<String> accounts = new ArrayList<>();
+ accounts.add("Account 1");
+ accounts.add("Account 2");
+
+ FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+ .setFactoryResetProtectionAccounts(accounts)
+ .setFactoryResetProtectionDisabled(true)
+ .build();
+
+ testParcelAndUnparcel(policy);
+ testSerializationAndDeserialization(policy);
+ }
+
+ @Test
+ public void testInvalidXmlFactoryResetProtectionPolicyObject() throws Exception {
+ List<String> accounts = new ArrayList<>();
+ accounts.add("Account 1");
+ accounts.add("Account 2");
+
+ FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
+ .setFactoryResetProtectionAccounts(accounts)
+ .setFactoryResetProtectionDisabled(true)
+ .build();
+
+ testParcelAndUnparcel(policy);
+ testInvalidXmlSerializationAndDeserialization(policy);
+ }
+
+ private void testParcelAndUnparcel(FactoryResetProtectionPolicy policy) {
+ Parcel parcel = Parcel.obtain();
+ policy.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ FactoryResetProtectionPolicy actualPolicy =
+ FactoryResetProtectionPolicy.CREATOR.createFromParcel(parcel);
+ assertPoliciesAreEqual(policy, actualPolicy);
+ parcel.recycle();
+ }
+
+ private void testSerializationAndDeserialization(FactoryResetProtectionPolicy policy)
+ throws Exception {
+ ByteArrayOutputStream outStream = serialize(policy);
+ ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new InputStreamReader(inStream));
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+
+ assertPoliciesAreEqual(policy, policy.readFromXml(parser));
+ }
+
+ private void testInvalidXmlSerializationAndDeserialization(FactoryResetProtectionPolicy policy)
+ throws Exception {
+ ByteArrayOutputStream outStream = serialize(policy);
+ ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
+ XmlPullParser parser = mock(XmlPullParser.class);
+ when(parser.next()).thenThrow(XmlPullParserException.class);
+ parser.setInput(new InputStreamReader(inStream));
+
+ // If deserialization fails, then null is returned.
+ assertNull(policy.readFromXml(parser));
+ }
+
+ private ByteArrayOutputStream serialize(FactoryResetProtectionPolicy policy)
+ throws IOException {
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ final XmlSerializer outXml = new FastXmlSerializer();
+ outXml.setOutput(outStream, StandardCharsets.UTF_8.name());
+ outXml.startDocument(null, true);
+ outXml.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
+ policy.writeToXml(outXml);
+ outXml.endTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
+ outXml.endDocument();
+ outXml.flush();
+ return outStream;
+ }
+
+ private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy,
+ FactoryResetProtectionPolicy actualPolicy) {
+ assertEquals(expectedPolicy.isFactoryResetProtectionDisabled(),
+ actualPolicy.isFactoryResetProtectionDisabled());
+ assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(),
+ actualPolicy.getFactoryResetProtectionAccounts());
+ }
+
+ private void assertAccountsAreEqual(List<String> expectedAccounts,
+ List<String> actualAccounts) {
+ assertEquals(expectedAccounts.size(), actualAccounts.size());
+ for (int i = 0; i < expectedAccounts.size(); i++) {
+ assertEquals(expectedAccounts.get(i), actualAccounts.get(i));
+ }
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 919a3f6..6c2c144 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -68,6 +68,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -117,6 +118,7 @@
public final TimeDetector timeDetector;
public final TimeZoneDetector timeZoneDetector;
public final KeyChain.KeyChainConnection keyChainConnection;
+ public final PersistentDataBlockManagerInternal persistentDataBlockManagerInternal;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
public final BuildMock buildMock = new BuildMock();
@@ -160,6 +162,7 @@
timeDetector = mock(TimeDetector.class);
timeZoneDetector = mock(TimeZoneDetector.class);
keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
+ persistentDataBlockManagerInternal = mock(PersistentDataBlockManagerInternal.class);
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(realContext.getPackageManager());
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
index 63189e7..5aed194 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
@@ -16,14 +16,7 @@
package com.android.server.integrity;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.hasItems;
-import static org.junit.Assert.assertThat;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.AtomicFormula;
@@ -33,26 +26,26 @@
import android.content.integrity.Rule;
import android.util.Slog;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.integrity.parser.RuleXmlParser;
-import com.android.server.integrity.serializer.RuleXmlSerializer;
+import com.android.server.integrity.parser.RuleBinaryParser;
+import com.android.server.integrity.serializer.RuleBinarySerializer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/** Unit test for {@link IntegrityFileManager} */
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnit4.class)
public class IntegrityFileManagerTest {
private static final String TAG = "IntegrityFileManagerTest";
@@ -72,7 +65,7 @@
// Use Xml Parser/Serializer to help with debugging since we can just print the file.
mIntegrityFileManager =
new IntegrityFileManager(
- new RuleXmlParser(), new RuleXmlSerializer(), mTmpDir);
+ new RuleBinaryParser(), new RuleBinarySerializer(), mTmpDir);
Files.walk(mTmpDir.toPath())
.forEach(
path -> {
@@ -97,12 +90,19 @@
@Test
public void testGetMetadata() throws Exception {
- assertNull(mIntegrityFileManager.readMetadata());
+ assertThat(mIntegrityFileManager.readMetadata()).isNull();
mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
- assertNotNull(mIntegrityFileManager.readMetadata());
- assertEquals(VERSION, mIntegrityFileManager.readMetadata().getVersion());
- assertEquals(RULE_PROVIDER, mIntegrityFileManager.readMetadata().getRuleProvider());
+ assertThat(mIntegrityFileManager.readMetadata()).isNotNull();
+ assertThat(mIntegrityFileManager.readMetadata().getVersion()).isEqualTo(VERSION);
+ assertThat(mIntegrityFileManager.readMetadata().getRuleProvider()).isEqualTo(RULE_PROVIDER);
+ }
+
+ @Test
+ public void testIsInitialized() throws Exception {
+ assertThat(mIntegrityFileManager.initialized()).isFalse();
+ mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
+ assertThat(mIntegrityFileManager.initialized()).isTrue();
}
@Test
@@ -110,20 +110,8 @@
String packageName = "package";
String packageCert = "cert";
int version = 123;
- Rule packageNameRule =
- new Rule(
- new StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false),
- Rule.DENY);
- Rule packageCertRule =
- new Rule(
- new StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- packageCert,
- /* isHashedValue= */ false),
- Rule.DENY);
+ Rule packageNameRule = getPackageNameIndexedRule(packageName);
+ Rule packageCertRule = getAppCertificateIndexedRule(packageCert);
Rule versionCodeRule =
new Rule(
new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, version),
@@ -142,9 +130,7 @@
AtomicFormula.LE,
version))),
Rule.DENY);
- // We will test the specifics of indexing in other classes. Here, we just require that all
- // rules that are related to the given AppInstallMetadata are returned and do not assert
- // anything on other rules.
+
List<Rule> rules =
Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule);
mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules);
@@ -159,17 +145,77 @@
.build();
List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
- assertThat(rulesFetched, hasItems(
- equalTo(packageNameRule),
- equalTo(packageCertRule),
- equalTo(versionCodeRule)
- ));
+ assertThat(rulesFetched)
+ .containsExactly(packageNameRule, packageCertRule, versionCodeRule, randomRule);
}
@Test
- public void testIsInitialized() throws Exception {
- assertFalse(mIntegrityFileManager.initialized());
- mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
- assertTrue(mIntegrityFileManager.initialized());
+ public void testGetRules_indexedForManyRules() throws Exception {
+ String packageName = "package";
+ String installerName = "installer";
+ String appCertificate = "cert";
+
+ // Create a rule set with 2500 package name indexed, 2500 app certificate indexed and
+ // 500 unindexed rules.
+ List<Rule> rules = new ArrayList<>();
+
+ for (int i = 0; i < 2500; i++) {
+ rules.add(getPackageNameIndexedRule(String.format("%s%04d", packageName, i)));
+ rules.add(getAppCertificateIndexedRule(String.format("%s%04d", appCertificate, i)));
+ }
+
+ for (int i = 0; i < 70; i++) {
+ rules.add(getInstallerCertificateRule(String.format("%s%04d", installerName, i)));
+ }
+
+ // Write the rules and get them indexed.
+ mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules);
+
+ // Read the rules for a specific rule.
+ String installedPackageName = String.format("%s%04d", packageName, 264);
+ String installedAppCertificate = String.format("%s%04d", appCertificate, 1264);
+ AppInstallMetadata appInstallMetadata = new AppInstallMetadata.Builder()
+ .setPackageName(installedPackageName)
+ .setAppCertificate(installedAppCertificate)
+ .setVersionCode(250)
+ .setInstallerName("abc")
+ .setInstallerCertificate("abc")
+ .setIsPreInstalled(true)
+ .build();
+ List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
+
+ // Verify that we do not load all the rules and we have the necessary rules to evaluate.
+ assertThat(rulesFetched.size()).isEqualTo(270);
+ assertThat(rulesFetched)
+ .containsAllOf(
+ getPackageNameIndexedRule(installedPackageName),
+ getAppCertificateIndexedRule(installedAppCertificate));
+ }
+
+ private Rule getPackageNameIndexedRule(String packageName) {
+ return new Rule(
+ new StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false),
+ Rule.DENY);
+ }
+
+ private Rule getAppCertificateIndexedRule(String appCertificate) {
+ return new Rule(
+ new StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE,
+ appCertificate,
+ /* isHashedValue= */ false),
+ Rule.DENY);
+ }
+
+ private Rule getInstallerCertificateRule(String installerCert) {
+ return new Rule(
+ new StringAtomicFormula(
+ AtomicFormula.INSTALLER_NAME,
+ installerCert,
+ /* isHashedValue= */ false),
+ Rule.DENY);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java
index 1eb5eb5..4fa7302 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java
@@ -121,6 +121,26 @@
}
@Test
+ public void testBitTrackedInputStream_canReadMoreRules() throws IOException {
+ String packageName = "com.test.app";
+ byte[] testInput =
+ getBytes(
+ IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName));
+
+ BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(testInput);
+ assertThat(bitTrackedInputStream.canReadMoreRules(2)).isTrue();
+
+ // Read until the string parameter.
+ String stringValue = BinaryFileOperations.getStringValue(bitTrackedInputStream);
+
+ // Verify that the read bytes are counted.
+ assertThat(stringValue).isEqualTo(packageName);
+ assertThat(bitTrackedInputStream.canReadMoreRules(2)).isFalse();
+ }
+
+ @Test
public void testBitTrackedInputStream_moveCursorForwardFailsIfAlreadyRead() throws IOException {
String packageName = "com.test.app";
byte[] testInput =
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
index 51f5c75..881b3d6 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
@@ -48,7 +48,6 @@
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -656,65 +655,4 @@
/* expectedExceptionMessageRegex */ "A rule must end with a '1' bit",
() -> binaryParser.parse(rule.array()));
}
-
- @Test
- public void testBinaryStream_multipleRules_indexingIdentifiesParsesIndexRangeCorrectly()
- throws Exception {
- String packageName2 = "com.test.2";
-
- byte[] ruleBytes1 = getBytes(getRulesWithPackageName("com.test.1"));
- byte[] ruleBytes2 = getBytes(getRulesWithPackageName(packageName2));
- byte[] ruleBytes3 = getBytes(getRulesWithPackageName("com.test.3"));
-
- ByteBuffer rule =
- ByteBuffer.allocate(
- DEFAULT_FORMAT_VERSION_BYTES.length
- + ruleBytes1.length
- + ruleBytes2.length
- + ruleBytes3.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes1);
- rule.put(ruleBytes2);
- rule.put(ruleBytes3);
- InputStream inputStream = new ByteArrayInputStream(rule.array());
-
- RuleParser binaryParser = new RuleBinaryParser();
-
- List<RuleIndexRange> indexRanges = new ArrayList<>();
- indexRanges.add(
- new RuleIndexRange(
- DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length,
- DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length
- + ruleBytes2.length));
- List<Rule> rules = binaryParser.parse(inputStream, indexRanges);
-
- Rule expectedRule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.NOT,
- Collections.singletonList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName2,
- /* isHashedValue= */ false))),
- Rule.DENY);
-
- assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
- }
-
- private static String getRulesWithPackageName(String packageName) {
- return START_BIT
- + COMPOUND_FORMULA_START_BITS
- + NOT
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
-
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
index 742952e..291cfee 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
@@ -115,7 +115,8 @@
+ getKeyValueString(START_INDEXING_KEY, 500)
+ getKeyValueString(END_INDEXING_KEY, 900)
+ getKeyValueString(START_INDEXING_KEY, 900)
- + getKeyValueString(END_INDEXING_KEY, 945));
+ + getKeyValueString(END_INDEXING_KEY, 945)
+ + getBits(1, 1));
ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
rule.put(stringBytes);
InputStream inputStream = new ByteArrayInputStream(rule.array());
@@ -152,7 +153,8 @@
+ getKeyValueString("888", 800)
+ getKeyValueString(END_INDEXING_KEY, 900)
+ getKeyValueString(START_INDEXING_KEY, 900)
- + getKeyValueString(END_INDEXING_KEY, 945));
+ + getKeyValueString(END_INDEXING_KEY, 945)
+ + getBits(1, 1));
ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
rule.put(stringBytes);
return new ByteArrayInputStream(rule.array());
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 eb6698b..bc2aac0 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
@@ -135,15 +135,14 @@
.isEqualTo(expectedRuleOutputStream.toByteArray());
ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream();
+ String serializedIndexingBytes =
+ SERIALIZED_START_INDEXING_KEY
+ + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+ + SERIALIZED_END_INDEXING_KEY
+ + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */32);
byte[] expectedIndexingBytes =
- getBytes(
- SERIALIZED_START_INDEXING_KEY
- + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
- + SERIALIZED_END_INDEXING_KEY
- + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */
- 32));
- expectedIndexingOutputStream.write(expectedIndexingBytes);
- expectedIndexingOutputStream.write(expectedIndexingBytes);
+ getBytes(serializedIndexingBytes + serializedIndexingBytes
+ + serializedIndexingBytes + getBits(1, 1));
expectedIndexingOutputStream.write(expectedIndexingBytes);
assertThat(indexingOutputStream.toByteArray())
.isEqualTo(expectedIndexingOutputStream.toByteArray());
@@ -197,15 +196,16 @@
+ getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+ SERIALIZED_END_INDEXING_KEY
+ getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32);
- expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForIndexed));
- expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForIndexed));
String expectedIndexingBitsForUnindexed =
SERIALIZED_START_INDEXING_KEY
+ getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+ SERIALIZED_END_INDEXING_KEY
- + getBits(DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(
- expectedBits).length, /* numOfBits= */ 32);
- expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForUnindexed));
+ + getBits(DEFAULT_FORMAT_VERSION_BYTES.length
+ + getBytes(expectedBits).length, /* numOfBits= */ 32);
+ expectedIndexingOutputStream.write(getBytes(
+ expectedIndexingBitsForIndexed + expectedIndexingBitsForIndexed
+ + expectedIndexingBitsForUnindexed + getBits(1, 1)));
+
assertThat(indexingOutputStream.toByteArray())
.isEqualTo(expectedIndexingOutputStream.toByteArray());
}
@@ -564,7 +564,6 @@
expectedIndexingBytesForPackageNameIndexed +=
SERIALIZED_END_INDEXING_KEY
+ getBits(totalBytesWritten, /* numOfBits= */ 32);
- expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForPackageNameIndexed));
String expectedIndexingBytesForAppCertificateIndexed =
SERIALIZED_START_INDEXING_KEY
@@ -588,7 +587,6 @@
expectedIndexingBytesForAppCertificateIndexed +=
SERIALIZED_END_INDEXING_KEY
+ getBits(totalBytesWritten, /* numOfBits= */ 32);
- expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForAppCertificateIndexed));
String expectedIndexingBytesForUnindexed =
SERIALIZED_START_INDEXING_KEY
@@ -603,8 +601,11 @@
expectedIndexingBytesForUnindexed +=
SERIALIZED_END_INDEXING_KEY
+ getBits(totalBytesWritten, /* numOfBits= */ 32);
- expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForUnindexed));
-
+ expectedIndexingOutputStream.write(
+ getBytes(expectedIndexingBytesForPackageNameIndexed
+ + expectedIndexingBytesForAppCertificateIndexed
+ + expectedIndexingBytesForUnindexed
+ + getBits(1, 1)));
assertThat(ruleOutputStream.toByteArray())
.isEqualTo(expectedOrderedRuleOutputStream.toByteArray());
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
index 55fada4..1674422 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
@@ -38,10 +38,8 @@
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.TreeMap;
/** Unit tests for {@link RuleIndexingDetailsIdentifier}. */
@RunWith(JUnit4.class)
@@ -140,7 +138,7 @@
List<Rule> ruleList = new ArrayList();
ruleList.add(RULE_WITH_PACKAGE_NAME);
- Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+ Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
// Verify the resulting map content.
assertThat(result.keySet())
@@ -157,7 +155,7 @@
List<Rule> ruleList = new ArrayList();
ruleList.add(RULE_WITH_APP_CERTIFICATE);
- Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+ Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
assertThat(result.keySet())
.containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
@@ -174,7 +172,7 @@
List<Rule> ruleList = new ArrayList();
ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS);
- Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+ Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
assertThat(result.keySet())
.containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
@@ -189,7 +187,7 @@
List<Rule> ruleList = new ArrayList();
ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS);
- Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+ Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
assertThat(result.keySet())
.containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
@@ -215,7 +213,7 @@
List<Rule> ruleList = new ArrayList();
ruleList.add(negatedRule);
- Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+ Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
assertThat(result.keySet())
.containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
@@ -225,7 +223,7 @@
}
@Test
- public void getIndexType_allRulesTogetherInValidOrder() {
+ public void getIndexType_allRulesTogetherSplitCorrectly() {
Rule packageNameRuleA = getRuleWithPackageName("aaa");
Rule packageNameRuleB = getRuleWithPackageName("bbb");
Rule packageNameRuleC = getRuleWithPackageName("ccc");
@@ -243,38 +241,20 @@
ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS);
ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS);
- Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
+ Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
assertThat(result.keySet())
.containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
// We check asserts this way to ensure ordering based on package name.
assertThat(result.get(PACKAGE_NAME_INDEXED).keySet()).containsExactly("aaa", "bbb", "ccc");
- Iterator<String> keySetIterator = result.get(PACKAGE_NAME_INDEXED).keySet().iterator();
- assertThat(keySetIterator.next()).isEqualTo("aaa");
- assertThat(keySetIterator.next()).isEqualTo("bbb");
- assertThat(keySetIterator.next()).isEqualTo("ccc");
- assertThat(result.get(PACKAGE_NAME_INDEXED).get("aaa")).containsExactly(packageNameRuleA);
- assertThat(result.get(PACKAGE_NAME_INDEXED).get("bbb")).containsExactly(packageNameRuleB);
- assertThat(result.get(PACKAGE_NAME_INDEXED).get("ccc")).containsExactly(packageNameRuleC);
// We check asserts this way to ensure ordering based on app certificate.
assertThat(result.get(APP_CERTIFICATE_INDEXED).keySet()).containsExactly("cert1", "cert2",
"cert3");
- keySetIterator = result.get(APP_CERTIFICATE_INDEXED).keySet().iterator();
- assertThat(keySetIterator.next()).isEqualTo("cert1");
- assertThat(keySetIterator.next()).isEqualTo("cert2");
- assertThat(keySetIterator.next()).isEqualTo("cert3");
- assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert1")).containsExactly(
- certificateRule1);
- assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert2")).containsExactly(
- certificateRule2);
- assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert3")).containsExactly(
- certificateRule3);
assertThat(result.get(NOT_INDEXED).get("N/A"))
- .containsExactly(
- RULE_WITH_INSTALLER_RESTRICTIONS,
+ .containsExactly(RULE_WITH_INSTALLER_RESTRICTIONS,
RULE_WITH_NONSTRING_RESTRICTIONS);
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index c7dbad8..3d40637 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -37,6 +37,7 @@
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.TAG_ALL;
+import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.MB_IN_BYTES;
@@ -1758,6 +1759,59 @@
}
/**
+ * Test that when StatsProvider triggers limit reached, new limit will be calculated and
+ * re-armed.
+ */
+ @Test
+ public void testStatsProviderLimitReached() throws Exception {
+ final int CYCLE_DAY = 15;
+
+ final NetworkStats stats = new NetworkStats(0L, 1);
+ stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+ 2999, 1, 2000, 1, 0);
+ when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
+ .thenReturn(stats.getTotalBytes());
+ when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
+ .thenReturn(stats);
+
+ // Get active mobile network in place
+ expectMobileDefaults();
+ mService.updateNetworks();
+ verify(mStatsService).setStatsProviderLimit(TEST_IFACE, Long.MAX_VALUE);
+
+ // Set limit to 10KB.
+ setNetworkPolicies(new NetworkPolicy(
+ sTemplateMobileAll, CYCLE_DAY, TIMEZONE_UTC, WARNING_DISABLED, 10000L,
+ true));
+ postMsgAndWaitForCompletion();
+
+ // Verifies that remaining quota is set to providers.
+ verify(mStatsService).setStatsProviderLimit(TEST_IFACE, 10000L - 4999L);
+
+ reset(mStatsService);
+
+ // Increase the usage.
+ stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+ 1000, 1, 999, 1, 0);
+ when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
+ .thenReturn(stats.getTotalBytes());
+ when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
+ .thenReturn(stats);
+
+ // Simulates that limit reached fires earlier by provider, but actually the quota is not
+ // yet reached.
+ final NetworkPolicyManagerInternal npmi = LocalServices
+ .getService(NetworkPolicyManagerInternal.class);
+ npmi.onStatsProviderLimitReached("TEST");
+
+ // Verifies that the limit reached leads to a force update and new limit should be set.
+ postMsgAndWaitForCompletion();
+ verify(mStatsService).forceUpdate();
+ postMsgAndWaitForCompletion();
+ verify(mStatsService).setStatsProviderLimit(TEST_IFACE, 10000L - 4999L - 1999L);
+ }
+
+ /**
* Exhaustively test isUidNetworkingBlocked to output the expected results based on external
* conditions.
*/
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index cc170af..c56034a 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -335,7 +335,9 @@
}).when(driver).getProperties_2_3(any());
}
- mService = new SoundTriggerMiddlewareImpl(mHalDriver, mAudioSessionProvider);
+ mService = new SoundTriggerMiddlewareImpl(() -> {
+ return mHalDriver;
+ }, mAudioSessionProvider);
}
private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_0(ISoundTriggerModule module,
@@ -798,12 +800,12 @@
@Override
public boolean linkToDeath(DeathRecipient recipient, long cookie) {
- throw new UnsupportedOperationException();
+ return true;
}
@Override
public boolean unlinkToDeath(DeathRecipient recipient) {
- throw new UnsupportedOperationException();
+ return true;
}
};
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index aaf9799..d940a6a 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -18,7 +18,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -26,7 +25,6 @@
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.PhoneTimeSuggestion;
-import android.content.Intent;
import android.icu.util.Calendar;
import android.icu.util.GregorianCalendar;
import android.icu.util.TimeZone;
@@ -78,8 +76,7 @@
long expectedSystemClockMillis =
mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
- mScript.verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+ mScript.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
.assertLatestPhoneSuggestion(phoneId, timeSuggestion);
}
@@ -118,8 +115,7 @@
mScript.calculateTimeInMillisForNow(timeSuggestion1.getUtcTime());
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1)
.assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
}
@@ -146,8 +142,7 @@
mScript.calculateTimeInMillisForNow(timeSuggestion3.getUtcTime());
mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis3, true /* expectNetworkBroadcast */)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis3)
.assertLatestPhoneSuggestion(phoneId, timeSuggestion3);
}
}
@@ -175,8 +170,7 @@
mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
.assertLatestPhoneSuggestion(phone1Id, null)
.assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
}
@@ -193,8 +187,7 @@
mScript.calculateTimeInMillisForNow(phone1TimeSuggestion.getUtcTime());
mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
.assertLatestPhoneSuggestion(phone1Id, phone1TimeSuggestion);
}
@@ -227,8 +220,7 @@
mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis)
.assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
}
}
@@ -265,8 +257,7 @@
mScript.simulateTimePassing();
long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1);
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1)
.assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
// The UTC time increment should be larger than the system clock update threshold so we
@@ -304,8 +295,7 @@
PhoneTimeSuggestion timeSuggestion4 =
createPhoneTimeSuggestion(phoneId, utcTime4);
mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis4, true /* expectNetworkBroadcast */)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4)
.assertLatestPhoneSuggestion(phoneId, timeSuggestion4);
}
@@ -339,8 +329,7 @@
// Turn on auto time detection.
mScript.simulateAutoTimeDetectionToggle()
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1)
.assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
// Turn off auto time detection.
@@ -367,8 +356,7 @@
// Turn on auto time detection.
mScript.simulateAutoTimeDetectionToggle()
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis2, true /* expectNetworkBroadcast */)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2)
.assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
}
@@ -388,7 +376,7 @@
mScript.calculateTimeInMillisForNow(phoneSuggestion.getUtcTime());
mScript.simulatePhoneTimeSuggestion(phoneSuggestion)
.verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis, true /* expectedNetworkBroadcast */)
+ expectedSystemClockMillis /* expectedNetworkBroadcast */)
.assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
// Look inside and check what the strategy considers the current best phone suggestion.
@@ -416,8 +404,7 @@
long expectedSystemClockMillis =
mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
mScript.simulateManualTimeSuggestion(timeSuggestion)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis, false /* expectNetworkBroadcast */);
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
}
@Test
@@ -439,8 +426,7 @@
long expectedAutoClockMillis =
mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedAutoClockMillis, true /* expectNetworkBroadcast */)
+ .verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis)
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
// Simulate the passage of time.
@@ -463,8 +449,7 @@
long expectedManualClockMillis =
mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUtcTime());
mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedManualClockMillis, false /* expectNetworkBroadcast */)
+ .verifySystemClockWasSetAndResetCallTracking(expectedManualClockMillis)
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
// Simulate the passage of time.
@@ -475,8 +460,7 @@
expectedAutoClockMillis =
mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
- mScript.verifySystemClockWasSetAndResetCallTracking(
- expectedAutoClockMillis, true /* expectNetworkBroadcast */)
+ mScript.verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis)
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
// Switch back to manual - nothing should happen to the clock.
@@ -514,8 +498,7 @@
long expectedSystemClockMillis =
mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
mScript.simulateNetworkTimeSuggestion(timeSuggestion)
- .verifySystemClockWasSetAndResetCallTracking(
- expectedSystemClockMillis, false /* expectNetworkBroadcast */);
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
}
@Test
@@ -550,8 +533,7 @@
mScript.simulateTimePassing(smallTimeIncrementMillis)
.simulateNetworkTimeSuggestion(networkTimeSuggestion1)
.verifySystemClockWasSetAndResetCallTracking(
- mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime()),
- false /* expectNetworkBroadcast */);
+ mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime()));
// Check internal state.
mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, null)
@@ -570,8 +552,7 @@
mScript.simulateTimePassing(smallTimeIncrementMillis)
.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(
- mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()),
- true /* expectNetworkBroadcast */);
+ mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()));
// Check internal state.
mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
@@ -622,8 +603,7 @@
// Verify the latest network time now wins.
mScript.verifySystemClockWasSetAndResetCallTracking(
- mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime()),
- false /* expectNetworkTimeBroadcast */);
+ mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime()));
// Check internal state.
mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
@@ -645,7 +625,6 @@
// Tracking operations.
private boolean mSystemClockWasSet;
- private Intent mBroadcastSent;
@Override
public int systemClockUpdateThresholdMillis() {
@@ -672,7 +651,6 @@
@Override
public long systemClockMillis() {
- assertWakeLockAcquired();
return mSystemClockMillis;
}
@@ -689,12 +667,6 @@
mWakeLockAcquired = false;
}
- @Override
- public void sendStickyBroadcast(Intent intent) {
- assertNotNull(intent);
- mBroadcastSent = intent;
- }
-
// Methods below are for managing the fake's behavior.
void pokeSystemClockUpdateThreshold(int thresholdMillis) {
@@ -739,17 +711,8 @@
assertEquals(expectedSystemClockMillis, mSystemClockMillis);
}
- void verifyIntentWasBroadcast() {
- assertTrue(mBroadcastSent != null);
- }
-
- void verifyIntentWasNotBroadcast() {
- assertNull(mBroadcastSent);
- }
-
void resetCallTracking() {
mSystemClockWasSet = false;
- mBroadcastSent = null;
}
private void assertWakeLockAcquired() {
@@ -832,17 +795,12 @@
Script verifySystemClockWasNotSetAndResetCallTracking() {
mFakeCallback.verifySystemClockNotSet();
- mFakeCallback.verifyIntentWasNotBroadcast();
mFakeCallback.resetCallTracking();
return this;
}
- Script verifySystemClockWasSetAndResetCallTracking(
- long expectedSystemClockMillis, boolean expectNetworkBroadcast) {
+ Script verifySystemClockWasSetAndResetCallTracking(long expectedSystemClockMillis) {
mFakeCallback.verifySystemClockWasSet(expectedSystemClockMillis);
- if (expectNetworkBroadcast) {
- mFakeCallback.verifyIntentWasBroadcast();
- }
mFakeCallback.resetCallTracking();
return this;
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 1dd7e64..6aca58f 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -23,7 +23,8 @@
import static android.app.usage.UsageEvents.Event.SYSTEM_INTERACTION;
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
@@ -501,12 +502,18 @@
// Can force to NEVER
mInjector.mElapsedRealtime = HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_MAIN_FORCED);
+ REASON_MAIN_FORCED_BY_USER);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
- // Prediction can't override FORCED reason
+ // Prediction can't override FORCED reasons
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_PREDICTED);
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_MAIN_FORCED);
+ REASON_MAIN_FORCED_BY_USER);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_PREDICTED);
assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
@@ -631,7 +638,7 @@
mInjector.mElapsedRealtime = 1 * RARE_THRESHOLD + 100;
// Make sure app is in NEVER bucket
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_MAIN_FORCED);
+ REASON_MAIN_FORCED_BY_USER);
mController.checkIdleStates(USER_ID);
assertBucket(STANDBY_BUCKET_NEVER);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java
index f7cd6a3..9906585 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java
@@ -21,12 +21,10 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
-import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
-import android.text.TextUtils;
import android.util.Slog;
-import java.util.Locale;
+import java.io.PrintWriter;
import java.util.UUID;
/**
@@ -39,7 +37,7 @@
static final boolean DBG = false;
private static final String NAME = "st_sound_model.db";
- private static final int VERSION = 1;
+ private static final int VERSION = 2;
// Sound trigger-based sound models.
public static interface GenericSoundModelContract {
@@ -47,15 +45,16 @@
public static final String KEY_MODEL_UUID = "model_uuid";
public static final String KEY_VENDOR_UUID = "vendor_uuid";
public static final String KEY_DATA = "data";
+ public static final String KEY_MODEL_VERSION = "model_version";
}
-
// Table Create Statement for the sound trigger table
private static final String CREATE_TABLE_ST_SOUND_MODEL = "CREATE TABLE "
+ GenericSoundModelContract.TABLE + "("
+ GenericSoundModelContract.KEY_MODEL_UUID + " TEXT PRIMARY KEY,"
+ GenericSoundModelContract.KEY_VENDOR_UUID + " TEXT,"
- + GenericSoundModelContract.KEY_DATA + " BLOB" + " )";
+ + GenericSoundModelContract.KEY_DATA + " BLOB,"
+ + GenericSoundModelContract.KEY_MODEL_VERSION + " INTEGER" + " )";
public SoundTriggerDbHelper(Context context) {
@@ -70,9 +69,13 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- // TODO: For now, drop older tables and recreate new ones.
- db.execSQL("DROP TABLE IF EXISTS " + GenericSoundModelContract.TABLE);
- onCreate(db);
+ if (oldVersion == 1) {
+ // In version 2, a model version number was added.
+ Slog.d(TAG, "Adding model version column");
+ db.execSQL("ALTER TABLE " + GenericSoundModelContract.TABLE + " ADD COLUMN "
+ + GenericSoundModelContract.KEY_MODEL_VERSION + " INTEGER DEFAULT -1");
+ oldVersion++;
+ }
}
/**
@@ -86,6 +89,7 @@
values.put(GenericSoundModelContract.KEY_MODEL_UUID, soundModel.uuid.toString());
values.put(GenericSoundModelContract.KEY_VENDOR_UUID, soundModel.vendorUuid.toString());
values.put(GenericSoundModelContract.KEY_DATA, soundModel.data);
+ values.put(GenericSoundModelContract.KEY_MODEL_VERSION, soundModel.version);
try {
return db.insertWithOnConflict(GenericSoundModelContract.TABLE, null, values,
@@ -113,8 +117,10 @@
GenericSoundModelContract.KEY_DATA));
String vendor_uuid = c.getString(
c.getColumnIndex(GenericSoundModelContract.KEY_VENDOR_UUID));
+ int version = c.getInt(
+ c.getColumnIndex(GenericSoundModelContract.KEY_MODEL_VERSION));
return new GenericSoundModel(model_uuid, UUID.fromString(vendor_uuid),
- data);
+ data, version);
} while (c.moveToNext());
}
} finally {
@@ -142,4 +148,48 @@
}
}
}
+
+ public void dump(PrintWriter pw) {
+ synchronized(this) {
+ String selectQuery = "SELECT * FROM " + GenericSoundModelContract.TABLE;
+ SQLiteDatabase db = getReadableDatabase();
+ Cursor c = db.rawQuery(selectQuery, null);
+ try {
+ pw.println(" Enrolled GenericSoundModels:");
+ if (c.moveToFirst()) {
+ String[] columnNames = c.getColumnNames();
+ do {
+ for (String name : columnNames) {
+ int colNameIndex = c.getColumnIndex(name);
+ int type = c.getType(colNameIndex);
+ switch (type) {
+ case Cursor.FIELD_TYPE_STRING:
+ pw.printf(" %s: %s\n", name,
+ c.getString(colNameIndex));
+ break;
+ case Cursor.FIELD_TYPE_BLOB:
+ pw.printf(" %s: data blob\n", name);
+ break;
+ case Cursor.FIELD_TYPE_INTEGER:
+ pw.printf(" %s: %d\n", name,
+ c.getInt(colNameIndex));
+ break;
+ case Cursor.FIELD_TYPE_FLOAT:
+ pw.printf(" %s: %f\n", name,
+ c.getFloat(colNameIndex));
+ break;
+ case Cursor.FIELD_TYPE_NULL:
+ pw.printf(" %s: null\n", name);
+ break;
+ }
+ }
+ pw.println();
+ } while (c.moveToNext());
+ }
+ } finally {
+ c.close();
+ db.close();
+ }
+ }
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index e37755b..767010a 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -1495,6 +1495,9 @@
// log
sEventLogger.dump(pw);
+ // enrolled models
+ mDbHelper.dump(pw);
+
// stats
mSoundModelStatTracker.dump(pw);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index dd7b5a8..c58b6da 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -27,6 +27,7 @@
import android.text.TextUtils;
import android.util.Slog;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -43,7 +44,7 @@
static final boolean DBG = false;
private static final String NAME = "sound_model.db";
- private static final int VERSION = 6;
+ private static final int VERSION = 7;
public static interface SoundModelContract {
public static final String TABLE = "sound_model";
@@ -56,6 +57,7 @@
public static final String KEY_LOCALE = "locale";
public static final String KEY_HINT_TEXT = "hint_text";
public static final String KEY_USERS = "users";
+ public static final String KEY_MODEL_VERSION = "model_version";
}
// Table Create Statement
@@ -70,6 +72,7 @@
+ SoundModelContract.KEY_LOCALE + " TEXT,"
+ SoundModelContract.KEY_HINT_TEXT + " TEXT,"
+ SoundModelContract.KEY_USERS + " TEXT,"
+ + SoundModelContract.KEY_MODEL_VERSION + " INTEGER,"
+ "PRIMARY KEY (" + SoundModelContract.KEY_KEYPHRASE_ID + ","
+ SoundModelContract.KEY_LOCALE + ","
+ SoundModelContract.KEY_USERS + ")"
@@ -138,6 +141,13 @@
}
oldVersion++;
}
+ if (oldVersion == 6) {
+ // In version 7, a model version number was added.
+ Slog.d(TAG, "Adding model version column");
+ db.execSQL("ALTER TABLE " + SoundModelContract.TABLE + " ADD COLUMN "
+ + SoundModelContract.KEY_MODEL_VERSION + " INTEGER DEFAULT -1");
+ oldVersion++;
+ }
}
/**
@@ -155,6 +165,7 @@
}
values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
values.put(SoundModelContract.KEY_DATA, soundModel.data);
+ values.put(SoundModelContract.KEY_MODEL_VERSION, soundModel.version);
if (soundModel.keyphrases != null && soundModel.keyphrases.length == 1) {
values.put(SoundModelContract.KEY_KEYPHRASE_ID, soundModel.keyphrases[0].id);
@@ -250,6 +261,8 @@
c.getColumnIndex(SoundModelContract.KEY_LOCALE));
String text = c.getString(
c.getColumnIndex(SoundModelContract.KEY_HINT_TEXT));
+ int version = c.getInt(
+ c.getColumnIndex(SoundModelContract.KEY_MODEL_VERSION));
// Only add keyphrases meant for the current user.
if (users == null) {
@@ -282,7 +295,7 @@
vendorUuid = UUID.fromString(vendorUuidString);
}
KeyphraseSoundModel model = new KeyphraseSoundModel(
- UUID.fromString(modelUuid), vendorUuid, data, keyphrases);
+ UUID.fromString(modelUuid), vendorUuid, data, keyphrases, version);
if (DBG) {
Slog.d(TAG, "Found SoundModel for the given keyphrase/locale/user: "
+ model);
@@ -325,6 +338,10 @@
return users;
}
+ /**
+ * SoundModelRecord is no longer used, and it should only be used on database migration.
+ * This class does not need to be modified when modifying the database scheme.
+ */
private static class SoundModelRecord {
public final String modelUuid;
public final String vendorUuid;
@@ -413,4 +430,48 @@
return a == b;
}
}
+
+ public void dump(PrintWriter pw) {
+ synchronized(this) {
+ String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE;
+ SQLiteDatabase db = getReadableDatabase();
+ Cursor c = db.rawQuery(selectQuery, null);
+ try {
+ pw.println(" Enrolled KeyphraseSoundModels:");
+ if (c.moveToFirst()) {
+ String[] columnNames = c.getColumnNames();
+ do {
+ for (String name : columnNames) {
+ int colNameIndex = c.getColumnIndex(name);
+ int type = c.getType(colNameIndex);
+ switch (type) {
+ case Cursor.FIELD_TYPE_STRING:
+ pw.printf(" %s: %s\n", name,
+ c.getString(colNameIndex));
+ break;
+ case Cursor.FIELD_TYPE_BLOB:
+ pw.printf(" %s: data blob\n", name);
+ break;
+ case Cursor.FIELD_TYPE_INTEGER:
+ pw.printf(" %s: %d\n", name,
+ c.getInt(colNameIndex));
+ break;
+ case Cursor.FIELD_TYPE_FLOAT:
+ pw.printf(" %s: %f\n", name,
+ c.getFloat(colNameIndex));
+ break;
+ case Cursor.FIELD_TYPE_NULL:
+ pw.printf(" %s: null\n", name);
+ break;
+ }
+ }
+ pw.println();
+ } while (c.moveToNext());
+ }
+ } finally {
+ c.close();
+ db.close();
+ }
+ }
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 06c8074..506c67e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -159,9 +159,13 @@
}
}
+ private boolean isSupported(UserInfo user) {
+ return user.isFull();
+ }
+
@Override
- public boolean isSupported(UserInfo userInfo) {
- return userInfo.isFull();
+ public boolean isSupportedUser(TargetUser user) {
+ return isSupported(user.getUserInfo());
}
@Override
@@ -1343,6 +1347,7 @@
pw.println(" mCurUserUnlocked: " + mCurUserUnlocked);
pw.println(" mCurUserSupported: " + mCurUserSupported);
dumpSupportedUsers(pw, " ");
+ mDbHelper.dump(pw);
if (mImpl == null) {
pw.println(" (No active implementation)");
return;
diff --git a/telecomm/TEST_MAPPING b/telecomm/TEST_MAPPING
new file mode 100644
index 0000000..d585666
--- /dev/null
+++ b/telecomm/TEST_MAPPING
@@ -0,0 +1,29 @@
+{
+ "presubmit": [
+ {
+ "name": "TeleServiceTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "TelecomUnitTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "TelephonyProviderTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/telephony/TEST_MAPPING b/telephony/TEST_MAPPING
new file mode 100644
index 0000000..d585666
--- /dev/null
+++ b/telephony/TEST_MAPPING
@@ -0,0 +1,29 @@
+{
+ "presubmit": [
+ {
+ "name": "TeleServiceTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "TelecomUnitTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "TelephonyProviderTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index a17a19c..9bc534c 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -27,10 +27,10 @@
import android.os.UserHandle;
import android.permission.IPermissionManager;
import android.provider.Settings;
-import android.util.Log;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -141,8 +141,8 @@
ContentResolver contentResolver, int userId,
ArraySet<String> systemCarrierAppsDisabledUntilUsed,
ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) {
- List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
- userId, systemCarrierAppsDisabledUntilUsed);
+ List<ApplicationInfo> candidates = getDefaultNotUpdatedCarrierAppCandidatesHelper(
+ packageManager, userId, systemCarrierAppsDisabledUntilUsed);
if (candidates == null || candidates.isEmpty()) {
return;
}
@@ -178,15 +178,16 @@
}
}
+ int enabledSetting = packageManager.getApplicationEnabledSetting(packageName,
+ userId);
if (hasPrivileges) {
// Only update enabled state for the app on /system. Once it has been
// updated we shouldn't touch it.
- if (!ai.isUpdatedSystemApp()
- && (ai.enabledSetting
+ if (enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
- || ai.enabledSetting
+ || enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
- || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) {
+ || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
Log.i(TAG, "Update state(" + packageName + "): ENABLED for user "
+ userId);
packageManager.setSystemAppInstallState(
@@ -204,9 +205,12 @@
// Also enable any associated apps for this carrier app.
if (associatedAppList != null) {
for (ApplicationInfo associatedApp : associatedAppList) {
- if (associatedApp.enabledSetting
+ int associatedAppEnabledSetting =
+ packageManager.getApplicationEnabledSetting(
+ associatedApp.packageName, userId);
+ if (associatedAppEnabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
- || associatedApp.enabledSetting
+ || associatedAppEnabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
|| (associatedApp.flags
& ApplicationInfo.FLAG_INSTALLED) == 0) {
@@ -231,8 +235,7 @@
} else { // No carrier privileges
// Only update enabled state for the app on /system. Once it has been
// updated we shouldn't touch it.
- if (!ai.isUpdatedSystemApp()
- && ai.enabledSetting
+ if (enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
&& (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
Log.i(TAG, "Update state(" + packageName
@@ -249,7 +252,10 @@
if (!hasRunOnce) {
if (associatedAppList != null) {
for (ApplicationInfo associatedApp : associatedAppList) {
- if (associatedApp.enabledSetting
+ int associatedAppEnabledSetting =
+ packageManager.getApplicationEnabledSetting(
+ associatedApp.packageName, userId);
+ if (associatedAppEnabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
&& (associatedApp.flags
& ApplicationInfo.FLAG_INSTALLED) != 0) {
@@ -360,6 +366,31 @@
return apps;
}
+ private static List<ApplicationInfo> getDefaultNotUpdatedCarrierAppCandidatesHelper(
+ IPackageManager packageManager,
+ int userId,
+ ArraySet<String> systemCarrierAppsDisabledUntilUsed) {
+ if (systemCarrierAppsDisabledUntilUsed == null) {
+ return null;
+ }
+
+ int size = systemCarrierAppsDisabledUntilUsed.size();
+ if (size == 0) {
+ return null;
+ }
+
+ List<ApplicationInfo> apps = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ String packageName = systemCarrierAppsDisabledUntilUsed.valueAt(i);
+ ApplicationInfo ai =
+ getApplicationInfoIfNotUpdatedSystemApp(packageManager, userId, packageName);
+ if (ai != null) {
+ apps.add(ai);
+ }
+ }
+ return apps;
+ }
+
private static Map<String, List<ApplicationInfo>> getDefaultCarrierAssociatedAppsHelper(
IPackageManager packageManager,
int userId,
@@ -372,11 +403,11 @@
systemCarrierAssociatedAppsDisabledUntilUsed.valueAt(i);
for (int j = 0; j < associatedAppPackages.size(); j++) {
ApplicationInfo ai =
- getApplicationInfoIfSystemApp(
+ getApplicationInfoIfNotUpdatedSystemApp(
packageManager, userId, associatedAppPackages.get(j));
// Only update enabled state for the app on /system. Once it has been updated we
// shouldn't touch it.
- if (ai != null && !ai.isUpdatedSystemApp()) {
+ if (ai != null) {
List<ApplicationInfo> appList = associatedApps.get(carrierAppPackage);
if (appList == null) {
appList = new ArrayList<>();
@@ -390,6 +421,26 @@
}
@Nullable
+ private static ApplicationInfo getApplicationInfoIfNotUpdatedSystemApp(
+ IPackageManager packageManager,
+ int userId,
+ String packageName) {
+ try {
+ ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
+ | PackageManager.MATCH_SYSTEM_ONLY
+ | PackageManager.MATCH_FACTORY_ONLY, userId);
+ if (ai != null) {
+ return ai;
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not reach PackageManager", e);
+ }
+ return null;
+ }
+
+ @Nullable
private static ApplicationInfo getApplicationInfoIfSystemApp(
IPackageManager packageManager,
int userId,
@@ -397,8 +448,9 @@
try {
ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
- | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, userId);
- if (ai != null && ai.isSystemApp()) {
+ | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
+ | PackageManager.MATCH_SYSTEM_ONLY, userId);
+ if (ai != null) {
return ai;
}
} catch (RemoteException e) {
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
index e01deb2..1e1cdba 100644
--- a/telephony/java/android/telephony/CallQuality.java
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -80,6 +80,9 @@
private int mMaxRelativeJitter;
private int mAverageRoundTripTime;
private int mCodecType;
+ private boolean mRtpInactivityDetected;
+ private boolean mRxSilenceDetected;
+ private boolean mTxSilenceDetected;
/** @hide **/
public CallQuality(Parcel in) {
@@ -94,6 +97,9 @@
mMaxRelativeJitter = in.readInt();
mAverageRoundTripTime = in.readInt();
mCodecType = in.readInt();
+ mRtpInactivityDetected = in.readBoolean();
+ mRxSilenceDetected = in.readBoolean();
+ mTxSilenceDetected = in.readBoolean();
}
/** @hide **/
@@ -109,7 +115,7 @@
* @param numRtpPacketsReceived RTP packets received from network
* @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
* transmitted
- * @param numRtpPacketsNotReceived RTP packets which were lost in network and never recieved
+ * @param numRtpPacketsNotReceived RTP packets which were lost in network and never received
* @param averageRelativeJitter average relative jitter in milliseconds
* @param maxRelativeJitter maximum relative jitter in milliseconds
* @param averageRoundTripTime average round trip delay in milliseconds
@@ -127,6 +133,48 @@
int maxRelativeJitter,
int averageRoundTripTime,
int codecType) {
+ this(downlinkCallQualityLevel, uplinkCallQualityLevel, callDuration,
+ numRtpPacketsTransmitted, numRtpPacketsReceived, numRtpPacketsTransmittedLost,
+ numRtpPacketsNotReceived, averageRelativeJitter, maxRelativeJitter,
+ averageRoundTripTime, codecType, false, false, false);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param callQualityLevel the call quality level (see #CallQualityLevel)
+ * @param callDuration the call duration in milliseconds
+ * @param numRtpPacketsTransmitted RTP packets sent to network
+ * @param numRtpPacketsReceived RTP packets received from network
+ * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+ * transmitted
+ * @param numRtpPacketsNotReceived RTP packets which were lost in network and never received
+ * @param averageRelativeJitter average relative jitter in milliseconds
+ * @param maxRelativeJitter maximum relative jitter in milliseconds
+ * @param averageRoundTripTime average round trip delay in milliseconds
+ * @param codecType the codec type
+ * @param rtpInactivityDetected True if no incoming RTP is received for a continuous duration of
+ * 4 seconds
+ * @param rxSilenceDetected True if only silence RTP packets are received for 20 seconds
+ * immediately after call is connected
+ * @param txSilenceDetected True if only silence RTP packets are sent for 20 seconds immediately
+ * after call is connected
+ */
+ public CallQuality(
+ @CallQualityLevel int downlinkCallQualityLevel,
+ @CallQualityLevel int uplinkCallQualityLevel,
+ int callDuration,
+ int numRtpPacketsTransmitted,
+ int numRtpPacketsReceived,
+ int numRtpPacketsTransmittedLost,
+ int numRtpPacketsNotReceived,
+ int averageRelativeJitter,
+ int maxRelativeJitter,
+ int averageRoundTripTime,
+ int codecType,
+ boolean rtpInactivityDetected,
+ boolean rxSilenceDetected,
+ boolean txSilenceDetected) {
this.mDownlinkCallQualityLevel = downlinkCallQualityLevel;
this.mUplinkCallQualityLevel = uplinkCallQualityLevel;
this.mCallDuration = callDuration;
@@ -138,6 +186,9 @@
this.mMaxRelativeJitter = maxRelativeJitter;
this.mAverageRoundTripTime = averageRoundTripTime;
this.mCodecType = codecType;
+ this.mRtpInactivityDetected = rtpInactivityDetected;
+ this.mRxSilenceDetected = rxSilenceDetected;
+ this.mTxSilenceDetected = txSilenceDetected;
}
// getters
@@ -226,6 +277,29 @@
}
/**
+ * Returns true if no rtp packets are received continuously for the last 4 seconds
+ */
+ public boolean isRtpInactivityDetected() {
+ return mRtpInactivityDetected;
+ }
+
+ /**
+ * Returns true if only silence rtp packets are received for a duration of 20 seconds starting
+ * at call setup
+ */
+ public boolean isIncomingSilenceDetected() {
+ return mRxSilenceDetected;
+ }
+
+ /**
+ * Returns true if only silence rtp packets are sent for a duration of 20 seconds starting at
+ * call setup
+ */
+ public boolean isOutgoingSilenceDetected() {
+ return mTxSilenceDetected;
+ }
+
+ /**
* Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in
* {@link ImsStreamMediaProfile}.
*
@@ -270,6 +344,9 @@
+ " maxRelativeJitter=" + mMaxRelativeJitter
+ " averageRoundTripTime=" + mAverageRoundTripTime
+ " codecType=" + mCodecType
+ + " rtpInactivityDetected=" + mRtpInactivityDetected
+ + " txSilenceDetected=" + mRxSilenceDetected
+ + " rxSilenceDetected=" + mTxSilenceDetected
+ "}";
}
@@ -286,7 +363,10 @@
mAverageRelativeJitter,
mMaxRelativeJitter,
mAverageRoundTripTime,
- mCodecType);
+ mCodecType,
+ mRtpInactivityDetected,
+ mRxSilenceDetected,
+ mTxSilenceDetected);
}
@Override
@@ -311,7 +391,10 @@
&& mAverageRelativeJitter == s.mAverageRelativeJitter
&& mMaxRelativeJitter == s.mMaxRelativeJitter
&& mAverageRoundTripTime == s.mAverageRoundTripTime
- && mCodecType == s.mCodecType);
+ && mCodecType == s.mCodecType
+ && mRtpInactivityDetected == s.mRtpInactivityDetected
+ && mRxSilenceDetected == s.mRxSilenceDetected
+ && mTxSilenceDetected == s.mTxSilenceDetected);
}
/**
@@ -336,6 +419,9 @@
dest.writeInt(mMaxRelativeJitter);
dest.writeInt(mAverageRoundTripTime);
dest.writeInt(mCodecType);
+ dest.writeBoolean(mRtpInactivityDetected);
+ dest.writeBoolean(mRxSilenceDetected);
+ dest.writeBoolean(mTxSilenceDetected);
}
public static final @android.annotation.NonNull Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6a62237..5a7c3b3 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -16,8 +16,6 @@
package android.telephony;
-import com.android.telephony.Rlog;
-
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -36,6 +34,7 @@
import android.telephony.ims.ImsReasonInfo;
import com.android.internal.telephony.ICarrierConfigLoader;
+import com.android.telephony.Rlog;
/**
* Provides access to telephony configuration values that are carrier-specific.
@@ -300,7 +299,6 @@
/**
* A string array containing numbers that shouldn't be included in the call log.
- * @hide
*/
public static final String KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY =
"unloggable_numbers_string_array";
@@ -313,12 +311,11 @@
KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
/**
- * Do only allow auto selection in Advanced Network Settings when in home network.
+ * Only allow auto selection in Advanced Network Settings when in home network.
* Manual selection is allowed when in roaming network.
- * @hide
*/
- public static final String
- KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network";
+ public static final String KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL =
+ "only_auto_select_in_home_network";
/**
* Control whether users receive a simplified network settings UI and improved network
@@ -582,9 +579,6 @@
* registration state to change. That is, turning on or off mobile data will not cause VT to be
* enabled or disabled.
* When {@code false}, disabling mobile data will cause VT to be de-registered.
- * <p>
- * See also {@link #KEY_VILTE_DATA_IS_METERED_BOOL}.
- * @hide
*/
public static final String KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS =
"ignore_data_enabled_changed_for_video_calls";
@@ -648,7 +642,6 @@
/**
* Default WFC_IMS_enabled: true VoWiFi by default is on
* false VoWiFi by default is off
- * @hide
*/
public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL =
"carrier_default_wfc_ims_enabled_bool";
@@ -672,6 +665,12 @@
"carrier_promote_wfc_on_call_fail_bool";
/**
+ * Flag specifying whether provisioning is required for RCS.
+ */
+ public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL =
+ "carrier_rcs_provisioning_required_bool";
+
+ /**
* Flag specifying whether provisioning is required for VoLTE, Video Telephony, and WiFi
* Calling.
*/
@@ -714,9 +713,7 @@
*
* As of now, Verizon is the only carrier enforcing this dependency in their
* WFC awareness and activation requirements.
- *
- * @hide
- * */
+ */
public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL
= "carrier_volte_override_wfc_provisioning_bool";
@@ -848,9 +845,12 @@
"carrier_force_disable_etws_cmas_test_bool";
/**
- * The default flag specifying whether "Turn on Notifications" option will be always shown in
- * Settings->More->Emergency broadcasts menu regardless developer options is turned on or not.
+ * The default flag specifying whether "Allow alerts" option will be always shown in
+ * emergency alerts settings regardless developer options is turned on or not.
+ *
+ * @deprecated The allow alerts option is always shown now. No longer need a config for that.
*/
+ @Deprecated
public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL =
"always_show_emergency_alert_onoff_bool";
@@ -1074,7 +1074,6 @@
*
* When {@code false}, the old behavior is used, where the toggle in accessibility settings is
* used to set the IMS stack's RTT enabled state.
- * @hide
*/
public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
"ignore_rtt_mode_setting_bool";
@@ -1115,7 +1114,6 @@
* Determines whether the IMS conference merge process supports and returns its participants
* data. When {@code true}, on merge complete, conference call would have a list of its
* participants returned in XML format, {@code false otherwise}.
- * @hide
*/
public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL =
"support_ims_conference_event_package_bool";
@@ -1188,20 +1186,18 @@
public static final String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
/**
- * Determine whether user can switch Wi-Fi preferred or Cellular preferred in calling preference.
+ * Determine whether user can switch Wi-Fi preferred or Cellular preferred
+ * in calling preference.
* Some operators support Wi-Fi Calling only, not VoLTE.
* They don't need "Cellular preferred" option.
- * In this case, set uneditalbe attribute for preferred preference.
- * @hide
+ * In this case, set uneditable attribute for preferred preference.
*/
public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
- /**
- * Flag to indicate if Wi-Fi needs to be disabled in ECBM
- * @hide
- **/
- public static final String
- KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm";
+ /**
+ * Flag to indicate if Wi-Fi needs to be disabled in ECBM.
+ */
+ public static final String KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm";
/**
* List operator-specific error codes and indices of corresponding error strings in
@@ -1265,9 +1261,8 @@
public static final String KEY_WFC_SPN_USE_ROOT_LOCALE = "wfc_spn_use_root_locale";
/**
- * The Component Name of the activity that can setup the emergency addrees for WiFi Calling
+ * The Component Name of the activity that can setup the emergency address for WiFi Calling
* as per carrier requirement.
- * @hide
*/
public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING =
"wfc_emergency_address_carrier_app_string";
@@ -1431,22 +1426,19 @@
public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
/**
- * APN types that user is not allowed to modify
- * @hide
+ * APN types that user is not allowed to modify.
*/
public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY =
"read_only_apn_types_string_array";
/**
- * APN fields that user is not allowed to modify
- * @hide
+ * APN fields that user is not allowed to modify.
*/
public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY =
"read_only_apn_fields_string_array";
/**
* Default value of APN types field if not specified by user when adding/modifying an APN.
- * @hide
*/
public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY =
"apn_settings_default_apn_types_string_array";
@@ -1477,29 +1469,25 @@
"hide_digits_helper_text_on_stk_input_screen_bool";
/**
- * Boolean indicating if show data RAT icon on status bar even when data is disabled
- * @hide
+ * Boolean indicating if show data RAT icon on status bar even when data is disabled.
*/
public static final String KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL =
"always_show_data_rat_icon_bool";
/**
- * Boolean indicating if default data account should show LTE or 4G icon
- * @hide
+ * Boolean indicating if default data account should show LTE or 4G icon.
*/
public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL =
"show_4g_for_lte_data_icon_bool";
/**
* Boolean indicating if default data account should show 4G icon when in 3G.
- * @hide
*/
public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL =
"show_4g_for_3g_data_icon_bool";
/**
- * Boolean indicating if lte+ icon should be shown if available
- * @hide
+ * Boolean indicating if LTE+ icon should be shown if available.
*/
public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL =
"hide_lte_plus_data_icon_bool";
@@ -1514,10 +1502,8 @@
"operator_name_filter_pattern_string";
/**
- * The string is used to compare with operator name. If it matches the pattern then show
- * specific data icon.
- *
- * @hide
+ * The string is used to compare with operator name.
+ * If it matches the pattern then show specific data icon.
*/
public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING =
"show_carrier_data_icon_pattern_string";
@@ -1530,33 +1516,28 @@
"show_precise_failed_cause_bool";
/**
- * Boolean to decide whether lte is enabled.
- * @hide
+ * Boolean to decide whether LTE is enabled.
*/
public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool";
/**
* Boolean to decide whether TD-SCDMA is supported.
- * @hide
*/
public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool";
/**
* A list of mcc/mnc that support TD-SCDMA for device when connect to the roaming network.
- * @hide
*/
public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY =
"support_tdscdma_roaming_networks_string_array";
/**
* Boolean to decide whether world mode is enabled.
- * @hide
*/
public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
/**
* Flatten {@link android.content.ComponentName} of the carrier's settings activity.
- * @hide
*/
public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING =
"carrier_settings_activity_component_name_string";
@@ -1610,25 +1591,23 @@
/**
* Defines carrier-specific actions which act upon
* com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED, used for customization of the
- * default carrier app
+ * default carrier app.
* Format: "CARRIER_ACTION_IDX, ..."
* Where {@code CARRIER_ACTION_IDX} is an integer defined in
- * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils}
+ * com.android.carrierdefaultapp.CarrierActionUtils
* Example:
- * {@link com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS
- * disable_metered_apns}
- * @hide
+ * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS
+ * disables metered APNs
*/
- @UnsupportedAppUsage
+ @SuppressLint("IntentName")
public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY =
"carrier_default_actions_on_redirection_string_array";
/**
- * Defines carrier-specific actions which act upon
- * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+ * Defines carrier-specific actions which act upon CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
* and configured signal args:
- * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_APN_TYPE_KEY apnType},
- * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_ERROR_CODE_KEY errorCode}
+ * android.telephony.TelephonyManager#EXTRA_APN_TYPE,
+ * android.telephony.TelephonyManager#EXTRA_ERROR_CODE
* used for customization of the default carrier app
* Format:
* {
@@ -1636,42 +1615,41 @@
* "APN_1, ERROR_CODE_2 : CARRIER_ACTION_IDX_1 "
* }
* Where {@code APN_1} is a string defined in
- * {@link com.android.internal.telephony.PhoneConstants PhoneConstants}
+ * com.android.internal.telephony.PhoneConstants
* Example: "default"
*
- * {@code ERROR_CODE_1} is an integer defined in
- * {@link DataFailCause DcFailure}
+ * {@code ERROR_CODE_1} is an integer defined in android.telephony.DataFailCause
* Example:
- * {@link DataFailCause#MISSING_UNKNOWN_APN}
+ * android.telephony.DataFailCause#MISSING_UNKNOWN_APN
*
* {@code CARRIER_ACTION_IDX_1} is an integer defined in
- * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils}
+ * com.android.carrierdefaultapp.CarrierActionUtils
* Example:
- * {@link com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS}
- * @hide
+ * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS
+ * disables metered APNs
*/
+ @SuppressLint("IntentName")
public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY =
"carrier_default_actions_on_dcfailure_string_array";
/**
- * Defines carrier-specific actions which act upon
- * com.android.internal.telephony.CARRIER_SIGNAL_RESET, used for customization of the
- * default carrier app
+ * Defines carrier-specific actions which act upon CARRIER_SIGNAL_RESET,
+ * used for customization of the default carrier app.
* Format: "CARRIER_ACTION_IDX, ..."
* Where {@code CARRIER_ACTION_IDX} is an integer defined in
- * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils}
+ * com.android.carrierdefaultapp.CarrierActionUtils
* Example:
- * {@link com.android.carrierdefaultapp.CarrierActionUtils
- * #CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS clear all notifications on reset}
- * @hide
+ * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS
+ * clears all notifications on reset
*/
+ @SuppressLint("IntentName")
public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET =
"carrier_default_actions_on_reset_string_array";
/**
* Defines carrier-specific actions which act upon
* com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE,
- * used for customization of the default carrier app
+ * used for customization of the default carrier app.
* Format:
* {
* "true : CARRIER_ACTION_IDX_1",
@@ -1679,17 +1657,17 @@
* }
* Where {@code true} is a boolean indicates default network available/unavailable
* Where {@code CARRIER_ACTION_IDX} is an integer defined in
- * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils}
+ * com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils
* Example:
- * {@link com.android.carrierdefaultapp.CarrierActionUtils
- * #CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER enable the app as the default URL handler}
- * @hide
+ * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER
+ * enables the app as the default URL handler
*/
+ @SuppressLint("IntentName")
public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE =
"carrier_default_actions_on_default_network_available_string_array";
+
/**
- * Defines a list of acceptable redirection url for default carrier app
- * @hides
+ * Defines a list of acceptable redirection url for default carrier app.
*/
public static final String KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY =
"carrier_default_redirection_url_string_array";
@@ -1817,10 +1795,10 @@
/**
* Determines whether to enable enhanced call blocking feature on the device.
- * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED
- * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE
- * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE
- * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN
+ * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED
+ * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE
+ * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE
+ * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN
*
* <p>
* 1. For Single SIM(SS) device, it can be customized in both carrier_config_mccmnc.xml
@@ -1830,7 +1808,6 @@
* function is used regardless of SIM.
* <p>
* If {@code true} enable enhanced call blocking feature on the device, {@code false} otherwise.
- * @hide
*/
public static final String KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL =
"support_enhanced_call_blocking_bool";
@@ -1941,7 +1918,6 @@
/**
* Flag indicating whether the carrier supports call deflection for an incoming IMS call.
- * @hide
*/
public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL =
"carrier_allow_deflect_ims_call_bool";
@@ -2006,8 +1982,6 @@
/**
* Whether system apps are allowed to use fallback if carrier video call is not available.
* Defaults to {@code true}.
- *
- * @hide
*/
public static final String KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL =
"allow_video_calling_fallback_bool";
@@ -2045,9 +2019,8 @@
"enhanced_4g_lte_title_variant_bool";
/**
- * The index indicates the carrier specified title string of Enahnce 4G LTE Mode settings.
+ * The index indicates the carrier specified title string of Enhanced 4G LTE Mode settings.
* Default value is 0, which indicates the default title string.
- * @hide
*/
public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT =
"enhanced_4g_lte_title_variant_int";
@@ -2091,15 +2064,13 @@
* {@link #KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL} is false. If
* {@link #KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL} is true, this
* configuration is ignored and roaming preference cannot be changed.
- * @hide
*/
public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL =
"editable_wfc_roaming_mode_bool";
/**
- * Flag specifying wether to show blocking pay phone option in blocked numbers screen. Only show
- * the option if payphone call presentation represents in the carrier's region.
- * @hide
+ * Flag specifying whether to show blocking pay phone option in blocked numbers screen.
+ * Only show the option if payphone call presentation is present in the carrier's region.
*/
public static final java.lang.String KEY_SHOW_BLOCKING_PAY_PHONE_OPTION_BOOL =
"show_blocking_pay_phone_option_bool";
@@ -2109,7 +2080,6 @@
* {@code false} - roaming preference can be selected separately from the home preference.
* {@code true} - roaming preference is the same as home preference and
* {@link #KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT} is used as the default value.
- * @hide
*/
public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL =
"use_wfc_home_network_mode_in_roaming_network_bool";
@@ -2137,7 +2107,6 @@
* while the device is registered over WFC. Default value is -1, which indicates
* that this notification is not pertinent for a particular carrier. We've added a delay
* to prevent false positives.
- * @hide
*/
public static final String KEY_EMERGENCY_NOTIFICATION_DELAY_INT =
"emergency_notification_delay_int";
@@ -2188,7 +2157,6 @@
/**
* Flag specifying whether to show an alert dialog for video call charges.
* By default this value is {@code false}.
- * @hide
*/
public static final String KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL =
"show_video_call_charges_alert_dialog_bool";
@@ -2475,10 +2443,9 @@
/**
* Identifies if the key is available for WLAN or EPDG or both. The value is a bitmask.
* 0 indicates that neither EPDG or WLAN is enabled.
- * 1 indicates that key type {@link TelephonyManager#KEY_TYPE_EPDG} is enabled.
- * 2 indicates that key type {@link TelephonyManager#KEY_TYPE_WLAN} is enabled.
+ * 1 indicates that key type TelephonyManager#KEY_TYPE_EPDG is enabled.
+ * 2 indicates that key type TelephonyManager#KEY_TYPE_WLAN is enabled.
* 3 indicates that both are enabled.
- * @hide
*/
public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int";
@@ -2496,7 +2463,6 @@
/**
* Flag specifying whether IMS registration state menu is shown in Status Info setting,
* default to false.
- * @hide
*/
public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL =
"show_ims_registration_status_bool";
@@ -2552,7 +2518,6 @@
/**
* The flag to disable the popup dialog which warns the user of data charges.
- * @hide
*/
public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL =
"disable_charge_indication_bool";
@@ -2617,15 +2582,13 @@
/**
* Determines whether any carrier has been identified and its specific config has been applied,
* default to false.
- * @hide
*/
public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool";
/**
* Determines whether we should show a warning asking the user to check with their carrier
- * on pricing when the user enabled data roaming.
+ * on pricing when the user enabled data roaming,
* default to false.
- * @hide
*/
public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL =
"check_pricing_with_carrier_data_roaming_bool";
@@ -2777,10 +2740,10 @@
* Specifies a carrier-defined {@link android.telecom.CallRedirectionService} which Telecom
* will bind to for outgoing calls. An empty string indicates that no carrier-defined
* {@link android.telecom.CallRedirectionService} is specified.
- * @hide
*/
public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING =
"call_redirection_service_component_name_string";
+
/**
* Support for the original string display of CDMA MO call.
* By default, it is disabled.
@@ -2893,8 +2856,8 @@
"call_waiting_service_class_int";
/**
- * This configuration allow the system UI to display different 5G icon for different 5G
- * scenario.
+ * This configuration allows the system UI to display different 5G icons for different 5G
+ * scenarios.
*
* There are five 5G scenarios:
* 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
@@ -2911,24 +2874,22 @@
* 5G cell as a secondary cell) but the use of 5G is restricted.
*
* The configured string contains multiple key-value pairs separated by comma. For each pair,
- * the key and value is separated by a colon. The key is corresponded to a 5G status above and
+ * the key and value are separated by a colon. The key corresponds to a 5G status above and
* the value is the icon name. Use "None" as the icon name if no icon should be shown in a
* specific 5G scenario. If the scenario is "None", config can skip this key and value.
*
* Icon name options: "5G_Plus", "5G".
*
* Here is an example:
- * UE want to display 5G_Plus icon for scenario#1, and 5G icon for scenario#2; otherwise no
+ * UE wants to display 5G_Plus icon for scenario#1, and 5G icon for scenario#2; otherwise not
* define.
* The configuration is: "connected_mmwave:5G_Plus,connected:5G"
- *
- * @hide
*/
public static final String KEY_5G_ICON_CONFIGURATION_STRING =
"5g_icon_configuration_string";
/**
- * Timeout in second for displaying 5G icon, default value is 0 which means the timer is
+ * Timeout in seconds for displaying 5G icon, default value is 0 which means the timer is
* disabled.
*
* System UI will show the 5G icon and start a timer with the timeout from this config when the
@@ -2937,8 +2898,6 @@
*
* If 5G is reacquired during this timer, the timer is canceled and restarted when 5G is next
* lost. Allows us to momentarily lose 5G without blinking the icon.
- *
- * @hide
*/
public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT =
"5g_icon_display_grace_period_sec_int";
@@ -3103,8 +3062,6 @@
* signal bar of primary network. By default it will be false, meaning whenever data
* is going over opportunistic network, signal bar will reflect signal strength and rat
* icon of that network.
- *
- * @hide
*/
public static final String KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN =
"always_show_primary_signal_bar_in_opportunistic_network_boolean";
@@ -3326,8 +3283,6 @@
/**
* Determines whether wifi calling location privacy policy is shown.
- *
- * @hide
*/
public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL =
"show_wfc_location_privacy_policy_bool";
@@ -3438,8 +3393,8 @@
"support_wps_over_ims_bool";
/**
- * Holds the list of carrier certificate hashes. Note that each carrier has its own certificates
- * @hide
+ * Holds the list of carrier certificate hashes.
+ * Note that each carrier has its own certificates.
*/
public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY =
"carrier_certificate_string_array";
@@ -3497,6 +3452,7 @@
sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT, 2);
sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT, 2);
sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL, false);
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 31434c1..5e2e554 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -20,6 +20,9 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.LinkProperties;
import android.os.Build;
@@ -31,8 +34,6 @@
import android.telephony.Annotation.NetworkType;
import android.telephony.data.ApnSetting;
-import dalvik.system.VMRuntime;
-
import java.util.Objects;
@@ -134,6 +135,13 @@
}
/**
+ * To check the SDK version for {@link PreciseDataConnectionState#getDataConnectionState}.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long GET_DATA_CONNECTION_STATE_CODE_CHANGE = 147600208L;
+
+ /**
* Returns the state of data connection that supported the apn types returned by
* {@link #getDataConnectionApnTypeBitMask()}
*
@@ -144,7 +152,7 @@
@SystemApi
public @DataState int getDataConnectionState() {
if (mState == TelephonyManager.DATA_DISCONNECTING
- && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
+ && !Compatibility.isChangeEnabled(GET_DATA_CONNECTION_STATE_CODE_CHANGE)) {
return TelephonyManager.DATA_CONNECTED;
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index eb328a7..8ed4ee5 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -281,6 +281,42 @@
*/
public static final int SMS_MESSAGE_PERIOD_NOT_SPECIFIED = -1;
+ /** @hide */
+ @IntDef(prefix = { "PREMIUM_SMS_CONSENT" }, value = {
+ SmsManager.PREMIUM_SMS_CONSENT_UNKNOWN,
+ SmsManager.PREMIUM_SMS_CONSENT_ASK_USER,
+ SmsManager.PREMIUM_SMS_CONSENT_NEVER_ALLOW,
+ SmsManager.PREMIUM_SMS_CONSENT_ALWAYS_ALLOW
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PremiumSmsConsent {}
+
+ /** Premium SMS Consent for the package is unknown. This indicates that the user
+ * has not set a permission for this package, because this package has never tried
+ * to send a premium SMS.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREMIUM_SMS_CONSENT_UNKNOWN = 0;
+
+ /** Default premium SMS Consent (ask user for each premium SMS sent).
+ * @hide
+ */
+ @SystemApi
+ public static final int PREMIUM_SMS_CONSENT_ASK_USER = 1;
+
+ /** Premium SMS Consent when the owner has denied the app from sending premium SMS.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREMIUM_SMS_CONSENT_NEVER_ALLOW = 2;
+
+ /** Premium SMS Consent when the owner has allowed the app to send premium SMS.
+ * @hide
+ */
+ @SystemApi
+ public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3;
+
// result of asking the user for a subscription to perform an operation.
private interface SubscriptionResolverResult {
void onSuccess(int subId);
@@ -2448,14 +2484,18 @@
* @param sentIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is successfully sent, or failed
* @throws IllegalArgumentException if contentUri is empty
+ * @deprecated use {@link MmsManager#sendMultimediaMessage} instead.
*/
public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
Bundle configOverrides, PendingIntent sentIntent) {
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
}
- MmsManager.getInstance().sendMultimediaMessage(getSubscriptionId(), contentUri,
- locationUrl, configOverrides, sentIntent);
+ MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
+ if (m != null) {
+ m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides,
+ sentIntent);
+ }
}
/**
@@ -2479,6 +2519,7 @@
* @param downloadedIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is downloaded, or the download is failed
* @throws IllegalArgumentException if locationUrl or contentUri is empty
+ * @deprecated use {@link MmsManager#downloadMultimediaMessage} instead.
*/
public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
Bundle configOverrides, PendingIntent downloadedIntent) {
@@ -2488,8 +2529,11 @@
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
}
- MmsManager.getInstance().downloadMultimediaMessage(getSubscriptionId(), locationUrl,
- contentUri, configOverrides, downloadedIntent);
+ MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
+ if (m != null) {
+ m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri,
+ configOverrides, downloadedIntent);
+ }
}
// MMS send/download failure result codes
@@ -2531,9 +2575,9 @@
* </p>
*
* @return the bundle key/values pairs that contains MMS configuration values
+ * or an empty Bundle if they cannot be found.
*/
- @Nullable
- public Bundle getCarrierConfigValues() {
+ @NonNull public Bundle getCarrierConfigValues() {
try {
ISms iSms = getISmsService();
if (iSms != null) {
@@ -2542,7 +2586,7 @@
} catch (RemoteException ex) {
// ignore it
}
- return null;
+ return new Bundle();
}
/**
@@ -2861,4 +2905,53 @@
}
return false;
}
+
+ /**
+ * Gets the premium SMS permission for the specified package. If the package has never
+ * been seen before, the default {@link SmsManager#PREMIUM_SMS_PERMISSION_ASK_USER}
+ * will be returned.
+ * @param packageName the name of the package to query permission
+ * @return one of {@link SmsManager#PREMIUM_SMS_CONSENT_UNKNOWN},
+ * {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
+ * {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
+ * {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @PremiumSmsConsent int getPremiumSmsConsent(@NonNull String packageName) {
+ int permission = 0;
+ try {
+ ISms iSms = getISmsService();
+ if (iSms != null) {
+ permission = iSms.getPremiumSmsPermission(packageName);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getPremiumSmsPermission() RemoteException", e);
+ }
+ return permission;
+ }
+
+ /**
+ * Sets the premium SMS permission for the specified package and save the value asynchronously
+ * to persistent storage.
+ * @param packageName the name of the package to set permission
+ * @param permission one of {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
+ * {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
+ * {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setPremiumSmsConsent(
+ @NonNull String packageName, @PremiumSmsConsent int permission) {
+ try {
+ ISms iSms = getISmsService();
+ if (iSms != null) {
+ iSms.setPremiumSmsPermission(packageName, permission);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "setPremiumSmsPermission() RemoteException", e);
+ }
+ }
}
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index c217b8b..40a7619 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -20,8 +20,13 @@
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.StringDef;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.os.Binder;
@@ -31,8 +36,10 @@
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.Sms7BitEncodingTranslator;
import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+import com.android.internal.telephony.cdma.sms.UserData;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -56,6 +63,16 @@
UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
}
+ /** @hide */
+ @IntDef(prefix = { "ENCODING_" }, value = {
+ ENCODING_UNKNOWN,
+ ENCODING_7BIT,
+ ENCODING_8BIT,
+ ENCODING_16BIT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EncodingSize {}
+
/** User data text encoding code unit size */
public static final int ENCODING_UNKNOWN = 0;
public static final int ENCODING_7BIT = 1;
@@ -633,6 +650,83 @@
}
/**
+ * Get an SMS-SUBMIT PDU's encoded message.
+ * This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages.
+ *
+ * @param isTypeGsm true when message's type is GSM, false when type is CDMA
+ * @param destinationAddress the address of the destination for the message
+ * @param message message content
+ * @param encoding User data text encoding code unit size
+ * @param languageTable GSM national language table to use, specified by 3GPP
+ * 23.040 9.2.3.24.16
+ * @param languageShiftTable GSM national language shift table to use, specified by 3GPP
+ * 23.040 9.2.3.24.15
+ * @param refNumber parameter to create SmsHeader
+ * @param seqNumber parameter to create SmsHeader
+ * @param msgCount parameter to create SmsHeader
+ * @return a byte[] containing the encoded message
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @SystemApi
+ @NonNull
+ public static byte[] getSubmitPduEncodedMessage(boolean isTypeGsm,
+ @NonNull String destinationAddress,
+ @NonNull String message,
+ @EncodingSize int encoding, int languageTable,
+ int languageShiftTable, int refNumber,
+ int seqNumber, int msgCount) {
+ byte[] data;
+ SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
+ concatRef.refNumber = refNumber;
+ concatRef.seqNumber = seqNumber; // 1-based sequence
+ concatRef.msgCount = msgCount;
+ // We currently set this to true since our messaging app will never
+ // send more than 255 parts (it converts the message to MMS well before that).
+ // However, we should support 3rd party messaging apps that might need 16-bit
+ // references
+ // Note: It's not sufficient to just flip this bit to true; it will have
+ // ripple effects (several calculations assume 8-bit ref).
+ concatRef.isEightBits = true;
+ SmsHeader smsHeader = new SmsHeader();
+ smsHeader.concatRef = concatRef;
+
+ /* Depending on the type, call either GSM or CDMA getSubmitPdu(). The encoding
+ * will be determined(again) by getSubmitPdu().
+ * All packets need to be encoded using the same encoding, as the bMessage
+ * only have one filed to describe the encoding for all messages in a concatenated
+ * SMS... */
+ if (encoding == ENCODING_7BIT) {
+ smsHeader.languageTable = languageTable;
+ smsHeader.languageShiftTable = languageShiftTable;
+ }
+
+ if (isTypeGsm) {
+ data = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(null,
+ destinationAddress, message, false,
+ SmsHeader.toByteArray(smsHeader), encoding, languageTable,
+ languageShiftTable).encodedMessage;
+ } else { // SMS_TYPE_CDMA
+ UserData uData = new UserData();
+ uData.payloadStr = message;
+ uData.userDataHeader = smsHeader;
+ if (encoding == ENCODING_7BIT) {
+ uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
+ } else { // assume UTF-16
+ uData.msgEncoding = UserData.ENCODING_UNICODE_16;
+ }
+ uData.msgEncodingSet = true;
+ data = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
+ destinationAddress, uData, false).encodedMessage;
+ }
+ if (data == null) {
+ return new byte[0];
+ }
+ return data;
+ }
+
+ /**
* Returns the address of the SMS service center that relayed this message
* or null if there is none.
*/
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 4510fed..36f541f 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -16,8 +16,6 @@
package android.telephony;
-import com.android.telephony.Rlog;
-
import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
@@ -67,6 +65,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.util.HandlerExecutor;
import com.android.internal.util.Preconditions;
+import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -2240,10 +2239,9 @@
@UnsupportedAppUsage
public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) {
if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
- intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, subId);
intent.putExtra(EXTRA_SLOT_INDEX, phoneId);
intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
+ putSubscriptionIdExtra(intent, subId);
}
/**
@@ -3489,4 +3487,19 @@
}
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
+
+ /**
+ * Helper method that puts a subscription id on an intent with the constants:
+ * PhoneConstant.SUBSCRIPTION_KEY and SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX.
+ * Both constants are used to support backwards compatibility. Once we know we got all places,
+ * we can remove PhoneConstants.SUBSCRIPTION_KEY.
+ * @param intent Intent to put sub id on.
+ * @param subId SubscriptionId to put on intent.
+ *
+ * @hide
+ */
+ public static void putSubscriptionIdExtra(Intent intent, int subId) {
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
+ intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f321427..7ee68ea 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -109,8 +109,6 @@
import com.android.internal.telephony.SmsApplication;
import com.android.telephony.Rlog;
-import dalvik.system.VMRuntime;
-
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -1106,6 +1104,16 @@
*/
public static final int CDMA_ROAMING_MODE_ANY = 2;
+ /** @hide */
+ @IntDef(prefix = { "CDMA_ROAMING_MODE_" }, value = {
+ CDMA_ROAMING_MODE_RADIO_DEFAULT,
+ CDMA_ROAMING_MODE_HOME,
+ CDMA_ROAMING_MODE_AFFILIATED,
+ CDMA_ROAMING_MODE_ANY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CdmaRoamingMode{}
+
/**
* An unknown carrier id. It could either be subscription unavailable or the subscription
* carrier cannot be recognized. Unrecognized carriers here means
@@ -1444,27 +1452,10 @@
"android.telephony.extra.SIM_COMBINATION_NAMES";
/**
- * Broadcast Action: The time was set by the carrier (typically by the NITZ string).
- * This is a sticky broadcast.
- * The intent will have the following extra values:</p>
- * <ul>
- * <li><em>time</em> - The time as a long in UTC milliseconds.</li>
- * </ul>
- *
- * <p class="note">
- * Requires the READ_PHONE_STATE permission.
- *
- * <p class="note">This is a protected intent that can only be sent by the system.
- *
- * @hide
- */
- @SystemApi
- public static final String ACTION_NETWORK_SET_TIME = "android.telephony.action.NETWORK_SET_TIME";
-
- /**
* <p>Broadcast Action: The emergency callback mode is changed.
* <ul>
- * <li><em>phoneinECMState</em> - A boolean value,true=phone in ECM, false=ECM off</li>
+ * <li><em>EXTRA_PHONE_IN_ECM_STATE</em> - A boolean value,true=phone in ECM,
+ * false=ECM off</li>
* </ul>
* <p class="note">
* You can <em>not</em> receive this through components declared
@@ -1474,18 +1465,218 @@
*
* <p class="note">This is a protected intent that can only be sent by the system.
*
+ * @see #EXTRA_PHONE_IN_ECM_STATE
+ *
* @hide
*/
@SystemApi
@SuppressLint("ActionValue")
- public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED
- = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
+ public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED =
+ "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
+
+
+ /**
+ * Extra included in {@link #ACTION_EMERGENCY_CALLBACK_MODE_CHANGED}.
+ * Indicates whether the phone is in an emergency phone state.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PHONE_IN_ECM_STATE =
+ "android.telephony.extra.PHONE_IN_ECM_STATE";
+
+ /**
+ * <p>Broadcast Action: when data connections get redirected with validation failure.
+ * intended for sim/account status checks and only sent to the specified carrier app
+ * The intent will have the following extra values:</p>
+ * <ul>
+ * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd>
+ * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd>
+ * <li>{@link #EXTRA_REDIRECTION_URL}</li><dd>redirection url string</dd>
+ * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
+ * </ul>
+ * <p class="note">This is a protected intent that can only be sent by the system.</p>
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String ACTION_CARRIER_SIGNAL_REDIRECTED =
+ "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED";
+
+ /**
+ * <p>Broadcast Action: when data connections setup fails.
+ * intended for sim/account status checks and only sent to the specified carrier app
+ * The intent will have the following extra values:</p>
+ * <ul>
+ * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd>
+ * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd>
+ * <li>{@link #EXTRA_ERROR_CODE}</li><dd>A integer with dataFailCause.</dd>
+ * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
+ * </ul>
+ * <p class="note">This is a protected intent that can only be sent by the system. </p>
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED =
+ "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED";
+
+ /**
+ * <p>Broadcast Action: when pco value is available.
+ * intended for sim/account status checks and only sent to the specified carrier app
+ * The intent will have the following extra values:</p>
+ * <ul>
+ * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd>
+ * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd>
+ * <li>{@link #EXTRA_APN_PROTOCOL}</li><dd>A string with the protocol of the apn connection
+ * (IP,IPV6, IPV4V6)</dd>
+ * <li>{@link #EXTRA_APN_PROTOCOL_INT}</li><dd>A integer with the protocol of the apn
+ * connection (IP,IPV6, IPV4V6)</dd>
+ * <li>{@link #EXTRA_PCO_ID}</li><dd>An integer indicating the pco id for the data.</dd>
+ * <li>{@link #EXTRA_PCO_VALUE}</li><dd>A byte array of pco data read from modem.</dd>
+ * <li>subId</li><dd>Sub Id which associated the data connection.</dd>
+ * </ul>
+ * <p class="note">This is a protected intent that can only be sent by the system. </p>
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE =
+ "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE";
+
+ /**
+ * <p>Broadcast Action: when system default network available/unavailable with
+ * carrier-disabled mobile data. Intended for carrier apps to set/reset carrier actions when
+ * other network becomes system default network, Wi-Fi for example.
+ * The intent will have the following extra values:</p>
+ * <ul>
+ * <li>{@link #EXTRA_DEFAULT_NETWORK_AVAILABLE}</li>
+ * <dd>A boolean indicates default network available.</dd>
+ * <li>subId</li><dd>Sub Id which associated the default data.</dd>
+ * </ul>
+ * <p class="note">This is a protected intent that can only be sent by the system. </p>
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE =
+ "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
+
+ /**
+ * <p>Broadcast Action: when framework reset all carrier actions on sim load or absent.
+ * intended for carrier apps clean up (clear UI e.g.) and only sent to the specified carrier app
+ * The intent will have the following extra values:</p>
+ * <ul>
+ * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
+ * </ul>
+ * <p class="note">This is a protected intent that can only be sent by the system.</p>
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String ACTION_CARRIER_SIGNAL_RESET =
+ "com.android.internal.telephony.CARRIER_SIGNAL_RESET";
+
+ // CARRIER_SIGNAL_ACTION extra keys
+ /**
+ * An string extra of redirected url upon {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}.
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
+
+ /**
+ * An integer extra of error code upon {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}.
+ * Check {@link DataFailCause} for all possible values.
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String EXTRA_ERROR_CODE = "errorCode";
+
+ /**
+ * An string extra of corresponding apn type upon
+ * {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED},
+ * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED} and
+ * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+ * @deprecated This is kept for backward compatibility reason. Use {@link #EXTRA_APN_TYPE_INT}
+ * instead.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Deprecated
+ @SuppressLint("ActionValue")
+ public static final String EXTRA_APN_TYPE = "apnType";
+
+ /**
+ * An string integer of corresponding apn type upon
+ * {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED},
+ * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED} and
+ * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+ * Check {@link ApnSetting} TYPE_* for its values.
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
+
+ /**
+ * An string extra with the protocol of the apn connection (IP,IPV6, IPV4V6) upon
+ * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+ * @deprecated This is kept for backward compatibility reason.
+ * Use {@link #EXTRA_APN_PROTOCOL_INT} instead.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Deprecated
+ @SuppressLint("ActionValue")
+ public static final String EXTRA_APN_PROTOCOL = "apnProto";
+
+ /**
+ * An integer extra with the protocol of the apn connection (IP,IPV6, IPV4V6) upon
+ * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+ * Check {@link ApnSetting} PROTOCOL_* for its values.
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
+
+ /**
+ * An integer extra indicating the pco id for the data upon
+ * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String EXTRA_PCO_ID = "pcoId";
+
+ /**
+ * An extra of byte array of pco data read from modem upon
+ * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts.
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String EXTRA_PCO_VALUE = "pcoValue";
+
+ /**
+ * An boolean extra indicating default network available upon
+ * {@link #ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE} broadcasts.
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
/**
* <p>Broadcast Action: The emergency call state is changed.
* <ul>
- * <li><em>phoneInEmergencyCall</em> - A boolean value, true if phone in emergency call,
- * false otherwise</li>
+ * <li><em>EXTRA_PHONE_IN_EMERGENCY_CALL</em> - A boolean value, true if phone in emergency
+ * call, false otherwise</li>
* </ul>
* <p class="note">
* You can <em>not</em> receive this through components declared
@@ -1495,12 +1686,25 @@
*
* <p class="note">This is a protected intent that can only be sent by the system.
*
+ * @see #EXTRA_PHONE_IN_EMERGENCY_CALL
+ *
* @hide
*/
@SystemApi
@SuppressLint("ActionValue")
- public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED
- = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
+ public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED =
+ "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
+
+
+ /**
+ * Extra included in {@link #ACTION_EMERGENCY_CALL_STATE_CHANGED}.
+ * It indicates whether the phone is making an emergency call.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PHONE_IN_EMERGENCY_CALL =
+ "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
/**
* <p>Broadcast Action: It indicates the Emergency callback mode blocks datacall/sms
@@ -1513,8 +1717,8 @@
* @hide
*/
@SystemApi
- public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS
- = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
+ public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS =
+ "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
/**
* Broadcast Action: The default data subscription has changed in a multi-SIM device.
@@ -1527,8 +1731,8 @@
*/
@SystemApi
@SuppressLint("ActionValue")
- public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED
- = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
+ public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED =
+ "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
/**
* Broadcast Action: The default voice subscription has changed in a mult-SIm device.
@@ -1541,8 +1745,8 @@
*/
@SystemApi
@SuppressLint("ActionValue")
- public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED
- = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
+ public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED =
+ "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
/**
* Broadcast Action: This triggers a client initiated OMA-DM session to the OMA server.
@@ -1555,8 +1759,8 @@
*/
@SystemApi
@SuppressLint("ActionValue")
- public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE
- = "com.android.omadm.service.CONFIGURATION_UPDATE";
+ public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
+ "com.android.omadm.service.CONFIGURATION_UPDATE";
//
//
@@ -5242,6 +5446,13 @@
public static final int DATA_DISCONNECTING = 4;
/**
+ * To check the SDK version for {@link TelephonyManager#getDataState}.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long GET_DATA_STATE_CODE_CHANGE = 147600208L;
+
+ /**
* Returns a constant indicating the current data connection state
* (cellular).
*
@@ -5259,7 +5470,7 @@
int state = telephony.getDataStateForSubId(
getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
if (state == TelephonyManager.DATA_DISCONNECTING
- && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
+ && !Compatibility.isChangeEnabled(GET_DATA_STATE_CODE_CHANGE)) {
return TelephonyManager.DATA_CONNECTED;
}
@@ -5321,6 +5532,13 @@
//
/**
+ * To check the SDK version for {@link TelephonyManager#listen}.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ private static final long LISTEN_CODE_CHANGE = 147600208L;
+
+ /**
* Registers a listener object to receive notification of changes
* in specified telephony states.
* <p>
@@ -5359,7 +5577,7 @@
// subId from PhoneStateListener is deprecated Q on forward, use the subId from
// TelephonyManager instance. keep using subId from PhoneStateListener for pre-Q.
int subId = mSubId;
- if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
+ if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
// since mSubId in PhoneStateListener is deprecated from Q on forward, this is
// the only place to set mSubId and its for "informational" only.
// TODO: remove this once we completely get rid of mSubId in PhoneStateListener
@@ -8766,8 +8984,9 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public int getCdmaRoamingMode() {
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @CdmaRoamingMode int getCdmaRoamingMode() {
int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
try {
ITelephony telephony = getITelephony();
@@ -8794,8 +9013,9 @@
*
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public boolean setCdmaRoamingMode(int mode) {
+ public boolean setCdmaRoamingMode(@CdmaRoamingMode int mode) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -8807,6 +9027,36 @@
return false;
}
+ /** @hide */
+ @IntDef(flag = true, prefix = { "CDMA_SUBSCRIPTION_" }, value = {
+ CDMA_SUBSCRIPTION_UNKNOWN,
+ CDMA_SUBSCRIPTION_RUIM_SIM,
+ CDMA_SUBSCRIPTION_NV
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CdmaSubscription{}
+
+ /** Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source.
+ * @hide
+ */
+ @SystemApi
+ public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1;
+
+ /** Used for CDMA subscription mode: RUIM/SIM (default)
+ * @hide
+ */
+ @SystemApi
+ public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0;
+
+ /** Used for CDMA subscription mode: NV -> non-volatile memory
+ * @hide
+ */
+ @SystemApi
+ public static final int CDMA_SUBSCRIPTION_NV = 1;
+
+ /** @hide */
+ public static final int PREFERRED_CDMA_SUBSCRIPTION = CDMA_SUBSCRIPTION_RUIM_SIM;
+
/**
* Sets the subscription mode for CDMA phone to the given mode {@code mode}.
*
@@ -8814,14 +9064,15 @@
*
* @return {@code true} if successed.
*
- * @see Phone#CDMA_SUBSCRIPTION_UNKNOWN
- * @see Phone#CDMA_SUBSCRIPTION_RUIM_SIM
- * @see Phone#CDMA_SUBSCRIPTION_NV
+ * @see #CDMA_SUBSCRIPTION_UNKNOWN
+ * @see #CDMA_SUBSCRIPTION_RUIM_SIM
+ * @see #CDMA_SUBSCRIPTION_NV
*
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public boolean setCdmaSubscriptionMode(int mode) {
+ public boolean setCdmaSubscriptionMode(@CdmaSubscription int mode) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 7ffba7a..ab22d46 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -34,6 +34,7 @@
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -546,21 +547,75 @@
}
/**
+ * Get the provisioning status for the IMS RCS capability specified.
+ *
+ * If provisioning is not required for the queried
+ * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method will always return
+ * {@code true}.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+ * @return true if the device is provisioned for the capability or does not require
+ * provisioning, false if the capability does require provisioning and has not been
+ * provisioned yet.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean getRcsProvisioningStatusForCapability(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+ try {
+ return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Set the provisioning status for the IMS RCS capability using the specified subscription.
+ *
+ * Provisioning may or may not be required, depending on the carrier configuration. If
+ * provisioning is not required for the carrier associated with this subscription or the device
+ * does not support the capability/technology combination specified, this operation will be a
+ * no-op.
+ *
+ * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+ * @param isProvisioned true if the device is provisioned for the RCS capability specified,
+ * false otherwise.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setRcsProvisioningStatusForCapability(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ boolean isProvisioned) {
+ try {
+ getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
+ isProvisioned);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Notify the framework that an RCS autoconfiguration XML file has been received for
* provisioning.
+ * <p>
+ * Requires Permission: Manifest.permission.MODIFY_PHONE_STATE or that the calling app has
+ * carrier privileges (see {@link #hasCarrierPrivileges}).
* @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed.
* @param isCompressed The XML file is compressed in gzip format and must be decompressed
* before being read.
- * @hide
+ *
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
if (config == null) {
throw new IllegalArgumentException("Must include a non-null config XML file.");
}
- // TODO: Connect to ImsConfigImplBase.
- throw new UnsupportedOperationException("notifyRcsAutoConfigurationReceived is not"
- + "supported");
+ try {
+ getITelephony().notifyRcsAutoConfigurationReceived(mSubId, config, isCompressed);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+
}
private static boolean isImsAvailableOnDevice() {
diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
index 53e4596..57206c9 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
@@ -40,4 +40,5 @@
// Return result code defined in ImsConfig#OperationStatusConstants
int setConfigString(int item, String value);
void updateImsCarrierConfigs(in PersistableBundle bundle);
+ void notifyRcsAutoConfigurationReceived(in byte[] config, boolean isCompressed);
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 60cf216..6a2638b 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -17,6 +17,7 @@
package android.telephony.ims.stub;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Context;
@@ -200,6 +201,12 @@
}
}
+ @Override
+ public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)
+ throws RemoteException {
+ getImsConfigImpl().notifyRcsAutoConfigurationReceived(config, isCompressed);
+ }
+
private void notifyImsConfigChanged(int item, int value) throws RemoteException {
getImsConfigImpl().notifyConfigChanged(item, value);
}
@@ -358,9 +365,9 @@
* @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format.
* @param isCompressed The XML file is compressed in gzip format and must be decompressed
* before being read.
- * @hide
+ *
*/
- public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) {
+ public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
}
/**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b846a10..cdb95a8 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1977,6 +1977,17 @@
*/
boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech);
+ /**
+ * Get the provisioning status for the IMS Rcs capability specified.
+ */
+ boolean getRcsProvisioningStatusForCapability(int subId, int capability);
+
+ /**
+ * Set the provisioning status for the IMS Rcs capability using the specified subscription.
+ */
+ void setRcsProvisioningStatusForCapability(int subId, int capability,
+ boolean isProvisioned);
+
/** Is the capability and tech flagged as provisioned in the cache */
boolean isMmTelCapabilityProvisionedInCache(int subId, int capability, int tech);
@@ -2126,4 +2137,9 @@
* Command line command to enable or disable handling of CEP data for test purposes.
*/
oneway void setCepEnabled(boolean isCepEnabled);
+
+ /**
+ * Notify Rcs auto config received.
+ */
+ void notifyRcsAutoConfigurationReceived(int subId, in byte[] config, boolean isCompressed);
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 51701eb..db8c845 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -100,9 +100,6 @@
public static final String DATA_APN_TYPE_KEY = "apnType";
public static final String DATA_APN_KEY = "apn";
- public static final String PHONE_IN_ECM_STATE = "phoneinECMState";
- public static final String PHONE_IN_EMERGENCY_CALL = "phoneInEmergencyCall";
-
/**
* Return codes for supplyPinReturnResult and
* supplyPukReturnResult APIs
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 1fbe8cb..a15f73c 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -328,85 +328,6 @@
"android.intent.action.ACTION_SET_RADIO_CAPABILITY_FAILED";
/**
- * <p>Broadcast Action: when data connections get redirected with validation failure.
- * intended for sim/account status checks and only sent to the specified carrier app
- * The intent will have the following extra values:</p>
- * <ul>
- * <li>apnType</li><dd>A string with the apn type.</dd>
- * <li>redirectionUrl</li><dd>redirection url string</dd>
- * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
- * </ul>
- * <p class="note">This is a protected intent that can only be sent by the system.</p>
- */
- public static final String ACTION_CARRIER_SIGNAL_REDIRECTED =
- "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED";
- /**
- * <p>Broadcast Action: when data connections setup fails.
- * intended for sim/account status checks and only sent to the specified carrier app
- * The intent will have the following extra values:</p>
- * <ul>
- * <li>apnType</li><dd>A string with the apn type.</dd>
- * <li>errorCode</li><dd>A integer with dataFailCause.</dd>
- * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
- * </ul>
- * <p class="note">This is a protected intent that can only be sent by the system. </p>
- */
- public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED =
- "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED";
-
- /**
- * <p>Broadcast Action: when pco value is available.
- * intended for sim/account status checks and only sent to the specified carrier app
- * The intent will have the following extra values:</p>
- * <ul>
- * <li>apnType</li><dd>A string with the apn type.</dd>
- * <li>apnProto</li><dd>A string with the protocol of the apn connection (IP,IPV6,
- * IPV4V6)</dd>
- * <li>pcoId</li><dd>An integer indicating the pco id for the data.</dd>
- * <li>pcoValue</li><dd>A byte array of pco data read from modem.</dd>
- * <li>subId</li><dd>Sub Id which associated the data connection.</dd>
- * </ul>
- * <p class="note">This is a protected intent that can only be sent by the system. </p>
- */
- public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE =
- "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE";
-
- /**
- * <p>Broadcast Action: when system default network available/unavailable with
- * carrier-disabled mobile data. Intended for carrier apps to set/reset carrier actions when
- * other network becomes system default network, Wi-Fi for example.
- * The intent will have the following extra values:</p>
- * <ul>
- * <li>defaultNetworkAvailable</li><dd>A boolean indicates default network available.</dd>
- * <li>subId</li><dd>Sub Id which associated the default data.</dd>
- * </ul>
- * <p class="note">This is a protected intent that can only be sent by the system. </p>
- */
- public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE =
- "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
-
- /**
- * <p>Broadcast Action: when framework reset all carrier actions on sim load or absent.
- * intended for carrier apps clean up (clear UI e.g.) and only sent to the specified carrier app
- * The intent will have the following extra values:</p>
- * <ul>
- * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd>
- * </ul>
- * <p class="note">This is a protected intent that can only be sent by the system.</p>
- */
- public static final String ACTION_CARRIER_SIGNAL_RESET =
- "com.android.internal.telephony.CARRIER_SIGNAL_RESET";
-
- // CARRIER_SIGNAL_ACTION extra keys
- public static final String EXTRA_REDIRECTION_URL_KEY = "redirectionUrl";
- public static final String EXTRA_ERROR_CODE_KEY = "errorCode";
- public static final String EXTRA_APN_TYPE_KEY = "apnType";
- public static final String EXTRA_APN_PROTO_KEY = "apnProto";
- public static final String EXTRA_PCO_ID_KEY = "pcoId";
- public static final String EXTRA_PCO_VALUE_KEY = "pcoValue";
- public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY = "defaultNetworkAvailable";
-
- /**
* Broadcast action to trigger CI OMA-DM Session.
*/
public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/tests/net/common/java/android/net/CaptivePortalTest.java
index eed7159f..ca4ba63 100644
--- a/tests/net/common/java/android/net/CaptivePortalTest.java
+++ b/tests/net/common/java/android/net/CaptivePortalTest.java
@@ -44,6 +44,11 @@
}
@Override
+ public void appRequest(final int request) throws RemoteException {
+ mCode = request;
+ }
+
+ @Override
public void logEvent(int eventId, String packageName) throws RemoteException {
mCode = eventId;
mPackageName = packageName;
@@ -80,6 +85,12 @@
}
@Test
+ public void testReevaluateNetwork() {
+ final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.reevaluateNetwork());
+ assertEquals(result.mCode, CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED);
+ }
+
+ @Test
public void testLogEvent() {
final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent(
MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY,
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index a7328ac..6005cc3 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -27,8 +27,8 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.net.LinkProperties.CompareResult;
import android.net.LinkProperties.ProvisioningChange;
+import android.net.util.LinkPropertiesUtils.CompareResult;
import android.system.OsConstants;
import android.util.ArraySet;
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 1e8d83c..11d5b25 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -35,10 +35,10 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
-import android.net.NetworkFactory;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
+import android.net.NetworkProvider;
import android.net.NetworkSpecifier;
import android.net.SocketKeepalive;
import android.net.UidRange;
@@ -114,7 +114,7 @@
public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp) {
super(wrapper.mHandlerThread.getLooper(), wrapper.mContext, wrapper.mLogTag,
wrapper.mNetworkInfo, wrapper.mNetworkCapabilities, lp, wrapper.mScore,
- new NetworkMisc(), NetworkFactory.SerialNumber.NONE);
+ new NetworkAgentConfig(), NetworkProvider.ID_NONE);
mWrapper = wrapper;
}
@@ -222,7 +222,7 @@
@Override
public Network getNetwork() {
- return new Network(mNetworkAgent.netId);
+ return mNetworkAgent.network;
}
public void expectPreventReconnectReceived(long timeoutMs) {
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index daf187d..91c9a2a 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -22,6 +22,8 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.net.util.MacAddressUtils;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -122,11 +124,11 @@
for (MacAddress mac : multicastAddresses) {
String msg = mac.toString() + " expected to be a multicast address";
- assertTrue(msg, mac.isMulticastAddress());
+ assertTrue(msg, MacAddressUtils.isMulticastAddress(mac));
}
for (MacAddress mac : unicastAddresses) {
String msg = mac.toString() + " expected not to be a multicast address";
- assertFalse(msg, mac.isMulticastAddress());
+ assertFalse(msg, MacAddressUtils.isMulticastAddress(mac));
}
}
@@ -156,7 +158,7 @@
public void testMacAddressConversions() {
final int iterations = 10000;
for (int i = 0; i < iterations; i++) {
- MacAddress mac = MacAddress.createRandomUnicastAddress();
+ MacAddress mac = MacAddressUtils.createRandomUnicastAddress();
String stringRepr = mac.toString();
byte[] bytesRepr = mac.toByteArray();
@@ -188,7 +190,7 @@
final String expectedLocalOui = "26:5f:78";
final MacAddress base = MacAddress.fromString(anotherOui + ":0:0:0");
for (int i = 0; i < iterations; i++) {
- MacAddress mac = MacAddress.createRandomUnicastAddress(base, r);
+ MacAddress mac = MacAddressUtils.createRandomUnicastAddress(base, r);
String stringRepr = mac.toString();
assertTrue(stringRepr + " expected to be a locally assigned address",
@@ -199,7 +201,7 @@
}
for (int i = 0; i < iterations; i++) {
- MacAddress mac = MacAddress.createRandomUnicastAddress();
+ MacAddress mac = MacAddressUtils.createRandomUnicastAddress();
String stringRepr = mac.toString();
assertTrue(stringRepr + " expected to be a locally assigned address",
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b2d363e..1901a1d 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -575,7 +575,7 @@
}
};
- assertEquals(na.netId, nmNetworkCaptor.getValue().netId);
+ assertEquals(na.network.netId, nmNetworkCaptor.getValue().netId);
mNmCallbacks = nmCbCaptor.getValue();
mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor);
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 535298f..e863266 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -36,9 +36,8 @@
import android.net.INetd;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkFactory;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
+import android.net.NetworkProvider;
import android.net.NetworkScore;
import android.os.INetworkManagementService;
import android.text.format.DateUtils;
@@ -75,7 +74,6 @@
@Mock INetd mNetd;
@Mock INetworkManagementService mNMS;
@Mock Context mCtx;
- @Mock NetworkMisc mMisc;
@Mock NetworkNotificationManager mNotifier;
@Mock Resources mResources;
@@ -358,8 +356,8 @@
NetworkScore ns = new NetworkScore();
ns.putIntExtension(NetworkScore.LEGACY_SCORE, 50);
NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
- caps, ns, mCtx, null, mMisc, mConnService, mNetd, mDnsResolver, mNMS,
- NetworkFactory.SerialNumber.NONE);
+ caps, ns, mCtx, null, null /* config */, mConnService, mNetd, mDnsResolver, mNMS,
+ NetworkProvider.ID_NONE);
nai.everValidated = true;
return nai;
}
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index b709af1..9b24887 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -33,8 +33,8 @@
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NetworkAgentConfig;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.test.TestLooper;
@@ -63,7 +63,6 @@
static final int NETID = 42;
@Mock ConnectivityService mConnectivity;
- @Mock NetworkMisc mMisc;
@Mock IDnsResolver mDnsResolver;
@Mock INetd mNetd;
@Mock INetworkManagementService mNms;
@@ -72,6 +71,7 @@
TestLooper mLooper;
Handler mHandler;
+ NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
Nat464Xlat makeNat464Xlat() {
return new Nat464Xlat(mNai, mNetd, mDnsResolver, mNms) {
@@ -93,7 +93,7 @@
mNai.networkInfo = new NetworkInfo(null);
mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
when(mNai.connService()).thenReturn(mConnectivity);
- when(mNai.netMisc()).thenReturn(mMisc);
+ when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
when(mNai.handler()).thenReturn(mHandler);
when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
@@ -104,7 +104,7 @@
String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
+ "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
nai.networkInfo.getDetailedState(),
- mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+ mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
nai.linkProperties.getLinkAddresses());
assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
}
@@ -113,7 +113,7 @@
String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
+ "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
nai.networkInfo.getDetailedState(),
- mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+ mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
nai.linkProperties.getLinkAddresses());
assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
}
@@ -151,11 +151,11 @@
assertRequiresClat(true, mNai);
assertShouldStartClat(true, mNai);
- mMisc.skip464xlat = true;
+ mAgentConfig.skip464xlat = true;
assertRequiresClat(false, mNai);
assertShouldStartClat(false, mNai);
- mMisc.skip464xlat = false;
+ mAgentConfig.skip464xlat = false;
assertRequiresClat(true, mNai);
assertShouldStartClat(true, mNai);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 6de068e..a9e0b9a 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -80,6 +80,7 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
@@ -102,6 +103,7 @@
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.TestableNetworkStatsProvider;
import libcore.io.IoUtils;
@@ -1001,6 +1003,88 @@
mService.unregisterUsageRequest(unknownRequest);
}
+ @Test
+ public void testStatsProviderUpdateStats() throws Exception {
+ // Pretend that network comes online.
+ expectDefaultSettings();
+ final NetworkState[] states = new NetworkState[]{buildWifiState(true /* isMetered */)};
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+
+ // Register custom provider and retrieve callback.
+ final TestableNetworkStatsProvider provider = new TestableNetworkStatsProvider();
+ final INetworkStatsProviderCallback cb =
+ mService.registerNetworkStatsProvider("TEST", provider);
+ assertNotNull(cb);
+
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+
+ // Verifies that one requestStatsUpdate will be called during iface update.
+ provider.expectStatsUpdate(0 /* unused */);
+
+ // Create some initial traffic and report to the service.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ final NetworkStats expectedStats = new NetworkStats(0L, 1)
+ .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+ TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+ 128L, 2L, 128L, 2L, 1L))
+ .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+ 0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+ 64L, 1L, 64L, 1L, 1L));
+ cb.onStatsUpdated(0 /* unused */, expectedStats, expectedStats);
+
+ // Make another empty mutable stats object. This is necessary since the new NetworkStats
+ // object will be used to compare with the old one in NetworkStatsRecoder, two of them
+ // cannot be the same object.
+ expectNetworkStatsUidDetail(buildEmptyStats());
+
+ forcePollAndWaitForIdle();
+
+ // Verifies that one requestStatsUpdate and setAlert will be called during polling.
+ provider.expectStatsUpdate(0 /* unused */);
+ provider.expectSetAlert(MB_IN_BYTES);
+
+ // Verifies that service recorded history, does not verify uid tag part.
+ assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1);
+
+ // Verifies that onStatsUpdated updates the stats accordingly.
+ final NetworkStats stats = mSession.getSummaryForAllUid(
+ sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
+ assertEquals(2, stats.size());
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L);
+
+ // Verifies that unregister the callback will remove the provider from service.
+ cb.unregister();
+ forcePollAndWaitForIdle();
+ provider.assertNoCallback();
+ }
+
+ @Test
+ public void testStatsProviderSetAlert() throws Exception {
+ // Pretend that network comes online.
+ expectDefaultSettings();
+ NetworkState[] states = new NetworkState[]{buildWifiState(true /* isMetered */)};
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+
+ // Register custom provider and retrieve callback.
+ final TestableNetworkStatsProvider provider = new TestableNetworkStatsProvider();
+ final INetworkStatsProviderCallback cb =
+ mService.registerNetworkStatsProvider("TEST", provider);
+ assertNotNull(cb);
+
+ // Simulates alert quota of the provider has been reached.
+ cb.onAlertReached();
+ HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT);
+
+ // Verifies that polling is triggered by alert reached.
+ provider.expectStatsUpdate(0 /* unused */);
+ // Verifies that global alert will be re-armed.
+ provider.expectSetAlert(MB_IN_BYTES);
+ }
+
private static File getBaseDir(File statsDir) {
File baseDir = new File(statsDir, "netstats");
baseDir.mkdirs();
@@ -1102,6 +1186,7 @@
private void expectSettings(long persistBytes, long bucketDuration, long deleteAge)
throws Exception {
when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS);
+ when(mSettings.getPollDelay()).thenReturn(0L);
when(mSettings.getSampleEnabled()).thenReturn(true);
final Config config = new Config(bucketDuration, deleteAge, deleteAge);
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 3623b11..469128b 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -800,7 +800,12 @@
}
// This is a normal reference.
- return util::make_unique<Reference>(data, ref_type);
+ auto reference = util::make_unique<Reference>(data, ref_type);
+ if (res_value.dataType == android::Res_value::TYPE_DYNAMIC_REFERENCE ||
+ res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
+ reference->is_dynamic = true;
+ }
+ return reference;
} break;
}
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index c016cb4..b08bf9a 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -109,6 +109,20 @@
EXPECT_TRUE(private_ref);
}
+TEST(ResourceUtilsTest, ParseBinaryDynamicReference) {
+ android::Res_value value = {};
+ value.data = util::HostToDevice32(0x01);
+ value.dataType = android::Res_value::TYPE_DYNAMIC_REFERENCE;
+ std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(ResourceType::kId,
+ android::ConfigDescription(),
+ android::ResStringPool(), value,
+ nullptr);
+
+ Reference* ref = ValueCast<Reference>(item.get());
+ EXPECT_TRUE(ref->is_dynamic);
+ EXPECT_EQ(ref->id.value().id, 0x01);
+}
+
TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) {
bool create = false;
bool private_ref = false;
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 7498e13..8a2f5af 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -269,6 +269,11 @@
}
}
+// Message holding a boolean, so it can be optionally encoded.
+message Boolean {
+ bool value = 1;
+}
+
// A value that is a reference to another resource. This reference can be by name or resource ID.
message Reference {
enum Type {
@@ -289,6 +294,9 @@
// Whether this reference is referencing a private resource (@*package:type/entry).
bool private = 4;
+
+ // Whether this reference is dynamic.
+ Boolean is_dynamic = 5;
}
// A value that represents an ID. This is just a placeholder, as ID values are used to occupy a
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 4555caa..5b6935b 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -895,7 +895,7 @@
// android:versionCode from the framework AndroidManifest.xml.
ExtractCompileSdkVersions(asset_source->GetAssetManager());
}
- } else if (asset_source->IsPackageDynamic(entry.first)) {
+ } else if (asset_source->IsPackageDynamic(entry.first, entry.second)) {
final_table_.included_packages_[entry.first] = entry.second;
}
}
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index efbf636..4cd6e93 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -634,6 +634,7 @@
std::string* out_error) {
out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type());
out_ref->private_reference = pb_ref.private_();
+ out_ref->is_dynamic = pb_ref.is_dynamic().value();
if (pb_ref.id() != 0) {
out_ref->id = ResourceId(pb_ref.id());
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index e4b3fce..d9f6c19 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -418,6 +418,9 @@
pb_ref->set_private_(ref.private_reference);
pb_ref->set_type(SerializeReferenceTypeToPb(ref.reference_type));
+ if (ref.is_dynamic) {
+ pb_ref->mutable_is_dynamic()->set_value(ref.is_dynamic);
+ }
}
template <typename T>
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index e7f2330..61a8335 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -608,4 +608,41 @@
ASSERT_FALSE(search_result.value().entry->overlayable_item);
}
+TEST(ProtoSerializeTest, SerializeAndDeserializeDynamicReference) {
+ Reference ref(ResourceId(0x00010001));
+ ref.is_dynamic = true;
+
+ pb::Item pb_item;
+ SerializeItemToPb(ref, &pb_item);
+
+ ASSERT_TRUE(pb_item.has_ref());
+ EXPECT_EQ(pb_item.ref().id(), ref.id.value().id);
+ EXPECT_TRUE(pb_item.ref().is_dynamic().value());
+
+ std::unique_ptr<Item> item = DeserializeItemFromPb(pb_item, android::ResStringPool(),
+ android::ConfigDescription(), nullptr,
+ nullptr, nullptr);
+ Reference* actual_ref = ValueCast<Reference>(item.get());
+ EXPECT_EQ(actual_ref->id.value().id, ref.id.value().id);
+ EXPECT_TRUE(actual_ref->is_dynamic);
+}
+
+TEST(ProtoSerializeTest, SerializeAndDeserializeNonDynamicReference) {
+ Reference ref(ResourceId(0x00010001));
+
+ pb::Item pb_item;
+ SerializeItemToPb(ref, &pb_item);
+
+ ASSERT_TRUE(pb_item.has_ref());
+ EXPECT_EQ(pb_item.ref().id(), ref.id.value().id);
+ EXPECT_FALSE(pb_item.ref().has_is_dynamic());
+
+ std::unique_ptr<Item> item = DeserializeItemFromPb(pb_item, android::ResStringPool(),
+ android::ConfigDescription(), nullptr,
+ nullptr, nullptr);
+ Reference* actual_ref = ValueCast<Reference>(item.get());
+ EXPECT_EQ(actual_ref->id.value().id, ref.id.value().id);
+ EXPECT_FALSE(actual_ref->is_dynamic);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 83e20b5..897fa80 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -245,7 +245,8 @@
return package_map;
}
-bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
+bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId,
+ const std::string& package_name) const {
if (packageId == 0) {
return true;
}
@@ -253,7 +254,7 @@
for (const std::unique_ptr<const ApkAssets>& assets : apk_assets_) {
for (const std::unique_ptr<const android::LoadedPackage>& loaded_package
: assets->GetLoadedArsc()->GetPackages()) {
- if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) {
+ if (package_name == loaded_package->GetPackageName() && loaded_package->IsDynamic()) {
return true;
}
}
@@ -328,12 +329,12 @@
bool found = false;
ResourceId res_id = 0;
uint32_t type_spec_flags;
+ ResourceName real_name;
// There can be mangled resources embedded within other packages. Here we will
// look into each package and look-up the mangled name until we find the resource.
asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool {
- ResourceName real_name(name.package, name.type, name.entry);
-
+ real_name = ResourceName(name.package, name.type, name.entry);
if (package_name != name.package) {
real_name.entry = mangled_entry;
real_name.package = package_name;
@@ -353,12 +354,12 @@
}
std::unique_ptr<SymbolTable::Symbol> s;
- if (name.type == ResourceType::kAttr) {
+ if (real_name.type == ResourceType::kAttr) {
s = LookupAttributeInTable(asset_manager_, res_id);
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = res_id;
- s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id());
+ s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package);
}
if (s) {
@@ -406,7 +407,7 @@
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = id;
- s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id());
+ s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package);
}
if (s) {
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 6997cd6..06eaf63 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -194,7 +194,7 @@
bool AddAssetPath(const android::StringPiece& path);
std::map<size_t, std::string> GetAssignedPackageIds() const;
- bool IsPackageDynamic(uint32_t packageId) const;
+ bool IsPackageDynamic(uint32_t packageId, const std::string& package_name) const;
std::unique_ptr<SymbolTable::Symbol> FindByName(
const ResourceName& name) override;
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 0b82a3d..7bbac13 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -291,6 +291,15 @@
}
if (field->options().GetExtension(os::statsd::state_field_option).option() ==
+ os::statsd::StateField::PRIMARY_FIELD_FIRST_UID) {
+ if (javaType != JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ errorCount++;
+ } else {
+ atomDecl->primaryFields.push_back(FIRST_UID_IN_CHAIN_ID);
+ }
+ }
+
+ if (field->options().GetExtension(os::statsd::state_field_option).option() ==
os::statsd::StateField::EXCLUSIVE) {
if (javaType == JAVA_TYPE_UNKNOWN ||
javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 3efdd52..87d4d5d 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -36,6 +36,8 @@
const int PULL_ATOM_START_ID = 10000;
+const int FIRST_UID_IN_CHAIN_ID = 0;
+
/**
* The types for atom parameters.
*/
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
index 54a9982..66ae964 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ b/tools/stats_log_api_gen/atoms_info_writer.cpp
@@ -25,6 +25,8 @@
namespace stats_log_api_gen {
static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) {
+ fprintf(out, "static int FIRST_UID_IN_CHAIN = 0;\n");
+
fprintf(out, "struct StateAtomFieldOptions {\n");
fprintf(out, " std::vector<int> primaryFields;\n");
fprintf(out, " int exclusiveField;\n");
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 286be0b..70c9bef 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -50,20 +50,47 @@
"//frameworks/opt/net/wifi/libs/WifiTrackerLib/tests",
"//external/robolectric-shadows:__subpackages__",
+ "//frameworks/base/packages/SettingsLib/tests/integ",
+ "//external/sl4a:__subpackages__",
]
+// wifi-service needs pre-jarjared version of framework-wifi so it can reference copied utility
+// classes before they are renamed.
java_library {
- name: "framework-wifi",
+ name: "framework-wifi-pre-jarjar",
// TODO(b/140299412) should be core_current once we build against framework-system-stubs
sdk_version: "core_platform",
+ static_libs: [
+ "framework-wifi-util-lib",
+ "android.hardware.wifi-V1.0-java-constants",
+ ],
libs: [
// TODO(b/140299412) should be framework-system-stubs once we fix all @hide dependencies
"framework-minus-apex",
- "unsupportedappusage",
+ "framework-annotations-lib",
+ "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+ "unsupportedappusage-annotation", // for dalvik.annotation.compat.UnsupportedAppUsage
],
srcs: [
":framework-wifi-updatable-sources",
],
+ installable: false,
+ visibility: [
+ "//frameworks/opt/net/wifi/service",
+ "//frameworks/opt/net/wifi/tests/wifitests",
+ ],
+}
+
+// post-jarjar version of framework-wifi
+java_library {
+ name: "framework-wifi",
+ // TODO(b/140299412) should be core_current once we build against framework-system-stubs
+ sdk_version: "core_platform",
+ static_libs: [
+ "framework-wifi-pre-jarjar",
+ ],
+ jarjar_rules: ":wifi-jarjar-rules",
+
installable: true,
optimize: {
enabled: false
@@ -122,3 +149,8 @@
],
visibility: test_access_hidden_api_whitelist,
}
+
+filegroup {
+ name: "wifi-jarjar-rules",
+ srcs: ["jarjar-rules.txt"],
+}
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
new file mode 100644
index 0000000..8f72040
--- /dev/null
+++ b/wifi/jarjar-rules.txt
@@ -0,0 +1,40 @@
+rule android.net.InterfaceConfigurationParcel* @0
+rule android.net.InterfaceConfiguration* com.android.server.x.wifi.net.InterfaceConfiguration@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
+# inside our jar (currently in framework.jar, but will be in wifisdk.jar in the future).
+rule com.android.internal.util.FastXmlSerializer* com.android.server.x.wifi.util.FastXmlSerializer@1
+rule com.android.internal.util.HexDump* com.android.server.x.wifi.util.HexDump@1
+rule com.android.internal.util.IState* com.android.server.x.wifi.util.IState@1
+rule com.android.internal.util.MessageUtils* com.android.server.x.wifi.util.MessageUtils@1
+rule com.android.internal.util.State* com.android.server.x.wifi.util.State@1
+rule com.android.internal.util.StateMachine* com.android.server.x.wifi.util.StateMachine@1
+rule com.android.internal.util.WakeupMessage* com.android.server.x.wifi.util.WakeupMessage@1
+
+rule android.util.BackupUtils* com.android.server.x.wifi.util.BackupUtils@1
+rule android.util.LocalLog* com.android.server.x.wifi.util.LocalLog@1
+rule android.util.Rational* com.android.server.x.wifi.util.Rational@1
+
+rule android.os.BasicShellCommandHandler* com.android.server.x.wifi.os.BasicShellCommandHandler@1
+
+# Use our statically linked bouncy castle library
+rule org.bouncycastle.** com.android.server.x.wifi.bouncycastle.@1
+# Use our statically linked protobuf library
+rule com.google.protobuf.** com.android.server.x.wifi.protobuf.@1
+# use statically linked SystemMessageProto
+rule com.android.internal.messages.SystemMessageProto* com.android.server.x.wifi.messages.SystemMessageProto@1
+# Use our statically linked PlatformProperties library
+rule android.sysprop.** com.android.server.x.wifi.sysprop.@1
+
+
+# used by both framework-wifi and wifi-service
+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.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
+rule com.android.internal.util.AsyncService* android.x.net.wifi.util.AsyncService@1
+rule com.android.internal.util.Preconditions* android.x.net.wifi.util.Preconditions@1
+rule com.android.internal.util.Protocol* android.x.net.wifi.util.Protocol@1
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index c6aca07..3413305 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -23,6 +23,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -793,10 +795,19 @@
}
}
- /** empty scan result
+ /**
+ * Construct an empty scan result.
*
- * {@hide}
- * */
+ * Test code has a need to construct a ScanResult in a specific state.
+ * (Note that mocking using Mockito does not work if the object needs to be parceled and
+ * unparceled.)
+ * Export a @SystemApi default constructor to allow tests to construct an empty ScanResult
+ * object. The test can then directly set the fields it cares about.
+ *
+ * @hide
+ */
+ @SystemApi
+ @VisibleForTesting
public ScanResult() {
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index f4c5b91..d4fd903 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -29,6 +29,7 @@
import android.net.ProxyInfo;
import android.net.StaticIpConfiguration;
import android.net.Uri;
+import android.net.util.MacAddressUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,6 +40,8 @@
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
@@ -1164,7 +1167,7 @@
* @return true if mac is good to use
*/
public static boolean isValidMacAddressForRandomization(MacAddress mac) {
- return mac != null && !mac.isMulticastAddress() && mac.isLocallyAssigned()
+ return mac != null && !MacAddressUtils.isMulticastAddress(mac) && mac.isLocallyAssigned()
&& !MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS).equals(mac);
}
@@ -1208,22 +1211,27 @@
*/
@SystemApi
public static class NetworkSelectionStatus {
- // Quality Network Selection Status enable, temporary disabled, permanently disabled
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "NETWORK_SELECTION_",
+ value = {
+ NETWORK_SELECTION_ENABLED,
+ NETWORK_SELECTION_TEMPORARY_DISABLED,
+ NETWORK_SELECTION_PERMANENTLY_DISABLED})
+ public @interface NetworkEnabledStatus {}
/**
- * This network is allowed to join Quality Network Selection
- * @hide
+ * This network will be considered as a potential candidate to connect to during network
+ * selection.
*/
public static final int NETWORK_SELECTION_ENABLED = 0;
/**
- * network was temporary disabled. Can be re-enabled after a time period expire
- * @hide
+ * This network was temporary disabled. May be re-enabled after a time out.
*/
- public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1;
+ public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1;
/**
- * network was permanently disabled.
- * @hide
+ * This network was permanently disabled.
*/
- public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED = 2;
+ public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED = 2;
/**
* Maximum Network selection status
* @hide
@@ -1455,6 +1463,7 @@
* Network selection status, should be in one of three status: enable, temporaily disabled
* or permanently disabled
*/
+ @NetworkEnabledStatus
private int mStatus;
/**
@@ -1635,6 +1644,56 @@
}
/**
+ * NetworkSelectionStatus exports an immutable public API.
+ * However, test code has a need to construct a NetworkSelectionStatus in a specific state.
+ * (Note that mocking using Mockito does not work if the object needs to be parceled and
+ * unparceled.)
+ * Export a @SystemApi Builder to allow tests to construct a NetworkSelectionStatus object
+ * in the desired state, without sacrificing NetworkSelectionStatus's immutability.
+ */
+ @VisibleForTesting
+ public static final class Builder {
+ private final NetworkSelectionStatus mNetworkSelectionStatus =
+ new NetworkSelectionStatus();
+
+ /**
+ * Set the current network selection status.
+ * One of:
+ * {@link #NETWORK_SELECTION_ENABLED},
+ * {@link #NETWORK_SELECTION_TEMPORARY_DISABLED},
+ * {@link #NETWORK_SELECTION_PERMANENTLY_DISABLED}
+ * @see NetworkSelectionStatus#getNetworkSelectionStatus()
+ */
+ @NonNull
+ public Builder setNetworkSelectionStatus(@NetworkEnabledStatus int status) {
+ mNetworkSelectionStatus.setNetworkSelectionStatus(status);
+ return this;
+ }
+
+ /**
+ *
+ * Set the current network's disable reason.
+ * One of the {@link #NETWORK_SELECTION_ENABLE} or DISABLED_* constants.
+ * e.g. {@link #DISABLED_ASSOCIATION_REJECTION}.
+ * @see NetworkSelectionStatus#getNetworkSelectionDisableReason()
+ */
+ @NonNull
+ public Builder setNetworkSelectionDisableReason(
+ @NetworkSelectionDisableReason int reason) {
+ mNetworkSelectionStatus.setNetworkSelectionDisableReason(reason);
+ return this;
+ }
+
+ /**
+ * Build a NetworkSelectionStatus object.
+ */
+ @NonNull
+ public NetworkSelectionStatus build() {
+ return mNetworkSelectionStatus;
+ }
+ }
+
+ /**
* Get the network disable reason string for a reason code (for debugging).
* @param reason specific error reason. One of the {@link #NETWORK_SELECTION_ENABLE} or
* DISABLED_* constants e.g. {@link #DISABLED_ASSOCIATION_REJECTION}.
@@ -1660,10 +1719,13 @@
}
/**
- * get current network network selection status
- * @return return current network network selection status
- * @hide
+ * Get the current network network selection status.
+ * One of:
+ * {@link #NETWORK_SELECTION_ENABLED},
+ * {@link #NETWORK_SELECTION_TEMPORARY_DISABLED},
+ * {@link #NETWORK_SELECTION_PERMANENTLY_DISABLED}
*/
+ @NetworkEnabledStatus
public int getNetworkSelectionStatus() {
return mStatus;
}
@@ -1965,10 +2027,11 @@
}
/**
- * Set the network selection status
+ * Set the network selection status.
* @hide
*/
- public void setNetworkSelectionStatus(NetworkSelectionStatus status) {
+ @SystemApi
+ public void setNetworkSelectionStatus(@NonNull NetworkSelectionStatus status) {
mNetworkSelectionStatus = status;
}
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 41f7c6e..5edcc2d 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -1028,8 +1028,10 @@
}
/**
- * @hide
+ * Get the client private key as supplied in {@link #setClientKeyEntryWithCertificateChain}, or
+ * null if unset.
*/
+ @Nullable
public PrivateKey getClientPrivateKey() {
return mClientPrivateKey;
}
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 62337cb..51b15af 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -17,6 +17,7 @@
package android.net.wifi;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -27,6 +28,8 @@
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -356,6 +359,72 @@
}
}
+ /**
+ * WifiInfo exports an immutable public API.
+ * However, test code has a need to construct a WifiInfo in a specific state.
+ * (Note that mocking using Mockito does not work if the object needs to be parceled and
+ * unparceled.)
+ * Export a @SystemApi Builder to allow tests to construct a WifiInfo object
+ * in the desired state, without sacrificing WifiInfo's immutability.
+ *
+ * @hide
+ */
+ // This builder was not made public to reduce confusion for external developers as there are
+ // no legitimate uses for this builder except for testing.
+ @SystemApi
+ @VisibleForTesting
+ public static final class Builder {
+ private final WifiInfo mWifiInfo = new WifiInfo();
+
+ /**
+ * Set the SSID, in the form of a raw byte array.
+ * @see WifiInfo#getSSID()
+ */
+ @NonNull
+ public Builder setSsid(@NonNull byte[] ssid) {
+ mWifiInfo.setSSID(WifiSsid.createFromByteArray(ssid));
+ return this;
+ }
+
+ /**
+ * Set the BSSID.
+ * @see WifiInfo#getBSSID()
+ */
+ @NonNull
+ public Builder setBssid(@NonNull String bssid) {
+ mWifiInfo.setBSSID(bssid);
+ return this;
+ }
+
+ /**
+ * Set the RSSI, in dBm.
+ * @see WifiInfo#getRssi()
+ */
+ @NonNull
+ public Builder setRssi(int rssi) {
+ mWifiInfo.setRssi(rssi);
+ return this;
+ }
+
+ /**
+ * Set the network ID.
+ * @see WifiInfo#getNetworkId()
+ */
+ @NonNull
+ public Builder setNetworkId(int networkId) {
+ mWifiInfo.setNetworkId(networkId);
+ return this;
+ }
+
+ /**
+ * Build a WifiInfo object.
+ */
+ @NonNull
+ public WifiInfo build() {
+ return mWifiInfo;
+ }
+ }
+
/** @hide */
public void setSSID(WifiSsid wifiSsid) {
mWifiSsid = wifiSsid;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 729ac14..a8a31eb 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -233,16 +233,20 @@
public @interface SuggestionConnectionStatusCode {}
/**
- * Broadcast intent action indicating whether Wi-Fi scanning is allowed currently
- * @hide
+ * Broadcast intent action indicating whether Wi-Fi scanning is currently available.
+ * Available extras:
+ * - {@link #EXTRA_SCAN_AVAILABLE}
*/
- public static final String WIFI_SCAN_AVAILABLE = "wifi_scan_available";
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_WIFI_SCAN_AVAILABLE =
+ "android.net.wifi.action.WIFI_SCAN_AVAILABLE";
/**
- * Extra int indicating scan availability, WIFI_STATE_ENABLED and WIFI_STATE_DISABLED
- * @hide
+ * A boolean extra indicating whether scanning is currently available.
+ * Sent in the broadcast {@link #ACTION_WIFI_SCAN_AVAILABLE}.
+ * Its value is true if scanning is currently available, false otherwise.
*/
- public static final String EXTRA_SCAN_AVAILABLE = "scan_enabled";
+ public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE";
/**
* Broadcast intent action indicating that the credential of a Wi-Fi network
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 2c39c32a..4f602fa 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -833,6 +833,7 @@
* delivered to the listener. It is possible that onFullResult will not be called for all
* results of the first scan if the listener was registered during the scan.
*
+ * @param executor the Executor on which to run the callback.
* @param listener specifies the object to report events to. This object is also treated as a
* key for this request, and must also be specified to cancel the request.
* Multiple requests should also not share this object.
@@ -955,15 +956,32 @@
* starts a single scan and reports results asynchronously
* @param settings specifies various parameters for the scan; for more information look at
* {@link ScanSettings}
- * @param workSource WorkSource to blame for power usage
* @param listener specifies the object to report events to. This object is also treated as a
* key for this scan, and must also be specified to cancel the scan. Multiple
* scans should also not share this object.
+ * @param workSource WorkSource to blame for power usage
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) {
+ startScan(settings, null, listener, workSource);
+ }
+
+ /**
+ * starts a single scan and reports results asynchronously
+ * @param settings specifies various parameters for the scan; for more information look at
+ * {@link ScanSettings}
+ * @param executor the Executor on which to run the callback.
+ * @param listener specifies the object to report events to. This object is also treated as a
+ * key for this scan, and must also be specified to cancel the scan. Multiple
+ * scans should also not share this object.
+ * @param workSource WorkSource to blame for power usage
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ public void startScan(ScanSettings settings, @Nullable @CallbackExecutor Executor executor,
+ ScanListener listener, WorkSource workSource) {
Objects.requireNonNull(listener, "listener cannot be null");
- int key = addListener(listener);
+ int key = addListener(listener, executor);
if (key == INVALID_KEY) return;
validateChannel();
Bundle scanParams = new Bundle();
@@ -1029,16 +1047,17 @@
* {@link ScanSettings}
* @param pnoSettings specifies various parameters for PNO; for more information look at
* {@link PnoSettings}
+ * @param executor the Executor on which to run the callback.
* @param listener specifies the object to report events to. This object is also treated as a
* key for this scan, and must also be specified to cancel the scan. Multiple
* scans should also not share this object.
* {@hide}
*/
public void startConnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
- PnoScanListener listener) {
+ @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) {
Objects.requireNonNull(listener, "listener cannot be null");
Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null");
- int key = addListener(listener);
+ int key = addListener(listener, executor);
if (key == INVALID_KEY) return;
validateChannel();
pnoSettings.isConnected = true;
@@ -1057,10 +1076,10 @@
*/
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
- PnoScanListener listener) {
+ @NonNull @CallbackExecutor Executor executor, PnoScanListener listener) {
Objects.requireNonNull(listener, "listener cannot be null");
Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null");
- int key = addListener(listener);
+ int key = addListener(listener, executor);
if (key == INVALID_KEY) return;
validateChannel();
pnoSettings.isConnected = false;
diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp
new file mode 100644
index 0000000..6a39959
--- /dev/null
+++ b/wifi/tests/Android.bp
@@ -0,0 +1,50 @@
+// 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.
+
+// Make test APK
+// ============================================================
+
+android_test {
+ name: "FrameworksWifiApiTests",
+
+ defaults: ["framework-wifi-test-defaults"],
+
+ srcs: ["**/*.java"],
+
+ jacoco: {
+ include_filter: ["android.net.wifi.*"],
+ // TODO(b/147521214) need to exclude test classes
+ exclude_filter: [],
+ },
+
+ static_libs: [
+ "androidx.test.rules",
+ "core-test-rules",
+ "guava",
+ "mockito-target-minus-junit4",
+ "net-tests-utils",
+ "frameworks-base-testutils",
+ "truth-prebuilt",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ test_suites: [
+ "device-tests",
+ "mts",
+ ],
+}
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
deleted file mode 100644
index d2c385b4..0000000
--- a/wifi/tests/Android.mk
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright (C) 2016 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Make test APK
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-# This list is generated from the java source files in this module
-# The list is a comma separated list of class names with * matching zero or more characters.
-# Example:
-# Input files: src/com/android/server/wifi/Test.java src/com/android/server/wifi/AnotherTest.java
-# Generated exclude list: com.android.server.wifi.Test*,com.android.server.wifi.AnotherTest*
-
-# Filter all src files to just java files
-local_java_files := $(filter %.java,$(LOCAL_SRC_FILES))
-# Transform java file names into full class names.
-# This only works if the class name matches the file name and the directory structure
-# matches the package.
-local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files)))
-# Convert class name list to jacoco exclude list
-# This appends a * to all classes and replace the space separators with commas.
-# These patterns will match all classes in this module and their inner classes.
-jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes)))
-
-jacoco_include := android.net.wifi.*
-
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := $(jacoco_include)
-LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.rules \
- core-test-rules \
- guava \
- mockito-target-minus-junit4 \
- net-tests-utils \
- frameworks-base-testutils \
- truth-prebuilt \
-
-LOCAL_JAVA_LIBRARIES := \
- android.test.runner \
- android.test.base \
-
-LOCAL_PACKAGE_NAME := FrameworksWifiApiTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := \
- device-tests \
- mts \
-
-include $(BUILD_PACKAGE)
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 8689a38..0ef75aa 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.assertTrue;
import android.net.MacAddress;
+import android.net.util.MacAddressUtils;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.os.Parcel;
@@ -63,7 +64,7 @@
config.updateIdentifier = "1234";
config.fromWifiNetworkSpecifier = true;
config.fromWifiNetworkSuggestion = true;
- config.setRandomizedMacAddress(MacAddress.createRandomUnicastAddress());
+ config.setRandomizedMacAddress(MacAddressUtils.createRandomUnicastAddress());
MacAddress macBeforeParcel = config.getRandomizedMacAddress();
Parcel parcelW = Parcel.obtain();
config.writeToParcel(parcelW, 0);
@@ -169,7 +170,7 @@
MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
assertEquals(defaultMac, config.getRandomizedMacAddress());
- MacAddress macToChangeInto = MacAddress.createRandomUnicastAddress();
+ MacAddress macToChangeInto = MacAddressUtils.createRandomUnicastAddress();
config.setRandomizedMacAddress(macToChangeInto);
MacAddress macAfterChange = config.getRandomizedMacAddress();
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index 1af0bcb..0cc76b6 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -366,7 +366,7 @@
/**
* Test behavior of {@link WifiScanner#startDisconnectedPnoScan(ScanSettings, PnoSettings,
- * WifiScanner.PnoScanListener)}
+ * Executor, WifiScanner.PnoScanListener)}
* @throws Exception
*/
@Test
@@ -375,7 +375,8 @@
PnoSettings pnoSettings = new PnoSettings();
WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class);
- mWifiScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, pnoScanListener);
+ mWifiScanner.startDisconnectedPnoScan(
+ scanSettings, pnoSettings, mock(Executor.class), pnoScanListener);
mLooper.dispatchAll();
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
@@ -396,7 +397,7 @@
/**
* Test behavior of {@link WifiScanner#startConnectedPnoScan(ScanSettings, PnoSettings,
- * WifiScanner.PnoScanListener)}
+ * Executor, WifiScanner.PnoScanListener)}
* @throws Exception
*/
@Test
@@ -405,7 +406,8 @@
PnoSettings pnoSettings = new PnoSettings();
WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class);
- mWifiScanner.startConnectedPnoScan(scanSettings, pnoSettings, pnoScanListener);
+ mWifiScanner.startConnectedPnoScan(
+ scanSettings, pnoSettings, mock(Executor.class), pnoScanListener);
mLooper.dispatchAll();
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
@@ -426,7 +428,7 @@
/**
* Test behavior of {@link WifiScanner#stopPnoScan(ScanListener)}
- * WifiScanner.PnoScanListener)}
+ * Executor, WifiScanner.PnoScanListener)}
* @throws Exception
*/
@Test
@@ -435,7 +437,8 @@
PnoSettings pnoSettings = new PnoSettings();
WifiScanner.PnoScanListener pnoScanListener = mock(WifiScanner.PnoScanListener.class);
- mWifiScanner.startDisconnectedPnoScan(scanSettings, pnoSettings, pnoScanListener);
+ mWifiScanner.startDisconnectedPnoScan(
+ scanSettings, pnoSettings, mock(Executor.class), pnoScanListener);
mLooper.dispatchAll();
mWifiScanner.stopPnoScan(pnoScanListener);
mLooper.dispatchAll();