Merge "Transition SerialManager to @EnforcePermission"
diff --git a/Android.bp b/Android.bp
index 36ed865..d1b4f66 100644
--- a/Android.bp
+++ b/Android.bp
@@ -50,12 +50,8 @@
"SPDX-license-identifier-Apache-2.0",
"SPDX-license-identifier-BSD",
"SPDX-license-identifier-CC-BY",
- "SPDX-license-identifier-CPL-1.0",
- "SPDX-license-identifier-GPL",
- "SPDX-license-identifier-GPL-2.0",
"SPDX-license-identifier-MIT",
"SPDX-license-identifier-Unicode-DFS",
- "SPDX-license-identifier-W3C",
"legacy_unencumbered",
],
license_text: [
@@ -81,7 +77,7 @@
":framework-mca-effect-sources",
":framework-mca-filterfw-sources",
":framework-mca-filterpacks-sources",
- ":framework-media-sources",
+ ":framework-media-non-updatable-sources",
":framework-mms-sources",
":framework-omapi-sources",
":framework-opengl-sources",
@@ -141,27 +137,6 @@
}
java_library {
- name: "framework-updatable-stubs-module_libs_api",
- static_libs: [
- "android.net.ipsec.ike.stubs.module_lib",
- "framework-appsearch.stubs.module_lib",
- "framework-connectivity.stubs.module_lib",
- "framework-graphics.stubs.module_lib",
- "framework-media.stubs.module_lib",
- "framework-mediaprovider.stubs.module_lib",
- "framework-permission.stubs.module_lib",
- "framework-permission-s.stubs.module_lib",
- "framework-scheduling.stubs.module_lib",
- "framework-sdkextensions.stubs.module_lib",
- "framework-statsd.stubs.module_lib",
- "framework-tethering.stubs.module_lib",
- "framework-wifi.stubs.module_lib",
- ],
- sdk_version: "module_current",
- visibility: ["//visibility:private"],
-}
-
-java_library {
name: "framework-all",
installable: false,
static_libs: [
@@ -419,7 +394,6 @@
name: "framework-ike-shared-srcs",
visibility: ["//packages/modules/IPsec"],
srcs: [
- "core/java/android/net/annotations/PolicyDirection.java",
"core/java/com/android/internal/util/HexDump.java",
"core/java/com/android/internal/util/WakeupMessage.java",
"services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java",
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 8af2e02..4b5f473 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -60,9 +60,9 @@
defaults: ["android-non-updatable-stubs-defaults"],
srcs: [
// No longer part of the stubs, but are included in the docs.
- "test-base/src/**/*.java",
- "test-mock/src/**/*.java",
- "test-runner/src/**/*.java",
+ ":android-test-base-sources",
+ ":android-test-mock-sources",
+ ":android-test-runner-sources",
],
libs: framework_docs_only_libs,
create_doc_stubs: true,
@@ -74,11 +74,6 @@
srcs: [
":android-non-updatable-stub-sources",
- // Module sources
- ":art.module.public.api{.public.stubs.source}",
- ":conscrypt.module.public.api{.public.stubs.source}",
- ":i18n.module.public.api{.public.stubs.source}",
-
// No longer part of the stubs, but are included in the docs.
":android-test-base-sources",
":android-test-mock-sources",
@@ -116,6 +111,10 @@
name: "framework-doc-stubs-sources-default",
defaults: ["framework-doc-stubs-default"],
srcs: [
+ ":art.module.public.api{.public.stubs.source}",
+ ":conscrypt.module.public.api{.public.stubs.source}",
+ ":i18n.module.public.api{.public.stubs.source}",
+
":framework-appsearch-sources",
":framework-connectivity-sources",
":framework-graphics-srcs",
@@ -156,22 +155,8 @@
droidstubs {
name: "framework-doc-stubs",
defaults: ["framework-doc-stubs-default"],
+ srcs: [":all-modules-public-stubs-source"],
args: metalava_framework_docs_args,
- srcs: [
- ":android.net.ipsec.ike{.public.stubs.source}",
- ":framework-appsearch{.public.stubs.source}",
- ":framework-connectivity{.public.stubs.source}",
- ":framework-graphics{.public.stubs.source}",
- ":framework-media{.public.stubs.source}",
- ":framework-mediaprovider{.public.stubs.source}",
- ":framework-permission{.public.stubs.source}",
- ":framework-permission-s{.public.stubs.source}",
- ":framework-scheduling{.public.stubs.source}",
- ":framework-sdkextensions{.public.stubs.source}",
- ":framework-statsd{.public.stubs.source}",
- ":framework-tethering{.public.stubs.source}",
- ":framework-wifi{.public.stubs.source}",
- ],
aidl: {
local_include_dirs: [
"apex/media/aidl/stable",
@@ -182,42 +167,6 @@
},
}
-// This produces the same annotations.zip as framework-doc-stubs, but by using
-// outputs from individual modules instead of all the source code.
-genrule {
- name: "sdk-annotations.zip",
- srcs: [
- ":android-non-updatable-doc-stubs{.annotations.zip}",
-
- // Conscrypt and i18n currently do not enable annotations
- // ":conscrypt.module.public.api{.public.annotations.zip}",
- // ":i18n.module.public.api{.public.annotations.zip}",
-
- // Modules that enable annotations below
- ":android.net.ipsec.ike{.public.annotations.zip}",
- ":art.module.public.api{.public.annotations.zip}",
- ":framework-appsearch{.public.annotations.zip}",
- ":framework-connectivity{.public.annotations.zip}",
- ":framework-graphics{.public.annotations.zip}",
- ":framework-media{.public.annotations.zip}",
- ":framework-mediaprovider{.public.annotations.zip}",
- ":framework-permission{.public.annotations.zip}",
- ":framework-permission-s{.public.annotations.zip}",
- ":framework-scheduling{.public.annotations.zip}",
- ":framework-sdkextensions{.public.annotations.zip}",
- ":framework-statsd{.public.annotations.zip}",
- ":framework-tethering{.public.annotations.zip}",
- ":framework-wifi{.public.annotations.zip}",
- ],
- out: ["annotations.zip"],
- tools: [
- "merge_annotation_zips",
- "soong_zip",
- ],
- cmd: "$(location merge_annotation_zips) $(genDir)/out $(in) && " +
- "$(location soong_zip) -o $(out) -C $(genDir)/out -D $(genDir)/out",
-}
-
/////////////////////////////////////////////////////////////////////
// API docs are created from the generated stub source files
// using droiddoc
diff --git a/METADATA b/METADATA
index 95577d8..5c3f89c 100644
--- a/METADATA
+++ b/METADATA
@@ -1,4 +1,3 @@
third_party {
- # would be NOTICE save for libs/usb/tests/accessorytest/f_accessory.h
- license_type: RESTRICTED
+ license_type: RECIPROCAL
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 31fecd1..14da6fb 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -177,6 +177,7 @@
last_released: {
api_file: ":android-non-updatable.api.module-lib.latest",
removed_api_file: ":android-non-updatable-removed.api.module-lib.latest",
+ baseline_file: ":android-non-updatable-incompatibilities.api.module-lib.latest",
},
api_lint: {
enabled: true,
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index a1a46af..161a317 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -235,7 +235,6 @@
public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
/**
* All Bluetooth broadcasts.
- * @hide
*/
public static final int REASON_BLUETOOTH_BROADCAST = 203;
/**
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
index bed3895..2c5965c 100644
--- a/apex/media/OWNERS
+++ b/apex/media/OWNERS
@@ -1,6 +1,5 @@
# Bug component: 1344
hdmoon@google.com
-hkuang@google.com
jinpark@google.com
klhyun@google.com
lnilsson@google.com
diff --git a/api/Android.bp b/api/Android.bp
index c6ea175..d5d2105 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -24,6 +24,20 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+bootstrap_go_package {
+ name: "soong-api",
+ pkgPath: "android/soong/api",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ "soong-genrule",
+ "soong-java",
+ ],
+ srcs: ["api.go"],
+ pluginFor: ["soong_build"],
+}
+
python_defaults {
name: "python3_version_defaults",
version: {
@@ -89,43 +103,30 @@
visibility: ["//visibility:public"],
}
-genrule {
- name: "frameworks-base-api-current.txt",
- srcs: [
- ":android.net.ipsec.ike{.public.api.txt}",
- ":art.module.public.api{.public.api.txt}",
- ":conscrypt.module.public.api{.public.api.txt}",
- ":framework-appsearch{.public.api.txt}",
- ":framework-connectivity{.public.api.txt}",
- ":framework-graphics{.public.api.txt}",
- ":framework-media{.public.api.txt}",
- ":framework-mediaprovider{.public.api.txt}",
- ":framework-permission{.public.api.txt}",
- ":framework-permission-s{.public.api.txt}",
- ":framework-scheduling{.public.api.txt}",
- ":framework-sdkextensions{.public.api.txt}",
- ":framework-statsd{.public.api.txt}",
- ":framework-tethering{.public.api.txt}",
- ":framework-wifi{.public.api.txt}",
- ":i18n.module.public.api{.public.api.txt}",
- ":non-updatable-current.txt",
+combined_apis {
+ name: "frameworks-base-api",
+ bootclasspath: [
+ "android.net.ipsec.ike",
+ "art.module.public.api",
+ "conscrypt.module.public.api",
+ "framework-appsearch",
+ "framework-connectivity",
+ "framework-graphics",
+ "framework-media",
+ "framework-mediaprovider",
+ "framework-permission",
+ "framework-permission-s",
+ "framework-scheduling",
+ "framework-sdkextensions",
+ "framework-statsd",
+ "framework-tethering",
+ "framework-wifi",
+ "i18n.module.public.api",
],
- out: ["current.txt"],
- tools: ["metalava"],
- cmd: metalava_cmd + "$(in) --api $(out)",
- dists: [
- {
- targets: ["droidcore"],
- dir: "api",
- dest: "current.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/public/api",
- dest: "android.txt",
- },
+ system_server_classpath: [
+ "service-media-s",
+ "service-permission",
],
- visibility: ["//visibility:public"],
}
genrule {
@@ -145,108 +146,6 @@
}
genrule {
- name: "frameworks-base-api-current.srcjar",
- srcs: [
- ":android.net.ipsec.ike{.public.stubs.source}",
- ":api-stubs-docs-non-updatable",
- ":art.module.public.api{.public.stubs.source}",
- ":conscrypt.module.public.api{.public.stubs.source}",
- ":framework-appsearch{.public.stubs.source}",
- ":framework-connectivity{.public.stubs.source}",
- ":framework-graphics{.public.stubs.source}",
- ":framework-media{.public.stubs.source}",
- ":framework-mediaprovider{.public.stubs.source}",
- ":framework-permission{.public.stubs.source}",
- ":framework-permission-s{.public.stubs.source}",
- ":framework-scheduling{.public.stubs.source}",
- ":framework-sdkextensions{.public.stubs.source}",
- ":framework-statsd{.public.stubs.source}",
- ":framework-tethering{.public.stubs.source}",
- ":framework-wifi{.public.stubs.source}",
- ":i18n.module.public.api{.public.stubs.source}",
- ],
- out: ["current.srcjar"],
- tools: ["merge_zips"],
- cmd: "$(location merge_zips) $(out) $(in)",
- visibility: ["//visibility:private"], // Used by make module in //development, mind.
-}
-
-genrule {
- name: "frameworks-base-api-removed.txt",
- srcs: [
- ":android.net.ipsec.ike{.public.removed-api.txt}",
- ":art.module.public.api{.public.removed-api.txt}",
- ":conscrypt.module.public.api{.public.removed-api.txt}",
- ":framework-appsearch{.public.removed-api.txt}",
- ":framework-connectivity{.public.removed-api.txt}",
- ":framework-graphics{.public.removed-api.txt}",
- ":framework-media{.public.removed-api.txt}",
- ":framework-mediaprovider{.public.removed-api.txt}",
- ":framework-permission{.public.removed-api.txt}",
- ":framework-permission-s{.public.removed-api.txt}",
- ":framework-scheduling{.public.removed-api.txt}",
- ":framework-sdkextensions{.public.removed-api.txt}",
- ":framework-statsd{.public.removed-api.txt}",
- ":framework-tethering{.public.removed-api.txt}",
- ":framework-wifi{.public.removed-api.txt}",
- ":i18n.module.public.api{.public.removed-api.txt}",
- ":non-updatable-removed.txt",
- ],
- out: ["removed.txt"],
- tools: ["metalava"],
- cmd: metalava_cmd + "$(in) --api $(out)",
- dists: [
- {
- targets: ["droidcore"],
- dir: "api",
- dest: "removed.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/public/api",
- dest: "removed.txt",
- },
- ],
-}
-
-genrule {
- name: "frameworks-base-api-system-current.txt",
- srcs: [
- ":art.module.public.api{.system.api.txt}",
- ":android.net.ipsec.ike{.system.api.txt}",
- ":framework-appsearch{.system.api.txt}",
- ":framework-connectivity{.system.api.txt}",
- ":framework-graphics{.system.api.txt}",
- ":framework-media{.system.api.txt}",
- ":framework-mediaprovider{.system.api.txt}",
- ":framework-permission{.system.api.txt}",
- ":framework-permission-s{.system.api.txt}",
- ":framework-scheduling{.system.api.txt}",
- ":framework-sdkextensions{.system.api.txt}",
- ":framework-statsd{.system.api.txt}",
- ":framework-tethering{.system.api.txt}",
- ":framework-wifi{.system.api.txt}",
- ":non-updatable-system-current.txt",
- ],
- out: ["system-current.txt"],
- tools: ["metalava"],
- cmd: metalava_cmd + "$(in) --api $(out)",
- dists: [
- {
- targets: ["droidcore"],
- dir: "api",
- dest: "system-current.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/system/api",
- dest: "android.txt",
- },
- ],
- visibility: ["//visibility:public"],
-}
-
-genrule {
name: "frameworks-base-api-system-current-compat",
srcs: [
":android.api.system.latest",
@@ -265,79 +164,6 @@
}
genrule {
- name: "frameworks-base-api-system-removed.txt",
- srcs: [
- ":art.module.public.api{.system.removed-api.txt}",
- ":android.net.ipsec.ike{.system.removed-api.txt}",
- ":framework-appsearch{.system.removed-api.txt}",
- ":framework-connectivity{.system.removed-api.txt}",
- ":framework-graphics{.system.removed-api.txt}",
- ":framework-media{.system.removed-api.txt}",
- ":framework-mediaprovider{.system.removed-api.txt}",
- ":framework-permission{.system.removed-api.txt}",
- ":framework-permission-s{.system.removed-api.txt}",
- ":framework-scheduling{.system.removed-api.txt}",
- ":framework-sdkextensions{.system.removed-api.txt}",
- ":framework-statsd{.system.removed-api.txt}",
- ":framework-tethering{.system.removed-api.txt}",
- ":framework-wifi{.system.removed-api.txt}",
- ":non-updatable-system-removed.txt",
- ],
- out: ["system-removed.txt"],
- tools: ["metalava"],
- cmd: metalava_cmd + "$(in) --api $(out)",
- dists: [
- {
- targets: ["droidcore"],
- dir: "api",
- dest: "system-removed.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/system/api",
- dest: "removed.txt",
- },
- ],
- visibility: ["//visibility:public"],
-}
-
-genrule {
- name: "frameworks-base-api-module-lib-current.txt",
- srcs: [
- ":art.module.public.api{.module-lib.api.txt}",
- ":android.net.ipsec.ike{.module-lib.api.txt}",
- ":framework-appsearch{.module-lib.api.txt}",
- ":framework-connectivity{.module-lib.api.txt}",
- ":framework-graphics{.module-lib.api.txt}",
- ":framework-media{.module-lib.api.txt}",
- ":framework-mediaprovider{.module-lib.api.txt}",
- ":framework-permission{.module-lib.api.txt}",
- ":framework-permission-s{.module-lib.api.txt}",
- ":framework-scheduling{.module-lib.api.txt}",
- ":framework-sdkextensions{.module-lib.api.txt}",
- ":framework-statsd{.module-lib.api.txt}",
- ":framework-tethering{.module-lib.api.txt}",
- ":framework-wifi{.module-lib.api.txt}",
- ":non-updatable-module-lib-current.txt",
- ],
- out: ["module-lib-current.txt"],
- tools: ["metalava"],
- cmd: metalava_cmd + "$(in) --api $(out)",
- dists: [
- {
- targets: ["droidcore"],
- dir: "api",
- dest: "module-lib-current.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/module-lib/api",
- dest: "android.txt",
- },
- ],
-}
-
-genrule {
name: "frameworks-base-api-module-lib-current-compat",
srcs: [
":android.api.module-lib.latest",
@@ -359,42 +185,6 @@
}
genrule {
- name: "frameworks-base-api-module-lib-removed.txt",
- srcs: [
- ":art.module.public.api{.module-lib.removed-api.txt}",
- ":android.net.ipsec.ike{.module-lib.removed-api.txt}",
- ":framework-appsearch{.module-lib.removed-api.txt}",
- ":framework-connectivity{.module-lib.removed-api.txt}",
- ":framework-graphics{.module-lib.removed-api.txt}",
- ":framework-media{.module-lib.removed-api.txt}",
- ":framework-mediaprovider{.module-lib.removed-api.txt}",
- ":framework-permission{.module-lib.removed-api.txt}",
- ":framework-permission-s{.module-lib.removed-api.txt}",
- ":framework-scheduling{.module-lib.removed-api.txt}",
- ":framework-sdkextensions{.module-lib.removed-api.txt}",
- ":framework-statsd{.module-lib.removed-api.txt}",
- ":framework-tethering{.module-lib.removed-api.txt}",
- ":framework-wifi{.module-lib.removed-api.txt}",
- ":non-updatable-module-lib-removed.txt",
- ],
- out: ["module-lib-removed.txt"],
- tools: ["metalava"],
- cmd: metalava_cmd + "$(in) --api $(out)",
- dists: [
- {
- targets: ["droidcore"],
- dir: "api",
- dest: "module-lib-removed.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/module-lib/api",
- dest: "removed.txt",
- },
- ],
-}
-
-genrule {
name: "combined-removed-dex",
visibility: [
"//frameworks/base/boot",
@@ -410,86 +200,3 @@
out: ["combined-removed-dex.txt"],
cmd: "$(location gen_combined_removed_dex.sh) $(location metalava) $(genDir) $(in) > $(out)",
}
-
-genrule {
- name: "frameworks-base-api-system-server-current.txt",
- srcs: [
- ":service-media-s{.system-server.api.txt}",
- ":service-permission{.system-server.api.txt}",
- ":non-updatable-system-server-current.txt",
- ],
- out: ["system-server-current.txt"],
- tools: ["metalava"],
- cmd: metalava_cmd + "$(in) --api $(out)",
- dists: [
- {
- targets: ["droidcore"],
- dir: "api",
- dest: "system-server-current.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/system-server/api",
- dest: "android.txt",
- },
- ],
-}
-
-genrule {
- name: "frameworks-base-api-system-server-removed.txt",
- srcs: [
- ":service-media-s{.system-server.removed-api.txt}",
- ":service-permission{.system-server.removed-api.txt}",
- ":non-updatable-system-server-removed.txt",
- ],
- out: ["system-server-removed.txt"],
- tools: ["metalava"],
- cmd: metalava_cmd + "$(in) --api $(out)",
- dists: [
- {
- targets: ["droidcore"],
- dir: "api",
- dest: "system-server-removed.txt",
- },
- {
- targets: ["sdk"],
- dir: "apistubs/android/system-server/api",
- dest: "removed.txt",
- },
- ],
-}
-
-// This rule will filter classes present in the jar files of mainline modules
-// from the lint database in api-versions.xml.
-// This is done to reduce the number of false positive NewApi findings in
-// java libraries that compile against the module SDK
-genrule {
- name: "api-versions-xml-public-filtered",
- srcs: [
- // Note: order matters: first parameter is the full api-versions.xml
- // after that the stubs files in any order
- // stubs files are all modules that export API surfaces EXCEPT ART
- ":framework-doc-stubs{.api_versions.xml}",
- ":android.net.ipsec.ike.stubs{.jar}",
- ":conscrypt.module.public.api.stubs{.jar}",
- ":framework-appsearch.stubs{.jar}",
- ":framework-connectivity.stubs{.jar}",
- ":framework-graphics.stubs{.jar}",
- ":framework-media.stubs{.jar}",
- ":framework-mediaprovider.stubs{.jar}",
- ":framework-permission.stubs{.jar}",
- ":framework-permission-s.stubs{.jar}",
- ":framework-scheduling.stubs{.jar}",
- ":framework-sdkextensions.stubs{.jar}",
- ":framework-statsd.stubs{.jar}",
- ":framework-tethering.stubs{.jar}",
- ":framework-wifi.stubs{.jar}",
- ":i18n.module.public.api.stubs{.jar}",
- ],
- out: ["api-versions-public-filtered.xml"],
- tools: ["api_versions_trimmer"],
- cmd: "$(location api_versions_trimmer) $(out) $(in)",
- dist: {
- targets: ["sdk"],
- },
-}
diff --git a/api/api.go b/api/api.go
new file mode 100644
index 0000000..4b6ebc1
--- /dev/null
+++ b/api/api.go
@@ -0,0 +1,304 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package api
+
+import (
+ "sort"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/genrule"
+ "android/soong/java"
+)
+
+const art = "art.module.public.api"
+const conscrypt = "conscrypt.module.public.api"
+const i18n = "i18n.module.public.api"
+
+// The intention behind this soong plugin is to generate a number of "merged"
+// API-related modules that would otherwise require a large amount of very
+// similar Android.bp boilerplate to define. For example, the merged current.txt
+// API definitions (created by merging the non-updatable current.txt with all
+// the module current.txts). This simplifies the addition of new android
+// modules, by reducing the number of genrules etc a new module must be added to.
+
+// The properties of the combined_apis module type.
+type CombinedApisProperties struct {
+ // Module libraries in the bootclasspath
+ Bootclasspath []string
+ // Module libraries on the bootclasspath if include_nonpublic_framework_api is true.
+ Conditional_bootclasspath []string
+ // Module libraries in system server
+ System_server_classpath []string
+}
+
+type CombinedApis struct {
+ android.ModuleBase
+
+ properties CombinedApisProperties
+}
+
+func init() {
+ registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
+}
+
+var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
+
+func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+
+type genruleProps struct {
+ Name *string
+ Cmd *string
+ Dists []android.Dist
+ Out []string
+ Srcs []string
+ Tools []string
+ Visibility []string
+}
+
+type libraryProps struct {
+ Name *string
+ Sdk_version *string
+ Static_libs []string
+ Visibility []string
+}
+
+type fgProps struct {
+ Name *string
+ Srcs []string
+ Visibility []string
+}
+
+// Struct to pass parameters for the various merged [current|removed].txt file modules we create.
+type MergedTxtDefinition struct {
+ // "current.txt" or "removed.txt"
+ TxtFilename string
+ // The module for the non-updatable / non-module part of the api.
+ BaseTxt string
+ // The list of modules that are relevant for this merged txt.
+ Modules []string
+ // The output tag for each module to use.e.g. {.public.api.txt} for current.txt
+ ModuleTag string
+ // public, system, module-lib or system-server
+ Scope string
+}
+
+func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) {
+ metalavaCmd := "$(location metalava)"
+ // Silence reflection warnings. See b/168689341
+ metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
+ metalavaCmd += " --quiet --no-banner --format=v2 "
+
+ filename := txt.TxtFilename
+ if txt.Scope != "public" {
+ filename = txt.Scope + "-" + filename
+ }
+
+ props := genruleProps{}
+ props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
+ props.Tools = []string{"metalava"}
+ props.Out = []string{filename}
+ props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
+ props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
+ props.Dists = []android.Dist{
+ {
+ Targets: []string{"droidcore"},
+ Dir: proptools.StringPtr("api"),
+ Dest: proptools.StringPtr(filename),
+ },
+ {
+ Targets: []string{"sdk"},
+ Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
+ Dest: proptools.StringPtr(txt.TxtFilename),
+ },
+ }
+ props.Visibility = []string{"//visibility:public"}
+ ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) {
+ props := genruleProps{}
+ props.Name = proptools.StringPtr(ctx.ModuleName() + "-current.srcjar")
+ props.Tools = []string{"merge_zips"}
+ props.Out = []string{"current.srcjar"}
+ props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)")
+ props.Srcs = append([]string{":api-stubs-docs-non-updatable"}, createSrcs(modules, "{.public.stubs.source}")...)
+ props.Visibility = []string{"//visibility:private"} // Used by make module in //development, mind
+ ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+// This produces the same annotations.zip as framework-doc-stubs, but by using
+// outputs from individual modules instead of all the source code.
+func createMergedAnnotations(ctx android.LoadHookContext, modules []string) {
+ // Conscrypt and i18n currently do not enable annotations
+ modules = removeAll(modules, []string{conscrypt, i18n})
+ props := genruleProps{}
+ props.Name = proptools.StringPtr("sdk-annotations.zip")
+ props.Tools = []string{"merge_annotation_zips", "soong_zip"}
+ props.Out = []string{"annotations.zip"}
+ props.Cmd = proptools.StringPtr("$(location merge_annotation_zips) $(genDir)/out $(in) && " +
+ "$(location soong_zip) -o $(out) -C $(genDir)/out -D $(genDir)/out")
+ props.Srcs = append([]string{":android-non-updatable-doc-stubs{.annotations.zip}"}, createSrcs(modules, "{.public.annotations.zip}")...)
+ ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) {
+ // For the filtered api versions, we prune all APIs except art module's APIs. because
+ // 1) ART apis are available by default to all modules, while other module-to-module deps are
+ // explicit and probably receive more scrutiny anyway
+ // 2) The number of ART/libcore APIs is large, so not linting them would create a large gap
+ // 3) It's a compromise. Ideally we wouldn't be filtering out any module APIs, and have
+ // per-module lint databases that excludes just that module's APIs. Alas, that's more
+ // difficult to achieve.
+ modules = remove(modules, art)
+
+ props := genruleProps{}
+ props.Name = proptools.StringPtr("api-versions-xml-public-filtered")
+ props.Tools = []string{"api_versions_trimmer"}
+ props.Out = []string{"api-versions-public-filtered.xml"}
+ props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)")
+ // Note: order matters: first parameter is the full api-versions.xml
+ // after that the stubs files in any order
+ // stubs files are all modules that export API surfaces EXCEPT ART
+ props.Srcs = append([]string{":framework-doc-stubs{.api_versions.xml}"}, createSrcs(modules, ".stubs{.jar}")...)
+ props.Dists = []android.Dist{{Targets: []string{"sdk"}}}
+ ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createMergedModuleLibStubs(ctx android.LoadHookContext, modules []string) {
+ // The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes.
+ modules = removeAll(modules, []string{art, conscrypt, i18n})
+ props := libraryProps{}
+ props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
+ props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
+ props.Sdk_version = proptools.StringPtr("module_current")
+ props.Visibility = []string{"//frameworks/base"}
+ ctx.CreateModule(java.LibraryFactory, &props)
+}
+
+func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []string) {
+ props := fgProps{}
+ props.Name = proptools.StringPtr("all-modules-public-stubs-source")
+ props.Srcs = createSrcs(modules, "{.public.stubs.source}")
+ props.Visibility = []string{"//frameworks/base"}
+ ctx.CreateModule(android.FileGroupFactory, &props)
+}
+
+func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
+ var textFiles []MergedTxtDefinition
+ // Two module libraries currently do not support @SystemApi so only have the public scope.
+ bcpWithSystemApi := removeAll(bootclasspath, []string{conscrypt, i18n})
+
+ tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
+ for i, f := range []string{"current.txt", "removed.txt"} {
+ textFiles = append(textFiles, MergedTxtDefinition{
+ TxtFilename: f,
+ BaseTxt: ":non-updatable-" + f,
+ Modules: bootclasspath,
+ ModuleTag: "{.public" + tagSuffix[i],
+ Scope: "public",
+ })
+ textFiles = append(textFiles, MergedTxtDefinition{
+ TxtFilename: f,
+ BaseTxt: ":non-updatable-system-" + f,
+ Modules: bcpWithSystemApi,
+ ModuleTag: "{.system" + tagSuffix[i],
+ Scope: "system",
+ })
+ textFiles = append(textFiles, MergedTxtDefinition{
+ TxtFilename: f,
+ BaseTxt: ":non-updatable-module-lib-" + f,
+ Modules: bcpWithSystemApi,
+ ModuleTag: "{.module-lib" + tagSuffix[i],
+ Scope: "module-lib",
+ })
+ textFiles = append(textFiles, MergedTxtDefinition{
+ TxtFilename: f,
+ BaseTxt: ":non-updatable-system-server-" + f,
+ Modules: system_server_classpath,
+ ModuleTag: "{.system-server" + tagSuffix[i],
+ Scope: "system-server",
+ })
+ }
+ for _, txt := range textFiles {
+ createMergedTxt(ctx, txt)
+ }
+}
+
+func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
+ bootclasspath := a.properties.Bootclasspath
+ if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") {
+ bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...)
+ sort.Strings(bootclasspath)
+ }
+ createMergedTxts(ctx, bootclasspath, a.properties.System_server_classpath)
+
+ createMergedStubsSrcjar(ctx, bootclasspath)
+
+ createMergedModuleLibStubs(ctx, bootclasspath)
+
+ createMergedAnnotations(ctx, bootclasspath)
+
+ createFilteredApiVersions(ctx, bootclasspath)
+
+ createPublicStubsSourceFilegroup(ctx, bootclasspath)
+}
+
+func combinedApisModuleFactory() android.Module {
+ module := &CombinedApis{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidModule(module)
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
+ return module
+}
+
+// Various utility methods below.
+
+// Creates an array of ":<m><tag>" for each m in <modules>.
+func createSrcs(modules []string, tag string) []string {
+ return transformArray(modules, ":", tag)
+}
+
+// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
+func transformArray(modules []string, prefix, suffix string) []string {
+ a := make([]string, 0, len(modules))
+ for _, module := range modules {
+ a = append(a, prefix+module+suffix)
+ }
+ return a
+}
+
+func removeAll(s []string, vs []string) []string {
+ for _, v := range vs {
+ s = remove(s, v)
+ }
+ return s
+}
+
+func remove(s []string, v string) []string {
+ s2 := make([]string, 0, len(s))
+ for _, sv := range s {
+ if sv != v {
+ s2 = append(s2, sv)
+ }
+ }
+ return s2
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index 9233941..d696bc7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17980,10 +17980,12 @@
ctor public BiometricPrompt.CryptoObject(@NonNull java.security.Signature);
ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Cipher);
ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac);
- ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
+ ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
+ ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
method public javax.crypto.Cipher getCipher();
- method @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
+ method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
method public javax.crypto.Mac getMac();
+ method @Nullable public android.security.identity.PresentationSession getPresentationSession();
method public java.security.Signature getSignature();
}
@@ -26896,11 +26898,13 @@
method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull java.net.InetAddress);
method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull String);
method @NonNull public android.net.VpnService.Builder addRoute(@NonNull java.net.InetAddress, int);
+ method @NonNull public android.net.VpnService.Builder addRoute(@NonNull android.net.IpPrefix);
method @NonNull public android.net.VpnService.Builder addRoute(@NonNull String, int);
method @NonNull public android.net.VpnService.Builder addSearchDomain(@NonNull String);
method @NonNull public android.net.VpnService.Builder allowBypass();
method @NonNull public android.net.VpnService.Builder allowFamily(int);
method @Nullable public android.os.ParcelFileDescriptor establish();
+ method @NonNull public android.net.VpnService.Builder excludeRoute(@NonNull android.net.IpPrefix);
method @NonNull public android.net.VpnService.Builder setBlocking(boolean);
method @NonNull public android.net.VpnService.Builder setConfigureIntent(@NonNull android.app.PendingIntent);
method @NonNull public android.net.VpnService.Builder setHttpProxy(@NonNull android.net.ProxyInfo);
@@ -27277,6 +27281,25 @@
package android.net.vcn {
+ public final class VcnCellUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
+ method @NonNull public java.util.Set<java.lang.String> getOperatorPlmnIds();
+ method public int getOpportunistic();
+ method public int getRoaming();
+ method @NonNull public java.util.Set<java.lang.Integer> getSimSpecificCarrierIds();
+ }
+
+ public static final class VcnCellUnderlyingNetworkTemplate.Builder {
+ ctor public VcnCellUnderlyingNetworkTemplate.Builder();
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate build();
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMetered(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOperatorPlmnIds(@NonNull java.util.Set<java.lang.String>);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOpportunistic(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRoaming(int);
+ method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setSimSpecificCarrierIds(@NonNull java.util.Set<java.lang.Integer>);
+ }
+
public final class VcnConfig implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.Set<android.net.vcn.VcnGatewayConnectionConfig> getGatewayConnectionConfigs();
@@ -27295,6 +27318,7 @@
method @NonNull public String getGatewayConnectionName();
method @IntRange(from=0x500) public int getMaxMtu();
method @NonNull public long[] getRetryIntervalsMillis();
+ method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
}
public static final class VcnGatewayConnectionConfig.Builder {
@@ -27304,6 +27328,7 @@
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>);
}
public class VcnManager {
@@ -27327,6 +27352,30 @@
method public abstract void onStatusChanged(int);
}
+ public abstract class VcnUnderlyingNetworkTemplate {
+ method public int getMetered();
+ method public int getMinEntryDownstreamBandwidthKbps();
+ method public int getMinEntryUpstreamBandwidthKbps();
+ method public int getMinExitDownstreamBandwidthKbps();
+ method public int getMinExitUpstreamBandwidthKbps();
+ field public static final int MATCH_ANY = 0; // 0x0
+ field public static final int MATCH_FORBIDDEN = 2; // 0x2
+ field public static final int MATCH_REQUIRED = 1; // 0x1
+ }
+
+ public final class VcnWifiUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
+ method @NonNull public java.util.Set<java.lang.String> getSsids();
+ }
+
+ public static final class VcnWifiUnderlyingNetworkTemplate.Builder {
+ ctor public VcnWifiUnderlyingNetworkTemplate.Builder();
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate build();
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMetered(int);
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinDownstreamBandwidthKbps(int, int);
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
+ method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setSsids(@NonNull java.util.Set<java.lang.String>);
+ }
+
}
package android.nfc {
@@ -37503,6 +37552,51 @@
ctor public CipherSuiteNotSupportedException(@NonNull String, @NonNull Throwable);
}
+ public class CredentialDataRequest {
+ method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getDeviceSignedEntriesToRequest();
+ method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getIssuerSignedEntriesToRequest();
+ method @Nullable public byte[] getReaderSignature();
+ method @Nullable public byte[] getRequestMessage();
+ method public boolean isAllowUsingExhaustedKeys();
+ method public boolean isAllowUsingExpiredKeys();
+ method public boolean isIncrementUseCount();
+ }
+
+ public static final class CredentialDataRequest.Builder {
+ ctor public CredentialDataRequest.Builder();
+ method @NonNull public android.security.identity.CredentialDataRequest build();
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExhaustedKeys(boolean);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExpiredKeys(boolean);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setDeviceSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setIncrementUseCount(boolean);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setIssuerSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setReaderSignature(@NonNull byte[]);
+ method @NonNull public android.security.identity.CredentialDataRequest.Builder setRequestMessage(@NonNull byte[]);
+ }
+
+ public abstract class CredentialDataResult {
+ method @Nullable public abstract byte[] getDeviceMac();
+ method @NonNull public abstract byte[] getDeviceNameSpaces();
+ method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getDeviceSignedEntries();
+ method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getIssuerSignedEntries();
+ method @NonNull public abstract byte[] getStaticAuthenticationData();
+ }
+
+ public static interface CredentialDataResult.Entries {
+ method @Nullable public byte[] getEntry(@NonNull String, @NonNull String);
+ method @NonNull public java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
+ method @NonNull public java.util.Collection<java.lang.String> getNamespaces();
+ method @NonNull public java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
+ method public int getStatus(@NonNull String, @NonNull String);
+ field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
+ field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
+ field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
+ field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
+ field public static final int STATUS_OK = 0; // 0x0
+ field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
+ field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
+ }
+
public class DocTypeNotSupportedException extends android.security.identity.IdentityCredentialException {
ctor public DocTypeNotSupportedException(@NonNull String);
ctor public DocTypeNotSupportedException(@NonNull String, @NonNull Throwable);
@@ -37514,19 +37608,19 @@
}
public abstract class IdentityCredential {
- method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
- method @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
+ method @Deprecated @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
+ method @Deprecated @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
method @NonNull public byte[] delete(@NonNull byte[]);
- method @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
+ method @Deprecated @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
method @NonNull public abstract int[] getAuthenticationDataUsageCount();
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
- method @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
+ method @Deprecated @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
method @NonNull public byte[] proveOwnership(@NonNull byte[]);
- method public abstract void setAllowUsingExhaustedKeys(boolean);
- method public void setAllowUsingExpiredKeys(boolean);
+ method @Deprecated public abstract void setAllowUsingExhaustedKeys(boolean);
+ method @Deprecated public void setAllowUsingExpiredKeys(boolean);
method public abstract void setAvailableAuthenticationKeys(int, int);
- method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+ method @Deprecated public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
method @Deprecated public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
method public void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull java.time.Instant, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
method @NonNull public byte[] update(@NonNull android.security.identity.PersonalizationData);
@@ -37539,6 +37633,7 @@
public abstract class IdentityCredentialStore {
method @NonNull public abstract android.security.identity.WritableIdentityCredential createCredential(@NonNull String, @NonNull String) throws android.security.identity.AlreadyPersonalizedException, android.security.identity.DocTypeNotSupportedException;
+ method @NonNull public android.security.identity.PresentationSession createPresentationSession(int) throws android.security.identity.CipherSuiteNotSupportedException;
method @Deprecated @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
method @Nullable public abstract android.security.identity.IdentityCredential getCredentialByName(@NonNull String, int) throws android.security.identity.CipherSuiteNotSupportedException;
method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context);
@@ -37577,22 +37672,29 @@
method @NonNull public android.security.identity.PersonalizationData.Builder putEntry(@NonNull String, @NonNull String, @NonNull java.util.Collection<android.security.identity.AccessControlProfileId>, @NonNull byte[]);
}
- public abstract class ResultData {
- method @NonNull public abstract byte[] getAuthenticatedData();
- method @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
- method @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
- method @Nullable public abstract byte[] getMessageAuthenticationCode();
- method @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
- method @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
- method @NonNull public abstract byte[] getStaticAuthenticationData();
- method public abstract int getStatus(@NonNull String, @NonNull String);
- field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
- field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
- field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
- field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
- field public static final int STATUS_OK = 0; // 0x0
- field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
- field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
+ public abstract class PresentationSession {
+ method @Nullable public abstract android.security.identity.CredentialDataResult getCredentialData(@NonNull String, @NonNull android.security.identity.CredentialDataRequest) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException;
+ method @NonNull public abstract java.security.KeyPair getEphemeralKeyPair();
+ method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+ method public abstract void setSessionTranscript(@NonNull byte[]);
+ }
+
+ @Deprecated public abstract class ResultData {
+ method @Deprecated @NonNull public abstract byte[] getAuthenticatedData();
+ method @Deprecated @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
+ method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
+ method @Deprecated @Nullable public abstract byte[] getMessageAuthenticationCode();
+ method @Deprecated @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
+ method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
+ method @Deprecated @NonNull public abstract byte[] getStaticAuthenticationData();
+ method @Deprecated public abstract int getStatus(@NonNull String, @NonNull String);
+ field @Deprecated public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
+ field @Deprecated public static final int STATUS_NOT_REQUESTED = 2; // 0x2
+ field @Deprecated public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
+ field @Deprecated public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
+ field @Deprecated public static final int STATUS_OK = 0; // 0x0
+ field @Deprecated public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
+ field @Deprecated public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
}
public class SessionTranscriptMismatchException extends android.security.identity.IdentityCredentialException {
@@ -41215,6 +41317,7 @@
field public static final int EPDG_ADDRESS_PCO = 2; // 0x2
field public static final int EPDG_ADDRESS_PLMN = 1; // 0x1
field public static final int EPDG_ADDRESS_STATIC = 0; // 0x0
+ field public static final int EPDG_ADDRESS_VISITED_COUNTRY = 4; // 0x4
field public static final int ID_TYPE_FQDN = 2; // 0x2
field public static final int ID_TYPE_KEY_ID = 11; // 0xb
field public static final int ID_TYPE_RFC822_ADDR = 3; // 0x3
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index d13170bf..b380e6d 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -71,6 +71,14 @@
field public static final String TEST_NETWORK_SERVICE = "test_network";
}
+ public class Intent implements java.lang.Cloneable android.os.Parcelable {
+ field public static final String ACTION_SETTING_RESTORED = "android.os.action.SETTING_RESTORED";
+ field public static final String EXTRA_SETTING_NAME = "setting_name";
+ field public static final String EXTRA_SETTING_NEW_VALUE = "new_value";
+ field public static final String EXTRA_SETTING_PREVIOUS_VALUE = "previous_value";
+ field public static final String EXTRA_SETTING_RESTORED_FROM_SDK_INT = "restored_from_sdk_int";
+ }
+
}
package android.content.pm {
@@ -127,6 +135,7 @@
public final class BtProfileConnectionInfo implements android.os.Parcelable {
method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int);
+ method @NonNull public static android.media.BtProfileConnectionInfo a2dpSinkInfo(int);
method public int describeContents();
method public boolean getIsLeOutput();
method public int getProfile();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 41ce6f6..a8ef47e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -138,6 +138,7 @@
field public static final String MANAGE_APP_PREDICTIONS = "android.permission.MANAGE_APP_PREDICTIONS";
field public static final String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
field public static final String MANAGE_AUTO_FILL = "android.permission.MANAGE_AUTO_FILL";
+ field public static final String MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED = "android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED";
field public static final String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE";
field public static final String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
@@ -273,6 +274,7 @@
field public static final String SIGNAL_REBOOT_READINESS = "android.permission.SIGNAL_REBOOT_READINESS";
field public static final String SOUND_TRIGGER_RUN_IN_BATTERY_SAVER = "android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER";
field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
+ field public static final String START_CROSS_PROFILE_ACTIVITIES = "android.permission.START_CROSS_PROFILE_ACTIVITIES";
field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
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";
@@ -2264,6 +2266,25 @@
package android.bluetooth.le {
+ public final class AdvertiseSettings implements android.os.Parcelable {
+ method public int getOwnAddressType();
+ }
+
+ public static final class AdvertiseSettings.Builder {
+ method @NonNull public android.bluetooth.le.AdvertiseSettings.Builder setOwnAddressType(int);
+ }
+
+ public final class AdvertisingSetParameters implements android.os.Parcelable {
+ method public int getOwnAddressType();
+ field public static final int ADDRESS_TYPE_DEFAULT = -1; // 0xffffffff
+ field public static final int ADDRESS_TYPE_PUBLIC = 0; // 0x0
+ field public static final int ADDRESS_TYPE_RANDOM = 1; // 0x1
+ }
+
+ public static final class AdvertisingSetParameters.Builder {
+ method @NonNull public android.bluetooth.le.AdvertisingSetParameters.Builder setOwnAddressType(int);
+ }
+
public final class BluetoothLeScanner {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
@@ -5861,6 +5882,7 @@
public static final class TvInputManager.Hardware {
method public void overrideAudioSink(int, String, int, int, int);
+ method public void overrideAudioSink(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) int, int, int);
method public void setStreamVolume(float);
method public boolean setSurface(android.view.Surface, android.media.tv.TvStreamConfig);
}
@@ -7529,6 +7551,17 @@
public static class NetworkStats.Entry {
ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long);
+ method public int getDefaultNetwork();
+ method public int getMetered();
+ method public long getOperations();
+ method public int getRoaming();
+ method public long getRxBytes();
+ method public long getRxPackets();
+ method public int getSet();
+ method public int getTag();
+ method public long getTxBytes();
+ method public long getTxPackets();
+ method public int getUid();
}
public class RssiCurve implements android.os.Parcelable {
@@ -7565,6 +7598,7 @@
public class TrafficStats {
method public static void setThreadStatsTagApp();
method public static void setThreadStatsTagBackup();
+ method public static void setThreadStatsTagDownload();
method public static void setThreadStatsTagRestore();
field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = -113; // 0xffffff8f
field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = -128; // 0xffffff80
@@ -8487,6 +8521,7 @@
field public static final int EVENT_UNSPECIFIED = 0; // 0x0
field public static final int REASON_ACCOUNT_TRANSFER = 104; // 0x68
field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
+ field public static final int REASON_BLUETOOTH_BROADCAST = 203; // 0xcb
field public static final int REASON_GEOFENCING = 100; // 0x64
field public static final int REASON_LOCATION_PROVIDER = 312; // 0x138
field public static final int REASON_OTHER = 1; // 0x1
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 9b37457..f94ee85 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -28,8 +28,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.app.PropertyInvalidatedCache;
+import android.annotation.SystemApi; //import android.app.PropertyInvalidatedCache;
import android.bluetooth.BluetoothDevice.Transport;
import android.bluetooth.BluetoothProfile.ConnectionPolicy;
import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
@@ -676,14 +675,15 @@
"android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED";
/** The profile is in disconnected state */
- public static final int STATE_DISCONNECTED = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED;
+ public static final int STATE_DISCONNECTED =
+ 0; //BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED;
/** The profile is in connecting state */
- public static final int STATE_CONNECTING = BluetoothProtoEnums.CONNECTION_STATE_CONNECTING;
+ public static final int STATE_CONNECTING = 1; //BluetoothProtoEnums.CONNECTION_STATE_CONNECTING;
/** The profile is in connected state */
- public static final int STATE_CONNECTED = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED;
+ public static final int STATE_CONNECTED = 2; //BluetoothProtoEnums.CONNECTION_STATE_CONNECTED;
/** The profile is in disconnecting state */
public static final int STATE_DISCONNECTING =
- BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING;
+ 3; //BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING;
/** @hide */
public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
@@ -1044,6 +1044,7 @@
return false;
}
+ /*
private static final String BLUETOOTH_GET_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_state";
private final PropertyInvalidatedCache<Void, Integer> mBluetoothGetStateCache =
@@ -1059,17 +1060,22 @@
}
}
};
+ */
/** @hide */
+ /*
@RequiresNoPermission
public void disableBluetoothGetStateCache() {
mBluetoothGetStateCache.disableLocal();
}
+ */
/** @hide */
+ /*
public static void invalidateBluetoothGetStateCache() {
PropertyInvalidatedCache.invalidateCache(BLUETOOTH_GET_STATE_CACHE_PROPERTY);
}
+ */
/**
* Fetch the current bluetooth state. If the service is down, return
@@ -1081,14 +1087,12 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- state = mBluetoothGetStateCache.query(null);
+ //state = mBluetoothGetStateCache.query(null);
+ state = mService.getState();
}
- } catch (RuntimeException e) {
- if (e.getCause() instanceof RemoteException) {
- Log.e(TAG, "", e.getCause());
- } else {
- throw e;
- }
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ e.rethrowFromSystemServer();
} finally {
mServiceLock.readLock().unlock();
}
@@ -2100,6 +2104,7 @@
}
}
+ /*
private static final String BLUETOOTH_FILTERING_CACHE_PROPERTY =
"cache_key.bluetooth.is_offloaded_filtering_supported";
private final PropertyInvalidatedCache<Void, Boolean> mBluetoothFilteringCache =
@@ -2122,17 +2127,22 @@
}
};
+ */
/** @hide */
+ /*
@RequiresNoPermission
public void disableIsOffloadedFilteringSupportedCache() {
mBluetoothFilteringCache.disableLocal();
}
+ */
/** @hide */
+ /*
public static void invalidateIsOffloadedFilteringSupportedCache() {
PropertyInvalidatedCache.invalidateCache(BLUETOOTH_FILTERING_CACHE_PROPERTY);
}
+ */
/**
* Return true if offloaded filters are supported
@@ -2145,7 +2155,18 @@
if (!getLeAccess()) {
return false;
}
- return mBluetoothFilteringCache.query(null);
+ //return mBluetoothFilteringCache.query(null);
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.isOffloadedFilteringSupported();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
}
/**
@@ -2551,15 +2572,13 @@
return supportedProfiles;
}
+ /*
private static final String BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY =
"cache_key.bluetooth.get_adapter_connection_state";
private final PropertyInvalidatedCache<Void, Integer>
mBluetoothGetAdapterConnectionStateCache =
new PropertyInvalidatedCache<Void, Integer> (
8, BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY) {
- /**
- * This method must not be called when mService is null.
- */
@Override
@SuppressLint("AndroidFrameworkRequiresPermission")
protected Integer recompute(Void query) {
@@ -2570,18 +2589,23 @@
}
}
};
+ */
/** @hide */
+ /*
@RequiresNoPermission
public void disableGetAdapterConnectionStateCache() {
mBluetoothGetAdapterConnectionStateCache.disableLocal();
}
+ */
/** @hide */
+ /*
public static void invalidateGetAdapterConnectionStateCache() {
PropertyInvalidatedCache.invalidateCache(
BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY);
}
+ */
/**
* Get the current connection state of the local Bluetooth adapter.
@@ -2605,20 +2629,18 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mBluetoothGetAdapterConnectionStateCache.query(null);
+ return mService.getAdapterConnectionState();
}
- } catch (RuntimeException e) {
- if (e.getCause() instanceof RemoteException) {
- Log.e(TAG, "getConnectionState:", e.getCause());
- } else {
- throw e;
- }
+ //return mBluetoothGetAdapterConnectionStateCache.query(null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to getConnectionState, error: ", e);
} finally {
mServiceLock.readLock().unlock();
}
return BluetoothAdapter.STATE_DISCONNECTED;
}
+ /*
private static final String BLUETOOTH_PROFILE_CACHE_PROPERTY =
"cache_key.bluetooth.get_profile_connection_state";
private final PropertyInvalidatedCache<Integer, Integer>
@@ -2646,17 +2668,22 @@
query);
}
};
+ */
/** @hide */
+ /*
@RequiresNoPermission
public void disableGetProfileConnectionStateCache() {
mGetProfileConnectionStateCache.disableLocal();
}
+ */
/** @hide */
+ /*
public static void invalidateGetProfileConnectionStateCache() {
PropertyInvalidatedCache.invalidateCache(BLUETOOTH_PROFILE_CACHE_PROPERTY);
}
+ */
/**
* Get the current connection state of a profile.
@@ -2678,7 +2705,18 @@
if (getState() != STATE_ON) {
return BluetoothProfile.STATE_DISCONNECTED;
}
- return mGetProfileConnectionStateCache.query(new Integer(profile));
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ mService.getProfileConnectionState(profile);
+ }
+ //return mGetProfileConnectionStateCache.query(new Integer(profile));
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to getProfileConnectionState, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return BluetoothProfile.STATE_DISCONNECTED;
}
/**
@@ -3086,6 +3124,9 @@
BluetoothCsipSetCoordinator csipSetCoordinator =
new BluetoothCsipSetCoordinator(context, listener, this);
return true;
+ } else if (profile == BluetoothProfile.LE_CALL_CONTROL) {
+ BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener);
+ return true;
} else {
return false;
}
@@ -3188,6 +3229,10 @@
(BluetoothCsipSetCoordinator) proxy;
csipSetCoordinator.close();
break;
+ case BluetoothProfile.LE_CALL_CONTROL:
+ BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy;
+ tbs.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 93f0268..1edf5cc 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -23,8 +23,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.app.PropertyInvalidatedCache;
+import android.annotation.SystemApi; //import android.app.PropertyInvalidatedCache;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
import android.bluetooth.annotations.RequiresBluetoothScanPermission;
@@ -1597,6 +1596,7 @@
return false;
}
+ /*
private static final String BLUETOOTH_BONDING_CACHE_PROPERTY =
"cache_key.bluetooth.get_bond_state";
private final PropertyInvalidatedCache<BluetoothDevice, Integer> mBluetoothBondCache =
@@ -1612,16 +1612,19 @@
}
}
};
+ */
/** @hide */
- public void disableBluetoothGetBondStateCache() {
+ /* public void disableBluetoothGetBondStateCache() {
mBluetoothBondCache.disableLocal();
- }
+ } */
/** @hide */
+ /*
public static void invalidateBluetoothGetBondStateCache() {
PropertyInvalidatedCache.invalidateCache(BLUETOOTH_BONDING_CACHE_PROPERTY);
}
+ */
/**
* Get the bond state of the remote device.
@@ -1643,13 +1646,11 @@
return BOND_NONE;
}
try {
- return mBluetoothBondCache.query(this);
- } catch (RuntimeException e) {
- if (e.getCause() instanceof RemoteException) {
- Log.e(TAG, "", e);
- } else {
- throw e;
- }
+ //return mBluetoothBondCache.query(this);
+ return sService.getBondState(this, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to ", e);
+ e.rethrowFromSystemServer();
}
return BOND_NONE;
}
diff --git a/core/java/android/bluetooth/BluetoothLeCall.java b/core/java/android/bluetooth/BluetoothLeCall.java
new file mode 100644
index 0000000..fb7789d
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeCall.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.bluetooth;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelUuid;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Representation of Call
+ *
+ * @hide
+ */
+public final class BluetoothLeCall implements Parcelable {
+
+ /** @hide */
+ @IntDef(prefix = "STATE_", value = {
+ STATE_INCOMING,
+ STATE_DIALING,
+ STATE_ALERTING,
+ STATE_ACTIVE,
+ STATE_LOCALLY_HELD,
+ STATE_REMOTELY_HELD,
+ STATE_LOCALLY_AND_REMOTELY_HELD
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {
+ }
+
+ /**
+ * A remote party is calling (incoming call).
+ *
+ * @hide
+ */
+ public static final int STATE_INCOMING = 0x00;
+
+ /**
+ * The process to call the remote party has started but the remote party is not
+ * being alerted (outgoing call).
+ *
+ * @hide
+ */
+ public static final int STATE_DIALING = 0x01;
+
+ /**
+ * A remote party is being alerted (outgoing call).
+ *
+ * @hide
+ */
+ public static final int STATE_ALERTING = 0x02;
+
+ /**
+ * The call is in an active conversation.
+ *
+ * @hide
+ */
+ public static final int STATE_ACTIVE = 0x03;
+
+ /**
+ * The call is connected but held locally. “Locally Held” implies that either
+ * the server or the client can affect the state.
+ *
+ * @hide
+ */
+ public static final int STATE_LOCALLY_HELD = 0x04;
+
+ /**
+ * The call is connected but held remotely. “Remotely Held” means that the state
+ * is controlled by the remote party of a call.
+ *
+ * @hide
+ */
+ public static final int STATE_REMOTELY_HELD = 0x05;
+
+ /**
+ * The call is connected but held both locally and remotely.
+ *
+ * @hide
+ */
+ public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06;
+
+ /**
+ * Whether the call direction is outgoing.
+ *
+ * @hide
+ */
+ public static final int FLAG_OUTGOING_CALL = 0x00000001;
+
+ /**
+ * Whether the call URI and Friendly Name are withheld by server.
+ *
+ * @hide
+ */
+ public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002;
+
+ /**
+ * Whether the call URI and Friendly Name are withheld by network.
+ *
+ * @hide
+ */
+ public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004;
+
+ /** Unique UUID that identifies this call */
+ private UUID mUuid;
+
+ /** Remote Caller URI */
+ private String mUri;
+
+ /** Caller friendly name */
+ private String mFriendlyName;
+
+ /** Call state */
+ private @State int mState;
+
+ /** Call flags */
+ private int mCallFlags;
+
+ /** @hide */
+ public BluetoothLeCall(@NonNull BluetoothLeCall that) {
+ mUuid = new UUID(that.getUuid().getMostSignificantBits(),
+ that.getUuid().getLeastSignificantBits());
+ mUri = that.mUri;
+ mFriendlyName = that.mFriendlyName;
+ mState = that.mState;
+ mCallFlags = that.mCallFlags;
+ }
+
+ /** @hide */
+ public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName,
+ @State int state, int callFlags) {
+ mUuid = uuid;
+ mUri = uri;
+ mFriendlyName = friendlyName;
+ mState = state;
+ mCallFlags = callFlags;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ BluetoothLeCall that = (BluetoothLeCall) o;
+ return mUuid.equals(that.mUuid) && mUri.equals(that.mUri)
+ && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState
+ && mCallFlags == that.mCallFlags;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags);
+ }
+
+ /**
+ * Returns a string representation of this BluetoothLeCall.
+ *
+ * <p>
+ * Currently this is the UUID.
+ *
+ * @return string representation of this BluetoothLeCall
+ */
+ @Override
+ public String toString() {
+ return mUuid.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeParcelable(new ParcelUuid(mUuid), 0);
+ out.writeString(mUri);
+ out.writeString(mFriendlyName);
+ out.writeInt(mState);
+ out.writeInt(mCallFlags);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeCall> CREATOR =
+ new Parcelable.Creator<BluetoothLeCall>() {
+ public BluetoothLeCall createFromParcel(Parcel in) {
+ return new BluetoothLeCall(in);
+ }
+
+ public BluetoothLeCall[] newArray(int size) {
+ return new BluetoothLeCall[size];
+ }
+ };
+
+ private BluetoothLeCall(Parcel in) {
+ mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
+ mUri = in.readString();
+ mFriendlyName = in.readString();
+ mState = in.readInt();
+ mCallFlags = in.readInt();
+ }
+
+ /**
+ * Returns an UUID of this BluetoothLeCall.
+ *
+ * <p>
+ * An UUID is unique identifier of a BluetoothLeCall.
+ *
+ * @return UUID of this BluetoothLeCall
+ * @hide
+ */
+ public @NonNull UUID getUuid() {
+ return mUuid;
+ }
+
+ /**
+ * Returns a URI of the remote party of this BluetoothLeCall.
+ *
+ * @return string representation of this BluetoothLeCall
+ * @hide
+ */
+ public @NonNull String getUri() {
+ return mUri;
+ }
+
+ /**
+ * Returns a friendly name of the call.
+ *
+ * @return friendly name representation of this BluetoothLeCall
+ * @hide
+ */
+ public @NonNull String getFriendlyName() {
+ return mFriendlyName;
+ }
+
+ /**
+ * Returns the call state.
+ *
+ * @return the state of this BluetoothLeCall
+ * @hide
+ */
+ public @State int getState() {
+ return mState;
+ }
+
+ /**
+ * Returns the call flags.
+ *
+ * @return call flags
+ * @hide
+ */
+ public int getCallFlags() {
+ return mCallFlags;
+ }
+
+ /**
+ * Whether the call direction is incoming.
+ *
+ * @return true if incoming call, false otherwise
+ * @hide
+ */
+ public boolean isIncomingCall() {
+ return (mCallFlags & FLAG_OUTGOING_CALL) == 0;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothLeCallControl.java b/core/java/android/bluetooth/BluetoothLeCallControl.java
new file mode 100644
index 0000000..fb080c9
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeCallControl.java
@@ -0,0 +1,899 @@
+/*
+ * Copyright 2019 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.Log;
+import android.annotation.SuppressLint;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides the APIs to control the Call Control profile.
+ *
+ * <p>
+ * This class provides Bluetooth Telephone Bearer Service functionality,
+ * allowing applications to expose a GATT Service based interface to control the
+ * state of the calls by remote devices such as LE audio devices.
+ *
+ * <p>
+ * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
+ * BluetoothLeCallControl proxy object.
+ *
+ * @hide
+ */
+public final class BluetoothLeCallControl implements BluetoothProfile {
+ private static final String TAG = "BluetoothLeCallControl";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+
+ /** @hide */
+ @IntDef(prefix = "RESULT_", value = {
+ RESULT_SUCCESS,
+ RESULT_ERROR_UNKNOWN_CALL_ID,
+ RESULT_ERROR_INVALID_URI,
+ RESULT_ERROR_APPLICATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Result {
+ }
+
+ /**
+ * Opcode write was successful.
+ *
+ * @hide
+ */
+ public static final int RESULT_SUCCESS = 0;
+
+ /**
+ * Unknown call Id has been used in the operation.
+ *
+ * @hide
+ */
+ public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1;
+
+ /**
+ * The URI provided in {@link Callback#onPlaceCallRequest} is invalid.
+ *
+ * @hide
+ */
+ public static final int RESULT_ERROR_INVALID_URI = 2;
+
+ /**
+ * Application internal error.
+ *
+ * @hide
+ */
+ public static final int RESULT_ERROR_APPLICATION = 3;
+
+ /** @hide */
+ @IntDef(prefix = "TERMINATION_REASON_", value = {
+ TERMINATION_REASON_INVALID_URI,
+ TERMINATION_REASON_FAIL,
+ TERMINATION_REASON_REMOTE_HANGUP,
+ TERMINATION_REASON_SERVER_HANGUP,
+ TERMINATION_REASON_LINE_BUSY,
+ TERMINATION_REASON_NETWORK_CONGESTION,
+ TERMINATION_REASON_CLIENT_HANGUP,
+ TERMINATION_REASON_NO_SERVICE,
+ TERMINATION_REASON_NO_ANSWER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TerminationReason {
+ }
+
+ /**
+ * Remote Caller ID value used to place a call was formed improperly.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_INVALID_URI = 0x00;
+
+ /**
+ * Call fail.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_FAIL = 0x01;
+
+ /**
+ * Remote party ended call.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02;
+
+ /**
+ * Call ended from the server.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03;
+
+ /**
+ * Line busy.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_LINE_BUSY = 0x04;
+
+ /**
+ * Network congestion.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05;
+
+ /**
+ * Client terminated.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06;
+
+ /**
+ * No service.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_NO_SERVICE = 0x07;
+
+ /**
+ * No answer.
+ *
+ * @hide
+ */
+ public static final int TERMINATION_REASON_NO_ANSWER = 0x08;
+
+ /*
+ * Flag indicating support for hold/unhold call feature.
+ *
+ * @hide
+ */
+ public static final int CAPABILITY_HOLD_CALL = 0x00000001;
+
+ /**
+ * Flag indicating support for joining calls feature.
+ *
+ * @hide
+ */
+ public static final int CAPABILITY_JOIN_CALLS = 0x00000002;
+
+ private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102;
+ private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103;
+
+ private static final int REG_TIMEOUT = 10000;
+
+ /**
+ * The template class is used to call callback functions on events from the TBS
+ * server. Callback functions are wrapped in this class and registered to the
+ * Android system during app registration.
+ *
+ * @hide
+ */
+ public abstract static class Callback {
+
+ private static final String TAG = "BluetoothLeCallControl.Callback";
+
+ /**
+ * Called when a remote client requested to accept the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to be accepted
+ * @hide
+ */
+ public abstract void onAcceptCall(int requestId, @NonNull UUID callId);
+
+ /**
+ * A remote client has requested to terminate the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to terminate
+ * @hide
+ */
+ public abstract void onTerminateCall(int requestId, @NonNull UUID callId);
+
+ /**
+ * A remote client has requested to hold the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to be put on hold
+ * @hide
+ */
+ public void onHoldCall(int requestId, @NonNull UUID callId) {
+ Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
+ }
+
+ /**
+ * A remote client has requested to unhold the call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The call Id requested to unhold
+ * @hide
+ */
+ public void onUnholdCall(int requestId, @NonNull UUID callId) {
+ Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
+ }
+
+ /**
+ * A remote client has requested to place a call.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callId The Id to be assigned for the new call
+ * @param uri The caller URI requested
+ * @hide
+ */
+ public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri);
+
+ /**
+ * A remote client has requested to join the calls.
+ *
+ * <p>
+ * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+ * request.
+ *
+ * @param requestId The Id of the request
+ * @param callIds The call Id list requested to join
+ * @hide
+ */
+ public void onJoinCalls(int requestId, @NonNull List<UUID> callIds) {
+ Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!");
+ }
+ }
+
+ private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub {
+
+ private final Executor mExecutor;
+ private final Callback mCallback;
+
+ CallbackWrapper(Executor executor, Callback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onBearerRegistered(int ccid) {
+ if (mCallback != null) {
+ mCcid = ccid;
+ } else {
+ // registration timeout
+ Log.e(TAG, "onBearerRegistered: mCallback is null");
+ }
+ }
+
+ @Override
+ public void onAcceptCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onTerminateCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onHoldCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onUnholdCall(int requestId, ParcelUuid uuid) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid()));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) {
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
+ public void onJoinCalls(int requestId, List<ParcelUuid> parcelUuids) {
+ List<UUID> uuids = new ArrayList<>();
+ for (ParcelUuid parcelUuid : parcelUuids) {
+ uuids.add(parcelUuid.getUuid());
+ }
+
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+ };
+
+ private Context mContext;
+ private ServiceListener mServiceListener;
+ private volatile IBluetoothLeCallControl mService;
+ private BluetoothAdapter mAdapter;
+ private int mCcid = 0;
+ private String mToken;
+ private Callback mCallback = null;
+
+ private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (DBG)
+ Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ if (!up) {
+ doUnbind();
+ } else {
+ doBind();
+ }
+ }
+ };
+
+ /**
+ * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth
+ * telephone bearer service.
+ */
+ /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) {
+ mContext = context;
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mServiceListener = listener;
+
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ doBind();
+ }
+
+ private boolean doBind() {
+ synchronized (mConnection) {
+ if (mService == null) {
+ if (VDBG)
+ Log.d(TAG, "Binding service...");
+ try {
+ return mAdapter.getBluetoothManager().
+ bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
+ mConnection);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to bind TelephoneBearerService", e);
+ }
+ }
+ }
+ return false;
+ }
+
+ private void doUnbind() {
+ synchronized (mConnection) {
+ if (mService != null) {
+ if (VDBG)
+ Log.d(TAG, "Unbinding service...");
+ try {
+ mAdapter.getBluetoothManager().
+ unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
+ mConnection);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to unbind TelephoneBearerService", e);
+ } finally {
+ mService = null;
+ }
+ }
+ }
+ }
+
+ /* package */ void close() {
+ if (VDBG)
+ log("close()");
+ unregisterBearer();
+
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "", re);
+ }
+ }
+ mServiceListener = null;
+ doUnbind();
+ }
+
+ private IBluetoothLeCallControl getService() {
+ return mService;
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ public int getConnectionState(@Nullable BluetoothDevice device) {
+ throw new UnsupportedOperationException("not supported");
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
+ throw new UnsupportedOperationException("not supported");
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ @NonNull int[] states) {
+ throw new UnsupportedOperationException("not supported");
+ }
+
+ /**
+ * Register Telephone Bearer exposing the interface that allows remote devices
+ * to track and control the call states.
+ *
+ * <p>
+ * This is an asynchronous call. The callback is used to notify success or
+ * failure if the function returns true.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * <!-- The UCI is a String identifier of the telephone bearer as defined at
+ * https://www.bluetooth.com/specifications/assigned-numbers/uniform-caller-identifiers
+ * (login required). -->
+ *
+ * <!-- The examples of common URI schemes can be found in
+ * https://iana.org/assignments/uri-schemes/uri-schemes.xhtml -->
+ *
+ * <!-- The Technology is an integer value. The possible values are defined at
+ * https://www.bluetooth.com/specifications/assigned-numbers (login required).
+ * -->
+ *
+ * @param uci Bearer Unique Client Identifier
+ * @param uriSchemes URI Schemes supported list
+ * @param capabilities bearer capabilities
+ * @param provider Network provider name
+ * @param technology Network technology
+ * @param executor {@link Executor} object on which callback will be
+ * executed. The Executor object is required.
+ * @param callback {@link Callback} object to which callback messages will
+ * be sent. The Callback object is required.
+ * @return true on success, false otherwise
+ * @hide
+ */
+ @SuppressLint("ExecutorRegistration")
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean registerBearer(@Nullable String uci,
+ @NonNull List<String> uriSchemes, int capabilities,
+ @NonNull String provider, int technology,
+ @NonNull Executor executor, @NonNull Callback callback) {
+ if (DBG) {
+ Log.d(TAG, "registerBearer");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("null parameter: " + callback);
+ }
+ if (mCcid != 0) {
+ return false;
+ }
+
+ mToken = uci;
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ if (mCallback != null) {
+ Log.e(TAG, "Bearer can be opened only once");
+ return false;
+ }
+
+ mCallback = callback;
+ try {
+ CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback);
+ service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities,
+ provider, technology);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ mCallback = null;
+ return false;
+ }
+
+ if (mCcid == 0) {
+ mCallback = null;
+ return false;
+ }
+
+ return true;
+ }
+
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return false;
+ }
+
+ /**
+ * Unregister Telephone Bearer Service and destroy all the associated data.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void unregisterBearer() {
+ if (DBG) {
+ Log.d(TAG, "unregisterBearer");
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ int ccid = mCcid;
+ mCcid = 0;
+ mCallback = null;
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.unregisterBearer(mToken);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Get the Content Control ID (CCID) value.
+ *
+ * @return ccid Content Control ID value
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public int getContentControlId() {
+ return mCcid;
+ }
+
+ /**
+ * Notify about the newly added call.
+ *
+ * <p>
+ * This shall be called as early as possible after the call has been added.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param call Newly added call
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void onCallAdded(@NonNull BluetoothLeCall call) {
+ if (DBG) {
+ Log.d(TAG, "onCallAdded: call=" + call);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.callAdded(mCcid, call);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Notify about the removed call.
+ *
+ * <p>
+ * This shall be called as early as possible after the call has been removed.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callId The Id of a call that has been removed
+ * @param reason Call termination reason
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) {
+ if (DBG) {
+ Log.d(TAG, "callRemoved: callId=" + callId);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.callRemoved(mCcid, new ParcelUuid(callId), reason);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Notify the call state change
+ *
+ * <p>
+ * This shall be called as early as possible after the state of the call has
+ * changed.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param callId The call Id that state has been changed
+ * @param state Call state
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) {
+ if (DBG) {
+ Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.callStateChanged(mCcid, new ParcelUuid(callId), state);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Provide the current calls list
+ *
+ * <p>
+ * This function must be invoked after registration if application has any
+ * calls.
+ *
+ * @param calls current calls list
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void currentCallsList(@NonNull List<BluetoothLeCall> calls) {
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.currentCallsList(mCcid, calls);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ }
+
+ /**
+ * Provide the network current status
+ *
+ * <p>
+ * This function must be invoked on change of network state.
+ *
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * <!-- The Technology is an integer value. The possible values are defined at
+ * https://www.bluetooth.com/specifications/assigned-numbers (login required).
+ * -->
+ *
+ * @param provider Network provider name
+ * @param technology Network technology
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void networkStateChanged(@NonNull String provider, int technology) {
+ if (DBG) {
+ Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.networkStateChanged(mCcid, provider, technology);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ }
+
+ /**
+ * Send a response to a call control request to a remote device.
+ *
+ * <p>
+ * This function must be invoked in when a request is received by one of these
+ * callback methods:
+ *
+ * <ul>
+ * <li>{@link Callback#onAcceptCall}
+ * <li>{@link Callback#onTerminateCall}
+ * <li>{@link Callback#onHoldCall}
+ * <li>{@link Callback#onUnholdCall}
+ * <li>{@link Callback#onPlaceCall}
+ * <li>{@link Callback#onJoinCalls}
+ * </ul>
+ *
+ * @param requestId The ID of the request that was received with the callback
+ * @param result The result of the request to be sent to the remote devices
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void requestResult(int requestId, @Result int result) {
+ if (DBG) {
+ Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result);
+ }
+ if (mCcid == 0) {
+ return;
+ }
+
+ final IBluetoothLeCallControl service = getService();
+ if (service != null) {
+ try {
+ service.requestResult(mCcid, requestId, result);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ }
+
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ private static boolean isValidDevice(@Nullable BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+
+ private final IBluetoothProfileServiceConnection mConnection =
+ new IBluetoothProfileServiceConnection.Stub() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) {
+ Log.d(TAG, "Proxy object connected");
+ }
+ mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service));
+ mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) {
+ Log.d(TAG, "Proxy object disconnected");
+ }
+ doUnbind();
+ mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED));
+ }
+ };
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_TBS_SERVICE_CONNECTED: {
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL,
+ BluetoothLeCallControl.this);
+ }
+ break;
+ }
+ case MESSAGE_TBS_SERVICE_DISCONNECTED: {
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL);
+ }
+ break;
+ }
+ }
+ }
+ };
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index e047e5d..d0f74e9 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -240,12 +240,19 @@
int LE_AUDIO_BROADCAST = 26;
/**
+ * @hide
+ * Telephone Bearer Service from Call Control Profile
+ *
+ */
+ int LE_CALL_CONTROL = 27;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 26;
+ int MAX_PROFILE_ID = 27;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/OWNERS b/core/java/android/bluetooth/OWNERS
index fbee577..8e9d7b7 100644
--- a/core/java/android/bluetooth/OWNERS
+++ b/core/java/android/bluetooth/OWNERS
@@ -1,6 +1,4 @@
# Bug component: 27441
-rahulsabnis@google.com
sattiraju@google.com
-siyuanh@google.com
-zachoverflow@google.com
+baligh@google.com
diff --git a/core/java/android/bluetooth/le/AdvertiseSettings.java b/core/java/android/bluetooth/le/AdvertiseSettings.java
index 7129d76..c52a6ee 100644
--- a/core/java/android/bluetooth/le/AdvertiseSettings.java
+++ b/core/java/android/bluetooth/le/AdvertiseSettings.java
@@ -16,6 +16,9 @@
package android.bluetooth.le;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.bluetooth.le.AdvertisingSetParameters.AddressTypeStatus;
import android.os.Parcel;
import android.os.Parcelable;
@@ -70,17 +73,21 @@
*/
private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
+
private final int mAdvertiseMode;
private final int mAdvertiseTxPowerLevel;
private final int mAdvertiseTimeoutMillis;
private final boolean mAdvertiseConnectable;
+ private final int mOwnAddressType;
private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel,
- boolean advertiseConnectable, int advertiseTimeout) {
+ boolean advertiseConnectable, int advertiseTimeout,
+ @AddressTypeStatus int ownAddressType) {
mAdvertiseMode = advertiseMode;
mAdvertiseTxPowerLevel = advertiseTxPowerLevel;
mAdvertiseConnectable = advertiseConnectable;
mAdvertiseTimeoutMillis = advertiseTimeout;
+ mOwnAddressType = ownAddressType;
}
private AdvertiseSettings(Parcel in) {
@@ -88,6 +95,7 @@
mAdvertiseTxPowerLevel = in.readInt();
mAdvertiseConnectable = in.readInt() != 0;
mAdvertiseTimeoutMillis = in.readInt();
+ mOwnAddressType = in.readInt();
}
/**
@@ -118,12 +126,23 @@
return mAdvertiseTimeoutMillis;
}
+ /**
+ * @return the own address type for advertising
+ *
+ * @hide
+ */
+ @SystemApi
+ public @AddressTypeStatus int getOwnAddressType() {
+ return mOwnAddressType;
+ }
+
@Override
public String toString() {
return "Settings [mAdvertiseMode=" + mAdvertiseMode
+ ", mAdvertiseTxPowerLevel=" + mAdvertiseTxPowerLevel
+ ", mAdvertiseConnectable=" + mAdvertiseConnectable
- + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis + "]";
+ + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis
+ + ", mOwnAddressType=" + mOwnAddressType + "]";
}
@Override
@@ -137,6 +156,7 @@
dest.writeInt(mAdvertiseTxPowerLevel);
dest.writeInt(mAdvertiseConnectable ? 1 : 0);
dest.writeInt(mAdvertiseTimeoutMillis);
+ dest.writeInt(mOwnAddressType);
}
public static final @android.annotation.NonNull Parcelable.Creator<AdvertiseSettings> CREATOR =
@@ -160,6 +180,7 @@
private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM;
private int mTimeoutMillis = 0;
private boolean mConnectable = true;
+ private int mOwnAddressType = AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT;
/**
* Set advertise mode to control the advertising power and latency.
@@ -226,10 +247,31 @@
}
/**
+ * Set own address type for advertising to control public or privacy mode. If used to set
+ * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT},
+ * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the
+ * time of starting advertising.
+ *
+ * @throws IllegalArgumentException If the {@code ownAddressType} is invalid
+ *
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) {
+ if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
+ || ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) {
+ throw new IllegalArgumentException("unknown address type " + ownAddressType);
+ }
+ mOwnAddressType = ownAddressType;
+ return this;
+ }
+
+ /**
* Build the {@link AdvertiseSettings} object.
*/
public AdvertiseSettings build() {
- return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis);
+ return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis,
+ mOwnAddressType);
}
}
}
diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
index e39b198..5c8fae6 100644
--- a/core/java/android/bluetooth/le/AdvertisingSetParameters.java
+++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
@@ -16,11 +16,17 @@
package android.bluetooth.le;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* The {@link AdvertisingSetParameters} provide a way to adjust advertising
* preferences for each
@@ -97,6 +103,39 @@
*/
private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
+ /** @hide */
+ @IntDef(prefix = "ADDRESS_TYPE_", value = {
+ ADDRESS_TYPE_DEFAULT,
+ ADDRESS_TYPE_PUBLIC,
+ ADDRESS_TYPE_RANDOM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AddressTypeStatus {}
+
+ /**
+ * Advertise own address type that corresponds privacy settings of the device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ADDRESS_TYPE_DEFAULT = -1;
+
+ /**
+ * Advertise own public address type.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ADDRESS_TYPE_PUBLIC = 0;
+
+ /**
+ * Generate and adverise own resolvable private address.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ADDRESS_TYPE_RANDOM = 1;
+
private final boolean mIsLegacy;
private final boolean mIsAnonymous;
private final boolean mIncludeTxPower;
@@ -106,11 +145,12 @@
private final boolean mScannable;
private final int mInterval;
private final int mTxPowerLevel;
+ private final int mOwnAddressType;
private AdvertisingSetParameters(boolean connectable, boolean scannable, boolean isLegacy,
boolean isAnonymous, boolean includeTxPower,
int primaryPhy, int secondaryPhy,
- int interval, int txPowerLevel) {
+ int interval, int txPowerLevel, @AddressTypeStatus int ownAddressType) {
mConnectable = connectable;
mScannable = scannable;
mIsLegacy = isLegacy;
@@ -120,6 +160,7 @@
mSecondaryPhy = secondaryPhy;
mInterval = interval;
mTxPowerLevel = txPowerLevel;
+ mOwnAddressType = ownAddressType;
}
private AdvertisingSetParameters(Parcel in) {
@@ -132,6 +173,7 @@
mSecondaryPhy = in.readInt();
mInterval = in.readInt();
mTxPowerLevel = in.readInt();
+ mOwnAddressType = in.readInt();
}
/**
@@ -197,6 +239,16 @@
return mTxPowerLevel;
}
+ /**
+ * @return the own address type for advertising
+ *
+ * @hide
+ */
+ @SystemApi
+ public @AddressTypeStatus int getOwnAddressType() {
+ return mOwnAddressType;
+ }
+
@Override
public String toString() {
return "AdvertisingSetParameters [connectable=" + mConnectable
@@ -206,7 +258,8 @@
+ ", primaryPhy=" + mPrimaryPhy
+ ", secondaryPhy=" + mSecondaryPhy
+ ", interval=" + mInterval
- + ", txPowerLevel=" + mTxPowerLevel + "]";
+ + ", txPowerLevel=" + mTxPowerLevel
+ + ", ownAddressType=" + mOwnAddressType + "]";
}
@Override
@@ -225,6 +278,7 @@
dest.writeInt(mSecondaryPhy);
dest.writeInt(mInterval);
dest.writeInt(mTxPowerLevel);
+ dest.writeInt(mOwnAddressType);
}
public static final @android.annotation.NonNull Parcelable.Creator<AdvertisingSetParameters> CREATOR =
@@ -253,6 +307,7 @@
private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M;
private int mInterval = INTERVAL_LOW;
private int mTxPowerLevel = TX_POWER_MEDIUM;
+ private int mOwnAddressType = ADDRESS_TYPE_DEFAULT;
/**
* Set whether the advertisement type should be connectable or
@@ -399,6 +454,26 @@
}
/**
+ * Set own address type for advertising to control public or privacy mode. If used to set
+ * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT},
+ * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the
+ * time of starting advertising.
+ *
+ * @throws IllegalArgumentException If the {@code ownAddressType} is invalid
+ *
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) {
+ if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
+ || ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) {
+ throw new IllegalArgumentException("unknown address type " + ownAddressType);
+ }
+ mOwnAddressType = ownAddressType;
+ return this;
+ }
+
+ /**
* Build the {@link AdvertisingSetParameters} object.
*
* @throws IllegalStateException if invalid combination of parameters is used.
@@ -431,7 +506,8 @@
}
return new AdvertisingSetParameters(mConnectable, mScannable, mIsLegacy, mIsAnonymous,
- mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel);
+ mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel,
+ mOwnAddressType);
}
}
}
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index b9f8a57..879dcee 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -138,6 +138,7 @@
parameters.setLegacyMode(true);
parameters.setConnectable(isConnectable);
parameters.setScannable(true); // legacy advertisements we support are always scannable
+ parameters.setOwnAddressType(settings.getOwnAddressType());
if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) {
parameters.setInterval(1600); // 1s
} else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9659df6..8a5e097 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4206,9 +4206,9 @@
* restored from (corresponds to {@link android.os.Build.VERSION#SDK_INT}). The first three
* values are represented as strings, the fourth one as int.
*
- * <p>This broadcast is sent only for settings provider entries known to require special handling
- * around restore time. These entries are found in the BROADCAST_ON_RESTORE table within
- * the provider's backup agent implementation.
+ * <p>This broadcast is sent only for settings provider entries known to require special
+ * handling around restore time to specific receivers. These entries are found in the
+ * BROADCAST_ON_RESTORE table within the provider's backup agent implementation.
*
* @see #EXTRA_SETTING_NAME
* @see #EXTRA_SETTING_PREVIOUS_VALUE
@@ -4216,15 +4216,46 @@
* @see #EXTRA_SETTING_RESTORED_FROM_SDK_INT
* {@hide}
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("ActionValue")
public static final String ACTION_SETTING_RESTORED = "android.os.action.SETTING_RESTORED";
- /** {@hide} */
+ /**
+ * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+ * Contain the name of the restored setting.
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("ActionValue")
public static final String EXTRA_SETTING_NAME = "setting_name";
- /** {@hide} */
+
+ /**
+ * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+ * Contain the value of the {@link EXTRA_SETTING_NAME} settings entry prior to the restore
+ * operation.
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("ActionValue")
public static final String EXTRA_SETTING_PREVIOUS_VALUE = "previous_value";
- /** {@hide} */
+
+ /**
+ * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+ * Contain the value of the {@link EXTRA_SETTING_NAME} settings entry being restored.
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("ActionValue")
public static final String EXTRA_SETTING_NEW_VALUE = "new_value";
- /** {@hide} */
+
+ /**
+ * Int intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+ * Contain the version of the SDK that the setting has been restored from (corresponds to
+ * {@link android.os.Build.VERSION#SDK_INT}).
+ * {@hide}
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("ActionValue")
public static final String EXTRA_SETTING_RESTORED_FROM_SDK_INT = "restored_from_sdk_int";
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2ed00b5..7cd7e7ac 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2485,6 +2485,8 @@
* API shipped in Android 11.
* <li><code>202101</code>: corresponds to the features included in the Identity Credential
* API shipped in Android 12.
+ * <li><code>202201</code>: corresponds to the features included in the Identity Credential
+ * API shipped in Android 13.
* </ul>
*/
@SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index c8c122d..9fb70d6 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -38,6 +38,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import android.util.Log;
@@ -653,8 +654,8 @@
/**
* A wrapper class for the cryptographic operations supported by BiometricPrompt.
*
- * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, and
- * {@link IdentityCredential}.
+ * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
+ * {@link IdentityCredential}, and {@link PresentationSession}.
*
* <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
* time-based. This is specified during key creation via the timeout parameter of the
@@ -684,10 +685,21 @@
super(mac);
}
+ /**
+ * Create from a {@link IdentityCredential} object.
+ *
+ * @param credential a {@link IdentityCredential} object.
+ * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
+ */
+ @Deprecated
public CryptoObject(@NonNull IdentityCredential credential) {
super(credential);
}
+ public CryptoObject(@NonNull PresentationSession session) {
+ super(session);
+ }
+
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
@@ -715,10 +727,20 @@
/**
* Get {@link IdentityCredential} object.
* @return {@link IdentityCredential} object or null if this doesn't contain one.
+ * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
*/
+ @Deprecated
public @Nullable IdentityCredential getIdentityCredential() {
return super.getIdentityCredential();
}
+
+ /**
+ * Get {@link PresentationSession} object.
+ * @return {@link PresentationSession} object or null if this doesn't contain one.
+ */
+ public @Nullable PresentationSession getPresentationSession() {
+ return super.getPresentationSession();
+ }
}
/**
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 7648cf2..d415706 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
import android.security.keystore2.AndroidKeyStoreProvider;
import java.security.Signature;
@@ -27,8 +28,8 @@
/**
* A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
- * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac} and
- * {@link IdentityCredential} objects.
+ * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
+ * {@link IdentityCredential}, and {@link PresentationSession} objects.
* @hide
*/
public class CryptoObject {
@@ -46,10 +47,21 @@
mCrypto = mac;
}
+ /**
+ * Create from a {@link IdentityCredential} object.
+ *
+ * @param credential a {@link IdentityCredential} object.
+ * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
+ */
+ @Deprecated
public CryptoObject(@NonNull IdentityCredential credential) {
mCrypto = credential;
}
+ public CryptoObject(@NonNull PresentationSession session) {
+ mCrypto = session;
+ }
+
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
@@ -77,12 +89,22 @@
/**
* Get {@link IdentityCredential} object.
* @return {@link IdentityCredential} object or null if this doesn't contain one.
+ * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
*/
+ @Deprecated
public IdentityCredential getIdentityCredential() {
return mCrypto instanceof IdentityCredential ? (IdentityCredential) mCrypto : null;
}
/**
+ * Get {@link PresentationSession} object.
+ * @return {@link PresentationSession} object or null if this doesn't contain one.
+ */
+ public PresentationSession getPresentationSession() {
+ return mCrypto instanceof PresentationSession ? (PresentationSession) mCrypto : null;
+ }
+
+ /**
* @hide
* @return the opId associated with this object or 0 if none
*/
@@ -91,6 +113,8 @@
return 0;
} else if (mCrypto instanceof IdentityCredential) {
return ((IdentityCredential) mCrypto).getCredstoreOperationHandle();
+ } else if (mCrypto instanceof PresentationSession) {
+ return ((PresentationSession) mCrypto).getCredstoreOperationHandle();
}
return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index a3d595c..480923e 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -58,6 +58,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
import android.util.Slog;
import android.view.Surface;
@@ -264,10 +265,21 @@
* Get {@link IdentityCredential} object.
* @return {@link IdentityCredential} object or null if this doesn't contain one.
* @hide
+ * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
*/
+ @Deprecated
public IdentityCredential getIdentityCredential() {
return super.getIdentityCredential();
}
+
+ /**
+ * Get {@link PresentationSession} object.
+ * @return {@link PresentationSession} object or null if this doesn't contain one.
+ * @hide
+ */
+ public PresentationSession getPresentationSession() {
+ return super.getPresentationSession();
+ }
}
/**
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index f50aa99..147138e 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -69,6 +69,8 @@
int getMultipathPreference(in Network network);
+ SubscriptionPlan getSubscriptionPlan(in NetworkTemplate template);
+ void onStatsProviderWarningOrLimitReached();
SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
String getSubscriptionPlansOwner(int subId);
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index fab692c..5554137 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -35,6 +35,7 @@
import android.annotation.RequiresFeature;
import android.content.pm.PackageManager;
import android.security.Credentials;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.VpnProfile;
@@ -70,6 +71,7 @@
* Exchange, Version 2 (IKEv2)</a>
*/
public final class Ikev2VpnProfile extends PlatformVpnProfile {
+ private static final String TAG = Ikev2VpnProfile.class.getSimpleName();
/** Prefix for when a Private Key is an alias to look for in KeyStore @hide */
public static final String PREFIX_KEYSTORE_ALIAS = "KEYSTORE_ALIAS:";
/** Prefix for when a Private Key is stored directly in the profile @hide */
@@ -163,6 +165,10 @@
// UnmodifiableList doesn't make a defensive copy by default.
mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms));
+ if (excludeLocalRoutes && !isBypassable) {
+ throw new IllegalArgumentException(
+ "Vpn should be byassable if excludeLocalRoutes is set");
+ }
mIsBypassable = isBypassable;
mIsMetered = isMetered;
@@ -520,7 +526,10 @@
throw new IllegalArgumentException("Invalid auth method set");
}
- builder.setExcludeLocalRoutes(profile.excludeLocalRoutes);
+ if (profile.excludeLocalRoutes && !profile.isBypassable) {
+ Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN");
+ }
+ builder.setExcludeLocalRoutes(profile.excludeLocalRoutes && profile.isBypassable);
return builder.build();
}
@@ -907,9 +916,23 @@
}
/**
- * Sets whether the local traffic is exempted from the VPN.
+ * Sets whether the local traffic is exempted from the VPN.
*
- * @hide TODO(184750836): unhide once the implementation is completed
+ * When this is set, the system will not use the VPN network when an app
+ * tries to send traffic for an IP address that is on a local network.
+ *
+ * Note that there are important security implications. In particular, the
+ * networks that the device connects to typically decides what IP addresses
+ * are part of the local network. This means that for VPNs setting this
+ * flag, it is possible for anybody to set up a public network in such a
+ * way that traffic to arbitrary IP addresses will bypass the VPN, including
+ * traffic to services like DNS. When using this API, please consider the
+ * security implications for your particular case.
+ *
+ * Note that because the local traffic will always bypass the VPN,
+ * it is not possible to set this flag on a non-bypassable VPN.
+ *
+ * @hide TODO(184750836): unhide once the implementation is completed
*/
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.java b/core/java/android/net/InternalNetworkUpdateRequest.java
index 6f09383..f42c4b7 100644
--- a/core/java/android/net/InternalNetworkUpdateRequest.java
+++ b/core/java/android/net/InternalNetworkUpdateRequest.java
@@ -17,7 +17,6 @@
package android.net;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,7 +26,7 @@
public final class InternalNetworkUpdateRequest implements Parcelable {
@NonNull
private final StaticIpConfiguration mIpConfig;
- @Nullable
+ @NonNull
private final NetworkCapabilities mNetworkCapabilities;
@NonNull
@@ -37,20 +36,16 @@
@NonNull
public NetworkCapabilities getNetworkCapabilities() {
- return mNetworkCapabilities == null
- ? null : new NetworkCapabilities(mNetworkCapabilities);
+ return new NetworkCapabilities(mNetworkCapabilities);
}
/** @hide */
public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
- @Nullable final NetworkCapabilities networkCapabilities) {
+ @NonNull final NetworkCapabilities networkCapabilities) {
Objects.requireNonNull(ipConfig);
+ Objects.requireNonNull(networkCapabilities);
mIpConfig = new StaticIpConfiguration(ipConfig);
- if (null == networkCapabilities) {
- mNetworkCapabilities = null;
- } else {
- mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
- }
+ mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
}
private InternalNetworkUpdateRequest(@NonNull final Parcel source) {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 7ebb646..426fc61 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -535,6 +535,46 @@
}
/**
+ * Get subscription plan for the given networkTemplate.
+ *
+ * @param template the networkTemplate to get the subscription plan for.
+ * @return the active {@link SubscriptionPlan} for the given template, or
+ * {@code null} if not found.
+ * @hide
+ */
+ @Nullable
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ // @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
+ try {
+ return mService.getSubscriptionPlan(template);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+ * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
+ * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ // @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void onStatsProviderWarningOrLimitReached() {
+ try {
+ mService.onStatsProviderWarningOrLimitReached();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Resets network policy settings back to factory defaults.
*
* @hide
diff --git a/core/java/android/net/PrivateDnsConnectivityChecker.java b/core/java/android/net/PrivateDnsConnectivityChecker.java
index cfd458c..ac97b36 100644
--- a/core/java/android/net/PrivateDnsConnectivityChecker.java
+++ b/core/java/android/net/PrivateDnsConnectivityChecker.java
@@ -44,7 +44,7 @@
*/
public static boolean canConnectToPrivateDnsServer(@NonNull String hostname) {
final SocketFactory factory = SSLSocketFactory.getDefault();
- TrafficStats.setThreadStatsTag(TrafficStats.TAG_SYSTEM_APP);
+ TrafficStats.setThreadStatsTagApp();
try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
socket.setSoTimeout(CONNECTION_TIMEOUT_MS);
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index 5c28553..3193826 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -96,6 +96,141 @@
*/
public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
+ /**
+ * Action sent in the intent when an error occurred.
+ *
+ * @hide
+ */
+ public static final String ACTION_VPN_MANAGER_ERROR = "android.net.action.VPN_MANAGER_ERROR";
+
+ /**
+ * An IKE protocol error. Codes are the codes from IkeProtocolException, RFC 7296.
+ *
+ * @hide
+ */
+ public static final String CATEGORY_ERROR_IKE = "android.net.category.ERROR_IKE";
+
+ /**
+ * User deactivated the VPN, either by turning it off or selecting a different VPN provider.
+ * The error code is always 0.
+ *
+ * @hide
+ */
+ public static final String CATEGORY_ERROR_USER_DEACTIVATED =
+ "android.net.category.ERROR_USER_DEACTIVATED";
+
+ /**
+ * Network error. Error codes are ERROR_CODE_NETWORK_*.
+ *
+ * @hide
+ */
+ public static final String CATEGORY_ERROR_NETWORK = "android.net.category.ERROR_NETWORK";
+
+ /**
+ * The key of the session that experienced this error, as returned by
+ * startProvisionedVpnProfileSession.
+ *
+ * @hide
+ */
+ public static final String EXTRA_SESSION_KEY = "android.net.extra.SESSION_KEY";
+
+ /**
+ * Extra for the Network object that was the underlying network at the time of the failure, or
+ * null if none.
+ *
+ * @hide
+ */
+ public static final String EXTRA_UNDERLYING_NETWORK = "android.net.extra.UNDERLYING_NETWORK";
+
+ /**
+ * The NetworkCapabilities of the underlying network.
+ *
+ * @hide
+ */
+ public static final String EXTRA_UNDERLYING_NETWORK_CAPABILITIES =
+ "android.net.extra.UNDERLYING_NETWORK_CAPABILITIES";
+
+ /**
+ * The LinkProperties of the underlying network.
+ *
+ * @hide
+ */
+ public static final String EXTRA_UNDERLYING_LINK_PROPERTIES =
+ "android.net.extra.UNDERLYING_LINK_PROPERTIES";
+
+ /**
+ * A long timestamp with SystemClock.elapsedRealtime base for when the event happened.
+ *
+ * @hide
+ */
+ public static final String EXTRA_TIMESTAMP = "android.net.extra.TIMESTAMP";
+
+ /**
+ * Extra for the error type. This is ERROR_NOT_RECOVERABLE or ERROR_RECOVERABLE.
+ *
+ * @hide
+ */
+ public static final String EXTRA_ERROR_TYPE = "android.net.extra.ERROR_TYPE";
+
+ /**
+ * Extra for the error code. The value will be 0 for CATEGORY_ERROR_USER_DEACTIVATED, one of
+ * ERROR_CODE_NETWORK_* for ERROR_CATEGORY_NETWORK or one of values defined in
+ * IkeProtocolException#ErrorType for CATEGORY_ERROR_IKE.
+ *
+ * @hide
+ */
+ public static final String EXTRA_ERROR_CODE = "android.net.extra.ERROR_CODE";
+
+ /**
+ * This error is fatal, e.g. the VPN was disabled or configuration error. The stack will not
+ * retry connection.
+ *
+ * @hide
+ */
+ public static final int ERROR_NOT_RECOVERABLE = 1;
+
+ /**
+ * The stack experienced an error but will retry with exponential backoff, e.g. network timeout.
+ *
+ * @hide
+ */
+ public static final int ERROR_RECOVERABLE = 2;
+
+ /**
+ * An error code to indicate that there was an UnknownHostException.
+ *
+ * @hide
+ */
+ public static final int ERROR_CODE_NETWORK_UNKNOWN_HOST = 0;
+
+ /**
+ * An error code to indicate that there is a SocketTimeoutException.
+ *
+ * @hide
+ */
+ public static final int ERROR_CODE_NETWORK_TIMEOUT = 1;
+
+ /**
+ * An error code to indicate that the connection is refused.
+ *
+ * @hide
+ */
+ public static final int ERROR_CODE_NETWORK_CONNECT = 2;
+
+ /**
+ * An error code to indicate the connection was reset. (e.g. SocketException)
+ *
+ * @hide
+ */
+ public static final int ERROR_CODE_NETWORK_CONNECTION_RESET = 3;
+
+ /**
+ * An error code to indicate that there is an IOException.
+ *
+ * @hide
+ */
+ public static final int ERROR_CODE_NETWORK_IO = 4;
+
/** @hide */
@IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY,
TYPE_VPN_OEM})
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 2ced056..1ae1b05 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -41,6 +41,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.NetworkUtilsInternal;
import com.android.internal.net.VpnConfig;
@@ -50,6 +51,7 @@
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -471,6 +473,13 @@
}
}
+ private static void checkNonPrefixBytes(@NonNull InetAddress address, int prefixLength) {
+ final IpPrefix prefix = new IpPrefix(address, prefixLength);
+ if (!prefix.getAddress().equals(address)) {
+ throw new IllegalArgumentException("Bad address");
+ }
+ }
+
/**
* Helper class to create a VPN interface. This class should be always
* used within the scope of the outer {@link VpnService}.
@@ -481,9 +490,9 @@
private final VpnConfig mConfig = new VpnConfig();
@UnsupportedAppUsage
- private final List<LinkAddress> mAddresses = new ArrayList<LinkAddress>();
+ private final List<LinkAddress> mAddresses = new ArrayList<>();
@UnsupportedAppUsage
- private final List<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
+ private final List<RouteInfo> mRoutes = new ArrayList<>();
public Builder() {
mConfig.user = VpnService.this.getClass().getName();
@@ -555,7 +564,6 @@
throw new IllegalArgumentException("Bad address");
}
mAddresses.add(new LinkAddress(address, prefixLength));
- mConfig.updateAllowedFamilies(address);
return this;
}
@@ -579,28 +587,68 @@
* Add a network route to the VPN interface. Both IPv4 and IPv6
* routes are supported.
*
+ * If a route with the same destination is already present, its type will be updated.
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ @NonNull
+ private Builder addRoute(@NonNull IpPrefix prefix, int type) {
+ check(prefix.getAddress(), prefix.getPrefixLength());
+
+ final RouteInfo newRoute = new RouteInfo(prefix, /* gateway */
+ null, /* interface */ null, type);
+
+ final int index = findRouteIndexByDestination(newRoute);
+
+ if (index == -1) {
+ mRoutes.add(newRoute);
+ } else {
+ mRoutes.set(index, newRoute);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a network route to the VPN interface. Both IPv4 and IPv6
+ * routes are supported.
+ *
* Adding a route implicitly allows traffic from that address family
* (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
*
+ * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
* @throws IllegalArgumentException if the route is invalid.
*/
@NonNull
public Builder addRoute(@NonNull InetAddress address, int prefixLength) {
- check(address, prefixLength);
+ checkNonPrefixBytes(address, prefixLength);
- int offset = prefixLength / 8;
- byte[] bytes = address.getAddress();
- if (offset < bytes.length) {
- for (bytes[offset] <<= prefixLength % 8; offset < bytes.length; ++offset) {
- if (bytes[offset] != 0) {
- throw new IllegalArgumentException("Bad address");
- }
- }
- }
- mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null, null,
- RouteInfo.RTN_UNICAST));
- mConfig.updateAllowedFamilies(address);
- return this;
+ return addRoute(new IpPrefix(address, prefixLength), RouteInfo.RTN_UNICAST);
+ }
+
+ /**
+ * Add a network route to the VPN interface. Both IPv4 and IPv6
+ * routes are supported.
+ *
+ * Adding a route implicitly allows traffic from that address family
+ * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
+ *
+ * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ @NonNull
+ public Builder addRoute(@NonNull IpPrefix prefix) {
+ return addRoute(prefix, RouteInfo.RTN_UNICAST);
}
/**
@@ -611,6 +659,12 @@
* Adding a route implicitly allows traffic from that address family
* (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
*
+ * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
* @throws IllegalArgumentException if the route is invalid.
* @see #addRoute(InetAddress, int)
*/
@@ -620,6 +674,23 @@
}
/**
+ * Exclude a network route from the VPN interface. Both IPv4 and IPv6
+ * routes are supported.
+ *
+ * Calling this method overrides previous calls to {@link #addRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ @NonNull
+ public Builder excludeRoute(@NonNull IpPrefix prefix) {
+ return addRoute(prefix, RouteInfo.RTN_THROW);
+ }
+
+ /**
* Add a DNS server to the VPN connection. Both IPv4 and IPv6
* addresses are supported. If none is set, the DNS servers of
* the default network will be used.
@@ -900,5 +971,23 @@
throw new IllegalStateException(e);
}
}
+
+ private int findRouteIndexByDestination(RouteInfo route) {
+ for (int i = 0; i < mRoutes.size(); i++) {
+ if (mRoutes.get(i).getDestination().equals(route.getDestination())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Method for testing, to observe mRoutes while builder is being used.
+ * @hide
+ */
+ @VisibleForTesting
+ public List<RouteInfo> routes() {
+ return Collections.unmodifiableList(mRoutes);
+ }
}
}
diff --git a/core/java/android/net/annotations/PolicyDirection.java b/core/java/android/net/annotations/PolicyDirection.java
index febd9b4..3f7521a 100644
--- a/core/java/android/net/annotations/PolicyDirection.java
+++ b/core/java/android/net/annotations/PolicyDirection.java
@@ -24,10 +24,6 @@
/**
* IPsec traffic direction.
- *
- * <p>Mainline modules cannot reference hidden @IntDef. Moving this annotation to a separate class
- * to allow others to statically include it.
- *
* @hide
*/
@IntDef(value = {IpSecManager.DIRECTION_IN, IpSecManager.DIRECTION_OUT})
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index 1ac3f0a..69e6313 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -15,6 +15,9 @@
*/
package android.net.vcn;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.getMatchCriteriaString;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
@@ -23,8 +26,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.net.NetworkCapabilities;
+import android.net.vcn.VcnUnderlyingNetworkTemplate.MatchCriteria;
import android.os.PersistableBundle;
import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
@@ -37,32 +44,45 @@
import java.util.Objects;
import java.util.Set;
-// TODO: Add documents
-/** @hide */
+/**
+ * This class represents a configuration for a network template class of underlying cellular
+ * networks.
+ *
+ * <p>See {@link VcnUnderlyingNetworkTemplate}
+ */
public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds";
@NonNull private final Set<String> mAllowedNetworkPlmnIds;
private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds";
@NonNull private final Set<Integer> mAllowedSpecificCarrierIds;
- private static final String ALLOW_ROAMING_KEY = "mAllowRoaming";
- private final boolean mAllowRoaming;
+ private static final String ROAMING_MATCH_KEY = "mRoamingMatchCriteria";
+ private final int mRoamingMatchCriteria;
- private static final String REQUIRE_OPPORTUNISTIC_KEY = "mRequireOpportunistic";
- private final boolean mRequireOpportunistic;
+ private static final String OPPORTUNISTIC_MATCH_KEY = "mOpportunisticMatchCriteria";
+ private final int mOpportunisticMatchCriteria;
private VcnCellUnderlyingNetworkTemplate(
- int networkQuality,
- boolean allowMetered,
+ int meteredMatchCriteria,
+ int minEntryUpstreamBandwidthKbps,
+ int minExitUpstreamBandwidthKbps,
+ int minEntryDownstreamBandwidthKbps,
+ int minExitDownstreamBandwidthKbps,
Set<String> allowedNetworkPlmnIds,
Set<Integer> allowedSpecificCarrierIds,
- boolean allowRoaming,
- boolean requireOpportunistic) {
- super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, allowMetered);
+ int roamingMatchCriteria,
+ int opportunisticMatchCriteria) {
+ super(
+ NETWORK_PRIORITY_TYPE_CELL,
+ meteredMatchCriteria,
+ minEntryUpstreamBandwidthKbps,
+ minExitUpstreamBandwidthKbps,
+ minEntryDownstreamBandwidthKbps,
+ minExitDownstreamBandwidthKbps);
mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds);
mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds);
- mAllowRoaming = allowRoaming;
- mRequireOpportunistic = requireOpportunistic;
+ mRoamingMatchCriteria = roamingMatchCriteria;
+ mOpportunisticMatchCriteria = opportunisticMatchCriteria;
validate();
}
@@ -72,15 +92,17 @@
protected void validate() {
super.validate();
validatePlmnIds(mAllowedNetworkPlmnIds);
- Objects.requireNonNull(mAllowedSpecificCarrierIds, "allowedCarrierIds is null");
+ Objects.requireNonNull(mAllowedSpecificCarrierIds, "matchingCarrierIds is null");
+ validateMatchCriteria(mRoamingMatchCriteria, "mRoamingMatchCriteria");
+ validateMatchCriteria(mOpportunisticMatchCriteria, "mOpportunisticMatchCriteria");
}
- private static void validatePlmnIds(Set<String> allowedNetworkPlmnIds) {
- Objects.requireNonNull(allowedNetworkPlmnIds, "allowedNetworkPlmnIds is null");
+ private static void validatePlmnIds(Set<String> matchingOperatorPlmnIds) {
+ Objects.requireNonNull(matchingOperatorPlmnIds, "matchingOperatorPlmnIds is null");
// A valid PLMN is a concatenation of MNC and MCC, and thus consists of 5 or 6 decimal
// digits.
- for (String id : allowedNetworkPlmnIds) {
+ for (String id : matchingOperatorPlmnIds) {
if ((id.length() == 5 || id.length() == 6) && id.matches("[0-9]+")) {
continue;
} else {
@@ -96,8 +118,16 @@
@NonNull PersistableBundle in) {
Objects.requireNonNull(in, "PersistableBundle is null");
- final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
- final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
+ final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
+
+ final int minEntryUpstreamBandwidthKbps =
+ in.getInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minExitUpstreamBandwidthKbps =
+ in.getInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minEntryDownstreamBandwidthKbps =
+ in.getInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minExitDownstreamBandwidthKbps =
+ in.getInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
final PersistableBundle plmnIdsBundle =
in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY);
@@ -114,16 +144,19 @@
PersistableBundleUtils.toList(
specificCarrierIdsBundle, INTEGER_DESERIALIZER));
- final boolean allowRoaming = in.getBoolean(ALLOW_ROAMING_KEY);
- final boolean requireOpportunistic = in.getBoolean(REQUIRE_OPPORTUNISTIC_KEY);
+ final int roamingMatchCriteria = in.getInt(ROAMING_MATCH_KEY);
+ final int opportunisticMatchCriteria = in.getInt(OPPORTUNISTIC_MATCH_KEY);
return new VcnCellUnderlyingNetworkTemplate(
- networkQuality,
- allowMetered,
+ meteredMatchCriteria,
+ minEntryUpstreamBandwidthKbps,
+ minExitUpstreamBandwidthKbps,
+ minEntryDownstreamBandwidthKbps,
+ minExitDownstreamBandwidthKbps,
allowedNetworkPlmnIds,
allowedSpecificCarrierIds,
- allowRoaming,
- requireOpportunistic);
+ roamingMatchCriteria,
+ opportunisticMatchCriteria);
}
/** @hide */
@@ -143,35 +176,51 @@
new ArrayList<>(mAllowedSpecificCarrierIds), INTEGER_SERIALIZER);
result.putPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY, specificCarrierIdsBundle);
- result.putBoolean(ALLOW_ROAMING_KEY, mAllowRoaming);
- result.putBoolean(REQUIRE_OPPORTUNISTIC_KEY, mRequireOpportunistic);
+ result.putInt(ROAMING_MATCH_KEY, mRoamingMatchCriteria);
+ result.putInt(OPPORTUNISTIC_MATCH_KEY, mOpportunisticMatchCriteria);
return result;
}
- /** Retrieve the allowed PLMN IDs, or an empty set if any PLMN ID is acceptable. */
+ /**
+ * Retrieve the matching operator PLMN IDs, or an empty set if any PLMN ID is acceptable.
+ *
+ * @see Builder#setOperatorPlmnIds(Set)
+ */
@NonNull
- public Set<String> getAllowedOperatorPlmnIds() {
+ public Set<String> getOperatorPlmnIds() {
return Collections.unmodifiableSet(mAllowedNetworkPlmnIds);
}
/**
- * Retrieve the allowed specific carrier IDs, or an empty set if any specific carrier ID is
- * acceptable.
+ * Retrieve the matching sim specific carrier IDs, or an empty set if any sim specific carrier
+ * ID is acceptable.
+ *
+ * @see Builder#setSimSpecificCarrierIds(Set)
*/
@NonNull
- public Set<Integer> getAllowedSpecificCarrierIds() {
+ public Set<Integer> getSimSpecificCarrierIds() {
return Collections.unmodifiableSet(mAllowedSpecificCarrierIds);
}
- /** Return if roaming is allowed. */
- public boolean allowRoaming() {
- return mAllowRoaming;
+ /**
+ * Return the matching criteria for roaming networks.
+ *
+ * @see Builder#setRoaming(int)
+ */
+ @MatchCriteria
+ public int getRoaming() {
+ return mRoamingMatchCriteria;
}
- /** Return if requiring an opportunistic network. */
- public boolean requireOpportunistic() {
- return mRequireOpportunistic;
+ /**
+ * Return the matching criteria for opportunistic cellular subscriptions.
+ *
+ * @see Builder#setOpportunistic(int)
+ */
+ @MatchCriteria
+ public int getOpportunistic() {
+ return mOpportunisticMatchCriteria;
}
@Override
@@ -180,8 +229,8 @@
super.hashCode(),
mAllowedNetworkPlmnIds,
mAllowedSpecificCarrierIds,
- mAllowRoaming,
- mRequireOpportunistic);
+ mRoamingMatchCriteria,
+ mOpportunisticMatchCriteria);
}
@Override
@@ -197,8 +246,8 @@
final VcnCellUnderlyingNetworkTemplate rhs = (VcnCellUnderlyingNetworkTemplate) other;
return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds)
&& Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds)
- && mAllowRoaming == rhs.mAllowRoaming
- && mRequireOpportunistic == rhs.mRequireOpportunistic;
+ && mRoamingMatchCriteria == rhs.mRoamingMatchCriteria
+ && mOpportunisticMatchCriteria == rhs.mOpportunisticMatchCriteria;
}
/** @hide */
@@ -206,77 +255,199 @@
void dumpTransportSpecificFields(IndentingPrintWriter pw) {
pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds.toString());
pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds.toString());
- pw.println("mAllowRoaming: " + mAllowRoaming);
- pw.println("mRequireOpportunistic: " + mRequireOpportunistic);
+ pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria));
+ pw.println(
+ "mOpportunisticMatchCriteria: "
+ + getMatchCriteriaString(mOpportunisticMatchCriteria));
}
- /** This class is used to incrementally build WifiNetworkPriority objects. */
- public static final class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> {
+ /** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */
+ public static final class Builder {
+ private int mMeteredMatchCriteria = MATCH_ANY;
+
@NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
@NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
- private boolean mAllowRoaming = false;
- private boolean mRequireOpportunistic = false;
+ private int mRoamingMatchCriteria = MATCH_ANY;
+ private int mOpportunisticMatchCriteria = MATCH_ANY;
+
+ private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinEntryDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinExitDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
/** Construct a Builder object. */
public Builder() {}
/**
- * Set allowed operator PLMN IDs.
+ * Set the matching criteria for metered networks.
+ *
+ * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
+ * without NET_CAPABILITY_NOT_METERED). A template where setMetered(MATCH_FORBIDDEN) will
+ * only match a network that is not metered (one with NET_CAPABILITY_NOT_METERED).
+ *
+ * @param matchCriteria the matching criteria for metered networks. Defaults to {@link
+ * #MATCH_ANY}.
+ * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
+ */
+ // The matching getter is defined in the super class. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMetered()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder setMetered(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setMetered");
+
+ mMeteredMatchCriteria = matchCriteria;
+ return this;
+ }
+
+ /**
+ * Set operator PLMN IDs with which a network can match this template.
*
* <p>This is used to distinguish cases where roaming agreements may dictate a different
* priority from a partner's networks.
*
- * @param allowedNetworkPlmnIds the allowed operator PLMN IDs in String. Defaults to an
- * empty set, allowing ANY PLMN ID. A valid PLMN is a concatenation of MNC and MCC, and
- * thus consists of 5 or 6 decimal digits. See {@link SubscriptionInfo#getMccString()}
- * and {@link SubscriptionInfo#getMncString()}.
+ * @param operatorPlmnIds the matching operator PLMN IDs in String. Network with one of the
+ * matching PLMN IDs can match this template. If the set is empty, any PLMN ID will
+ * match. The default is an empty set. A valid PLMN is a concatenation of MNC and MCC,
+ * and thus consists of 5 or 6 decimal digits.
+ * @see SubscriptionInfo#getMccString()
+ * @see SubscriptionInfo#getMncString()
*/
@NonNull
- public Builder setAllowedOperatorPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) {
- validatePlmnIds(allowedNetworkPlmnIds);
+ public Builder setOperatorPlmnIds(@NonNull Set<String> operatorPlmnIds) {
+ validatePlmnIds(operatorPlmnIds);
mAllowedNetworkPlmnIds.clear();
- mAllowedNetworkPlmnIds.addAll(allowedNetworkPlmnIds);
+ mAllowedNetworkPlmnIds.addAll(operatorPlmnIds);
return this;
}
/**
- * Set allowed specific carrier IDs.
+ * Set sim specific carrier IDs with which a network can match this template.
*
- * @param allowedSpecificCarrierIds the allowed specific carrier IDs. Defaults to an empty
- * set, allowing ANY carrier ID. See {@link TelephonyManager#getSimSpecificCarrierId()}.
+ * @param simSpecificCarrierIds the matching sim specific carrier IDs. Network with one of
+ * the sim specific carrier IDs can match this template. If the set is empty, any
+ * carrier ID will match. The default is an empty set.
+ * @see TelephonyManager#getSimSpecificCarrierId()
*/
@NonNull
- public Builder setAllowedSpecificCarrierIds(
- @NonNull Set<Integer> allowedSpecificCarrierIds) {
- Objects.requireNonNull(allowedSpecificCarrierIds, "allowedCarrierIds is null");
+ public Builder setSimSpecificCarrierIds(@NonNull Set<Integer> simSpecificCarrierIds) {
+ Objects.requireNonNull(simSpecificCarrierIds, "simSpecificCarrierIds is null");
+
mAllowedSpecificCarrierIds.clear();
- mAllowedSpecificCarrierIds.addAll(allowedSpecificCarrierIds);
+ mAllowedSpecificCarrierIds.addAll(simSpecificCarrierIds);
return this;
}
/**
- * Set if roaming is allowed.
+ * Set the matching criteria for roaming networks.
*
- * @param allowRoaming the flag to indicate if roaming is allowed. Defaults to {@code
- * false}.
+ * <p>A template where setRoaming(MATCH_REQUIRED) will only match roaming networks (one
+ * without NET_CAPABILITY_NOT_ROAMING). A template where setRoaming(MATCH_FORBIDDEN) will
+ * only match a network that is not roaming (one with NET_CAPABILITY_NOT_ROAMING).
+ *
+ * @param matchCriteria the matching criteria for roaming networks. Defaults to {@link
+ * #MATCH_ANY}.
+ * @see NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING
*/
@NonNull
- public Builder setAllowRoaming(boolean allowRoaming) {
- mAllowRoaming = allowRoaming;
+ public Builder setRoaming(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setRoaming");
+
+ mRoamingMatchCriteria = matchCriteria;
return this;
}
/**
- * Set if requiring an opportunistic network.
+ * Set the matching criteria for opportunistic cellular subscriptions.
*
- * @param requireOpportunistic the flag to indicate if caller requires an opportunistic
- * network. Defaults to {@code false}.
+ * @param matchCriteria the matching criteria for opportunistic cellular subscriptions.
+ * Defaults to {@link #MATCH_ANY}.
+ * @see SubscriptionManager#setOpportunistic(boolean, int)
*/
@NonNull
- public Builder setRequireOpportunistic(boolean requireOpportunistic) {
- mRequireOpportunistic = requireOpportunistic;
+ public Builder setOpportunistic(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setOpportunistic");
+
+ mOpportunisticMatchCriteria = matchCriteria;
+ return this;
+ }
+
+ /**
+ * Set the minimum upstream bandwidths that this template will match.
+ *
+ * <p>This template will not match a network that does not provide at least the bandwidth
+ * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+ * Gateway Connection's underlying network, where it will continue to match until the
+ * bandwidth drops under the exit bandwidth.
+ *
+ * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+ * invalid case where a network fulfills the entry criteria, but at the same time fails the
+ * exit criteria.
+ *
+ * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+ * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+ *
+ * @param minEntryUpstreamBandwidthKbps the minimum accepted upstream bandwidth for networks
+ * that ARE NOT the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @param minExitUpstreamBandwidthKbps the minimum accepted upstream bandwidth for a network
+ * that IS the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ // The getter for the two integers are separated, and in the superclass. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMinEntryUpstreamBandwidthKbps()} and {@link
+ // VcnUnderlyingNetworkTemplate#getMinExitUpstreamBandwidthKbps()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setMinUpstreamBandwidthKbps(
+ int minEntryUpstreamBandwidthKbps, int minExitUpstreamBandwidthKbps) {
+ validateMinBandwidthKbps(minEntryUpstreamBandwidthKbps, minExitUpstreamBandwidthKbps);
+
+ mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+ mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+
+ return this;
+ }
+
+ /**
+ * Set the minimum upstream bandwidths that this template will match.
+ *
+ * <p>This template will not match a network that does not provide at least the bandwidth
+ * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+ * Gateway Connection's underlying network, where it will continue to match until the
+ * bandwidth drops under the exit bandwidth.
+ *
+ * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+ * invalid case where a network fulfills the entry criteria, but at the same time fails the
+ * exit criteria.
+ *
+ * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+ * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+ *
+ * @param minEntryDownstreamBandwidthKbps the minimum accepted downstream bandwidth for
+ * networks that ARE NOT the already-selected underlying network, or {@code 0} to
+ * disable this requirement. Disabled by default.
+ * @param minExitDownstreamBandwidthKbps the minimum accepted downstream bandwidth for a
+ * network that IS the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ // The getter for the two integers are separated, and in the superclass. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMinEntryDownstreamBandwidthKbps()} and {@link
+ // VcnUnderlyingNetworkTemplate#getMinExitDownstreamBandwidthKbps()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setMinDownstreamBandwidthKbps(
+ int minEntryDownstreamBandwidthKbps, int minExitDownstreamBandwidthKbps) {
+ validateMinBandwidthKbps(
+ minEntryDownstreamBandwidthKbps, minExitDownstreamBandwidthKbps);
+
+ mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+ mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
+
return this;
}
@@ -284,18 +455,15 @@
@NonNull
public VcnCellUnderlyingNetworkTemplate build() {
return new VcnCellUnderlyingNetworkTemplate(
- mNetworkQuality,
- mAllowMetered,
+ mMeteredMatchCriteria,
+ mMinEntryUpstreamBandwidthKbps,
+ mMinExitUpstreamBandwidthKbps,
+ mMinEntryDownstreamBandwidthKbps,
+ mMinExitDownstreamBandwidthKbps,
mAllowedNetworkPlmnIds,
mAllowedSpecificCarrierIds,
- mAllowRoaming,
- mRequireOpportunistic);
- }
-
- /** @hide */
- @Override
- Builder self() {
- return this;
+ mRoamingMatchCriteria,
+ mOpportunisticMatchCriteria);
}
}
}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index d07c24a..a6830b7 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -16,7 +16,7 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -42,7 +42,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
@@ -162,30 +162,21 @@
/** @hide */
@VisibleForTesting(visibility = Visibility.PRIVATE)
- public static final LinkedHashSet<VcnUnderlyingNetworkTemplate>
- DEFAULT_UNDERLYING_NETWORK_PRIORITIES = new LinkedHashSet<>();
+ public static final List<VcnUnderlyingNetworkTemplate> DEFAULT_UNDERLYING_NETWORK_TEMPLATES =
+ new ArrayList<>();
static {
- DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+ DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
new VcnCellUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setAllowRoaming(true /* allowRoaming */)
- .setRequireOpportunistic(true /* requireOpportunistic */)
+ .setOpportunistic(MATCH_REQUIRED)
.build());
- DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+ DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
.build());
- DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+ DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
new VcnCellUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setAllowRoaming(true /* allowRoaming */)
- .setRequireOpportunistic(false /* requireOpportunistic */)
.build());
}
@@ -200,9 +191,9 @@
/** @hide */
@VisibleForTesting(visibility = Visibility.PRIVATE)
- public static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities";
+ public static final String UNDERLYING_NETWORK_TEMPLATES_KEY = "mUnderlyingNetworkTemplates";
- @NonNull private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities;
+ @NonNull private final List<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkTemplates;
private static final String MAX_MTU_KEY = "mMaxMtu";
private final int mMaxMtu;
@@ -215,7 +206,7 @@
@NonNull String gatewayConnectionName,
@NonNull IkeTunnelConnectionParams tunnelConnectionParams,
@NonNull Set<Integer> exposedCapabilities,
- @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
mGatewayConnectionName = gatewayConnectionName;
@@ -224,9 +215,9 @@
mRetryIntervalsMs = retryIntervalsMs;
mMaxMtu = maxMtu;
- mUnderlyingNetworkPriorities = new LinkedHashSet<>(underlyingNetworkPriorities);
- if (mUnderlyingNetworkPriorities.isEmpty()) {
- mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+ mUnderlyingNetworkTemplates = new ArrayList<>(underlyingNetworkTemplates);
+ if (mUnderlyingNetworkTemplates.isEmpty()) {
+ mUnderlyingNetworkTemplates.addAll(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
}
validate();
@@ -250,22 +241,19 @@
mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
- final PersistableBundle networkPrioritiesBundle =
- in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY);
+ final PersistableBundle networkTemplatesBundle =
+ in.getPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY);
- if (networkPrioritiesBundle == null) {
- // UNDERLYING_NETWORK_PRIORITIES_KEY was added in Android T. Thus
+ if (networkTemplatesBundle == null) {
+ // UNDERLYING_NETWORK_TEMPLATES_KEY was added in Android T. Thus
// VcnGatewayConnectionConfig created on old platforms will not have this data and will
// be assigned with the default value
- mUnderlyingNetworkPriorities =
- new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
-
+ mUnderlyingNetworkTemplates = new ArrayList<>(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
} else {
- mUnderlyingNetworkPriorities =
- new LinkedHashSet<>(
- PersistableBundleUtils.toList(
- networkPrioritiesBundle,
- VcnUnderlyingNetworkTemplate::fromPersistableBundle));
+ mUnderlyingNetworkTemplates =
+ PersistableBundleUtils.toList(
+ networkTemplatesBundle,
+ VcnUnderlyingNetworkTemplate::fromPersistableBundle);
}
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
@@ -285,7 +273,7 @@
checkValidCapability(cap);
}
- Objects.requireNonNull(mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
+ validateNetworkTemplateList(mUnderlyingNetworkTemplates);
Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null");
validateRetryInterval(mRetryIntervalsMs);
@@ -314,6 +302,19 @@
}
}
+ private static void validateNetworkTemplateList(
+ List<VcnUnderlyingNetworkTemplate> networkPriorityRules) {
+ Objects.requireNonNull(networkPriorityRules, "networkPriorityRules is null");
+
+ Set<VcnUnderlyingNetworkTemplate> existingRules = new ArraySet<>();
+ for (VcnUnderlyingNetworkTemplate rule : networkPriorityRules) {
+ Objects.requireNonNull(rule, "Found null value VcnUnderlyingNetworkTemplate");
+ if (!existingRules.add(rule)) {
+ throw new IllegalArgumentException("Found duplicate VcnUnderlyingNetworkTemplate");
+ }
+ }
+ }
+
/**
* Returns the configured Gateway Connection name.
*
@@ -368,15 +369,13 @@
}
/**
- * Retrieve the configured VcnUnderlyingNetworkTemplate list, or a default list if it is not
- * configured.
+ * Retrieve the VcnUnderlyingNetworkTemplate list, or a default list if it is not configured.
*
- * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkTemplate>)
- * @hide
+ * @see Builder#setVcnUnderlyingNetworkPriorities(List)
*/
@NonNull
- public LinkedHashSet<VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities() {
- return new LinkedHashSet<>(mUnderlyingNetworkPriorities);
+ public List<VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities() {
+ return new ArrayList<>(mUnderlyingNetworkTemplates);
}
/**
@@ -415,15 +414,15 @@
PersistableBundleUtils.fromList(
new ArrayList<>(mExposedCapabilities),
PersistableBundleUtils.INTEGER_SERIALIZER);
- final PersistableBundle networkPrioritiesBundle =
+ final PersistableBundle networkTemplatesBundle =
PersistableBundleUtils.fromList(
- new ArrayList<>(mUnderlyingNetworkPriorities),
+ mUnderlyingNetworkTemplates,
VcnUnderlyingNetworkTemplate::toPersistableBundle);
result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName);
result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle);
result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
- result.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, networkPrioritiesBundle);
+ result.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, networkTemplatesBundle);
result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
result.putInt(MAX_MTU_KEY, mMaxMtu);
@@ -436,7 +435,7 @@
mGatewayConnectionName,
mTunnelConnectionParams,
mExposedCapabilities,
- mUnderlyingNetworkPriorities,
+ mUnderlyingNetworkTemplates,
Arrays.hashCode(mRetryIntervalsMs),
mMaxMtu);
}
@@ -451,7 +450,7 @@
return mGatewayConnectionName.equals(rhs.mGatewayConnectionName)
&& mTunnelConnectionParams.equals(rhs.mTunnelConnectionParams)
&& mExposedCapabilities.equals(rhs.mExposedCapabilities)
- && mUnderlyingNetworkPriorities.equals(rhs.mUnderlyingNetworkPriorities)
+ && mUnderlyingNetworkTemplates.equals(rhs.mUnderlyingNetworkTemplates)
&& Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
&& mMaxMtu == rhs.mMaxMtu;
}
@@ -465,8 +464,8 @@
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
@NonNull
- private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities =
- new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+ private final List<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkTemplates =
+ new ArrayList<>(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
private int mMaxMtu = DEFAULT_MAX_MTU;
@@ -539,27 +538,37 @@
}
/**
- * Set the VcnUnderlyingNetworkTemplate list.
+ * Set the list of templates to match underlying networks against, in high-to-low priority
+ * order.
*
- * @param underlyingNetworkPriorities a list of unique VcnUnderlyingNetworkPriorities that
- * are ordered from most to least preferred, or an empty list to use the default
- * prioritization. The default network prioritization is Opportunistic cellular, Carrier
- * WiFi and Macro cellular
- * @return
+ * <p>To select the VCN underlying network, the VCN connection will go through all the
+ * network candidates and return a network matching the highest priority rule.
+ *
+ * <p>If multiple networks match the same rule, the VCN will prefer an already-selected
+ * network as opposed to a new/unselected network. However, if both are new/unselected
+ * networks, a network will be chosen arbitrarily amongst the networks matching the highest
+ * priority rule.
+ *
+ * <p>If all networks fail to match the rules provided, an underlying network will still be
+ * selected (at random if necessary).
+ *
+ * @param underlyingNetworkTemplates a list of unique VcnUnderlyingNetworkTemplates that are
+ * ordered from most to least preferred, or an empty list to use the default
+ * prioritization. The default network prioritization order is Opportunistic cellular,
+ * Carrier WiFi and then Macro cellular.
+ * @return this {@link Builder} instance, for chaining
*/
- /** @hide */
@NonNull
public Builder setVcnUnderlyingNetworkPriorities(
- @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities) {
- Objects.requireNonNull(
- mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
+ @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) {
+ validateNetworkTemplateList(underlyingNetworkTemplates);
- mUnderlyingNetworkPriorities.clear();
+ mUnderlyingNetworkTemplates.clear();
- if (underlyingNetworkPriorities.isEmpty()) {
- mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+ if (underlyingNetworkTemplates.isEmpty()) {
+ mUnderlyingNetworkTemplates.addAll(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
} else {
- mUnderlyingNetworkPriorities.addAll(underlyingNetworkPriorities);
+ mUnderlyingNetworkTemplates.addAll(underlyingNetworkTemplates);
}
return this;
@@ -629,7 +638,7 @@
mGatewayConnectionName,
mTunnelConnectionParams,
mExposedCapabilities,
- mUnderlyingNetworkPriorities,
+ mUnderlyingNetworkTemplates,
mRetryIntervalsMs,
mMaxMtu);
}
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index d306d5c..3a9ca3e 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -15,6 +15,8 @@
*/
package android.net.vcn;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import android.annotation.IntDef;
@@ -31,59 +33,126 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
-// TODO: Add documents
-/** @hide */
+/**
+ * This class represents a template containing set of underlying network requirements for doing
+ * route selection.
+ *
+ * <p>Apps provisioning a VCN can configure the underlying network priority for each Gateway
+ * Connection by setting a list (in priority order, most to least preferred) of the appropriate
+ * subclasses in the VcnGatewayConnectionConfig. See {@link
+ * VcnGatewayConnectionConfig.Builder#setVcnUnderlyingNetworkPriorities}
+ */
public abstract class VcnUnderlyingNetworkTemplate {
/** @hide */
- protected static final int NETWORK_PRIORITY_TYPE_WIFI = 1;
+ static final int NETWORK_PRIORITY_TYPE_WIFI = 1;
/** @hide */
- protected static final int NETWORK_PRIORITY_TYPE_CELL = 2;
+ static final int NETWORK_PRIORITY_TYPE_CELL = 2;
- /** Denotes that any network quality is acceptable */
- public static final int NETWORK_QUALITY_ANY = 0;
- /** Denotes that network quality needs to be OK */
- public static final int NETWORK_QUALITY_OK = 100000;
+ /**
+ * Used to configure the matching criteria of a network characteristic. This may include network
+ * capabilities, or cellular subscription information. Denotes that networks with or without the
+ * characteristic are both acceptable to match this template.
+ */
+ public static final int MATCH_ANY = 0;
- private static final SparseArray<String> NETWORK_QUALITY_TO_STRING_MAP = new SparseArray<>();
+ /**
+ * Used to configure the matching criteria of a network characteristic. This may include network
+ * capabilities, or cellular subscription information. Denotes that a network MUST have the
+ * capability in order to match this template.
+ */
+ public static final int MATCH_REQUIRED = 1;
- static {
- NETWORK_QUALITY_TO_STRING_MAP.put(NETWORK_QUALITY_ANY, "NETWORK_QUALITY_ANY");
- NETWORK_QUALITY_TO_STRING_MAP.put(NETWORK_QUALITY_OK, "NETWORK_QUALITY_OK");
- }
+ /**
+ * Used to configure the matching criteria of a network characteristic. This may include network
+ * capabilities, or cellular subscription information. Denotes that a network MUST NOT have the
+ * capability in order to match this template.
+ */
+ public static final int MATCH_FORBIDDEN = 2;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({NETWORK_QUALITY_OK, NETWORK_QUALITY_ANY})
- public @interface NetworkQuality {}
+ @IntDef({MATCH_ANY, MATCH_REQUIRED, MATCH_FORBIDDEN})
+ public @interface MatchCriteria {}
+
+ private static final SparseArray<String> MATCH_CRITERIA_TO_STRING_MAP = new SparseArray<>();
+
+ static {
+ MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_ANY, "MATCH_ANY");
+ MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_REQUIRED, "MATCH_REQUIRED");
+ MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_FORBIDDEN, "MATCH_FORBIDDEN");
+ }
private static final String NETWORK_PRIORITY_TYPE_KEY = "mNetworkPriorityType";
private final int mNetworkPriorityType;
/** @hide */
- protected static final String NETWORK_QUALITY_KEY = "mNetworkQuality";
- private final int mNetworkQuality;
+ static final String METERED_MATCH_KEY = "mMeteredMatchCriteria";
+
+ private final int mMeteredMatchCriteria;
/** @hide */
- protected static final String ALLOW_METERED_KEY = "mAllowMetered";
- private final boolean mAllowMetered;
+ public static final int DEFAULT_MIN_BANDWIDTH_KBPS = 0;
/** @hide */
- protected VcnUnderlyingNetworkTemplate(
- int networkPriorityType, int networkQuality, boolean allowMetered) {
+ static final String MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY = "mMinEntryUpstreamBandwidthKbps";
+
+ private final int mMinEntryUpstreamBandwidthKbps;
+
+ /** @hide */
+ static final String MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY = "mMinExitUpstreamBandwidthKbps";
+
+ private final int mMinExitUpstreamBandwidthKbps;
+
+ /** @hide */
+ static final String MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY =
+ "mMinEntryDownstreamBandwidthKbps";
+
+ private final int mMinEntryDownstreamBandwidthKbps;
+
+ /** @hide */
+ static final String MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY = "mMinExitDownstreamBandwidthKbps";
+
+ private final int mMinExitDownstreamBandwidthKbps;
+
+ /** @hide */
+ VcnUnderlyingNetworkTemplate(
+ int networkPriorityType,
+ int meteredMatchCriteria,
+ int minEntryUpstreamBandwidthKbps,
+ int minExitUpstreamBandwidthKbps,
+ int minEntryDownstreamBandwidthKbps,
+ int minExitDownstreamBandwidthKbps) {
mNetworkPriorityType = networkPriorityType;
- mNetworkQuality = networkQuality;
- mAllowMetered = allowMetered;
+ mMeteredMatchCriteria = meteredMatchCriteria;
+ mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+ mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+ mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+ mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
}
- private static void validateNetworkQuality(int networkQuality) {
+ /** @hide */
+ static void validateMatchCriteria(int matchCriteria, String matchingCapability) {
Preconditions.checkArgument(
- networkQuality == NETWORK_QUALITY_ANY || networkQuality == NETWORK_QUALITY_OK,
- "Invalid networkQuality:" + networkQuality);
+ MATCH_CRITERIA_TO_STRING_MAP.contains(matchCriteria),
+ "Invalid matching criteria: " + matchCriteria + " for " + matchingCapability);
+ }
+
+ /** @hide */
+ static void validateMinBandwidthKbps(int minEntryBandwidth, int minExitBandwidth) {
+ Preconditions.checkArgument(
+ minEntryBandwidth >= 0, "Invalid minEntryBandwidth, must be >= 0");
+ Preconditions.checkArgument(
+ minExitBandwidth >= 0, "Invalid minExitBandwidth, must be >= 0");
+ Preconditions.checkArgument(
+ minEntryBandwidth >= minExitBandwidth,
+ "Minimum entry bandwidth must be >= exit bandwidth");
}
/** @hide */
protected void validate() {
- validateNetworkQuality(mNetworkQuality);
+ validateMatchCriteria(mMeteredMatchCriteria, "mMeteredMatchCriteria");
+ validateMinBandwidthKbps(mMinEntryUpstreamBandwidthKbps, mMinExitUpstreamBandwidthKbps);
+ validateMinBandwidthKbps(mMinEntryDownstreamBandwidthKbps, mMinExitDownstreamBandwidthKbps);
}
/** @hide */
@@ -111,15 +180,24 @@
final PersistableBundle result = new PersistableBundle();
result.putInt(NETWORK_PRIORITY_TYPE_KEY, mNetworkPriorityType);
- result.putInt(NETWORK_QUALITY_KEY, mNetworkQuality);
- result.putBoolean(ALLOW_METERED_KEY, mAllowMetered);
+ result.putInt(METERED_MATCH_KEY, mMeteredMatchCriteria);
+ result.putInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, mMinEntryUpstreamBandwidthKbps);
+ result.putInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, mMinExitUpstreamBandwidthKbps);
+ result.putInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, mMinEntryDownstreamBandwidthKbps);
+ result.putInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, mMinExitDownstreamBandwidthKbps);
return result;
}
@Override
public int hashCode() {
- return Objects.hash(mNetworkPriorityType, mNetworkQuality, mAllowMetered);
+ return Objects.hash(
+ mNetworkPriorityType,
+ mMeteredMatchCriteria,
+ mMinEntryUpstreamBandwidthKbps,
+ mMinExitUpstreamBandwidthKbps,
+ mMinEntryDownstreamBandwidthKbps,
+ mMinExitDownstreamBandwidthKbps);
}
@Override
@@ -130,8 +208,21 @@
final VcnUnderlyingNetworkTemplate rhs = (VcnUnderlyingNetworkTemplate) other;
return mNetworkPriorityType == rhs.mNetworkPriorityType
- && mNetworkQuality == rhs.mNetworkQuality
- && mAllowMetered == rhs.mAllowMetered;
+ && mMeteredMatchCriteria == rhs.mMeteredMatchCriteria
+ && mMinEntryUpstreamBandwidthKbps == rhs.mMinEntryUpstreamBandwidthKbps
+ && mMinExitUpstreamBandwidthKbps == rhs.mMinExitUpstreamBandwidthKbps
+ && mMinEntryDownstreamBandwidthKbps == rhs.mMinEntryDownstreamBandwidthKbps
+ && mMinExitDownstreamBandwidthKbps == rhs.mMinExitDownstreamBandwidthKbps;
+ }
+
+ /** @hide */
+ static String getNameString(SparseArray<String> toStringMap, int key) {
+ return toStringMap.get(key, "Invalid value " + key);
+ }
+
+ /** @hide */
+ static String getMatchCriteriaString(int matchCriteria) {
+ return getNameString(MATCH_CRITERIA_TO_STRING_MAP, matchCriteria);
}
/** @hide */
@@ -146,67 +237,63 @@
pw.println(this.getClass().getSimpleName() + ":");
pw.increaseIndent();
- pw.println(
- "mNetworkQuality: "
- + NETWORK_QUALITY_TO_STRING_MAP.get(
- mNetworkQuality, "Invalid value " + mNetworkQuality));
- pw.println("mAllowMetered: " + mAllowMetered);
+ pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria));
+ pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps);
+ pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps);
+ pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps);
+ pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps);
dumpTransportSpecificFields(pw);
pw.decreaseIndent();
}
- /** Retrieve the required network quality. */
- @NetworkQuality
- public int getNetworkQuality() {
- return mNetworkQuality;
- }
-
- /** Return if a metered network is allowed. */
- public boolean allowMetered() {
- return mAllowMetered;
+ /**
+ * Return the matching criteria for metered networks.
+ *
+ * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMetered(int)
+ * @see VcnCellUnderlyingNetworkTemplate.Builder#setMetered(int)
+ */
+ public int getMetered() {
+ return mMeteredMatchCriteria;
}
/**
- * This class is used to incrementally build VcnUnderlyingNetworkTemplate objects.
+ * Returns the minimum entry upstream bandwidth allowed by this template.
*
- * @param <T> The subclass to be built.
+ * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+ * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
*/
- public abstract static class Builder<T extends Builder<T>> {
- /** @hide */
- protected int mNetworkQuality = NETWORK_QUALITY_ANY;
- /** @hide */
- protected boolean mAllowMetered = false;
+ public int getMinEntryUpstreamBandwidthKbps() {
+ return mMinEntryUpstreamBandwidthKbps;
+ }
- /** @hide */
- protected Builder() {}
+ /**
+ * Returns the minimum exit upstream bandwidth allowed by this template.
+ *
+ * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+ * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+ */
+ public int getMinExitUpstreamBandwidthKbps() {
+ return mMinExitUpstreamBandwidthKbps;
+ }
- /**
- * Set the required network quality.
- *
- * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
- */
- @NonNull
- public T setNetworkQuality(@NetworkQuality int networkQuality) {
- validateNetworkQuality(networkQuality);
+ /**
+ * Returns the minimum entry downstream bandwidth allowed by this template.
+ *
+ * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+ * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+ */
+ public int getMinEntryDownstreamBandwidthKbps() {
+ return mMinEntryDownstreamBandwidthKbps;
+ }
- mNetworkQuality = networkQuality;
- return self();
- }
-
- /**
- * Set if a metered network is allowed.
- *
- * @param allowMetered the flag to indicate if a metered network is allowed, defaults to
- * {@code false}
- */
- @NonNull
- public T setAllowMetered(boolean allowMetered) {
- mAllowMetered = allowMetered;
- return self();
- }
-
- /** @hide */
- abstract T self();
+ /**
+ * Returns the minimum exit downstream bandwidth allowed by this template.
+ *
+ * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+ * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+ */
+ public int getMinExitDownstreamBandwidthKbps() {
+ return mMinExitDownstreamBandwidthKbps;
}
}
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 6bbb2bf..23a07ab 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -16,41 +16,99 @@
package android.net.vcn;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.net.NetworkCapabilities;
import android.os.PersistableBundle;
+import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.util.PersistableBundleUtils;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Objects;
+import java.util.Set;
-// TODO: Add documents
-/** @hide */
+/**
+ * This class represents a configuration for a network template class of underlying Carrier WiFi
+ * networks.
+ *
+ * <p>See {@link VcnUnderlyingNetworkTemplate}
+ */
public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
- private static final String SSID_KEY = "mSsid";
- @Nullable private final String mSsid;
+ private static final String SSIDS_KEY = "mSsids";
+ @Nullable private final Set<String> mSsids;
private VcnWifiUnderlyingNetworkTemplate(
- int networkQuality, boolean allowMetered, String ssid) {
- super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, allowMetered);
- mSsid = ssid;
+ int meteredMatchCriteria,
+ int minEntryUpstreamBandwidthKbps,
+ int minExitUpstreamBandwidthKbps,
+ int minEntryDownstreamBandwidthKbps,
+ int minExitDownstreamBandwidthKbps,
+ Set<String> ssids) {
+ super(
+ NETWORK_PRIORITY_TYPE_WIFI,
+ meteredMatchCriteria,
+ minEntryUpstreamBandwidthKbps,
+ minExitUpstreamBandwidthKbps,
+ minEntryDownstreamBandwidthKbps,
+ minExitDownstreamBandwidthKbps);
+ mSsids = new ArraySet<>(ssids);
validate();
}
/** @hide */
+ @Override
+ protected void validate() {
+ super.validate();
+ validateSsids(mSsids);
+ }
+
+ private static void validateSsids(Set<String> ssids) {
+ Objects.requireNonNull(ssids, "ssids is null");
+
+ for (String ssid : ssids) {
+ Objects.requireNonNull(ssid, "found null value ssid");
+ }
+ }
+
+ /** @hide */
@NonNull
@VisibleForTesting(visibility = Visibility.PROTECTED)
public static VcnWifiUnderlyingNetworkTemplate fromPersistableBundle(
@NonNull PersistableBundle in) {
Objects.requireNonNull(in, "PersistableBundle is null");
- final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
- final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
- final String ssid = in.getString(SSID_KEY);
- return new VcnWifiUnderlyingNetworkTemplate(networkQuality, allowMetered, ssid);
+ final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
+
+ final int minEntryUpstreamBandwidthKbps =
+ in.getInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minExitUpstreamBandwidthKbps =
+ in.getInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minEntryDownstreamBandwidthKbps =
+ in.getInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+ final int minExitDownstreamBandwidthKbps =
+ in.getInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+
+ final PersistableBundle ssidsBundle = in.getPersistableBundle(SSIDS_KEY);
+ Objects.requireNonNull(ssidsBundle, "ssidsBundle is null");
+ final Set<String> ssids =
+ new ArraySet<String>(
+ PersistableBundleUtils.toList(ssidsBundle, STRING_DESERIALIZER));
+ return new VcnWifiUnderlyingNetworkTemplate(
+ meteredMatchCriteria,
+ minEntryUpstreamBandwidthKbps,
+ minExitUpstreamBandwidthKbps,
+ minEntryDownstreamBandwidthKbps,
+ minExitDownstreamBandwidthKbps,
+ ssids);
}
/** @hide */
@@ -59,13 +117,17 @@
@VisibleForTesting(visibility = Visibility.PROTECTED)
public PersistableBundle toPersistableBundle() {
final PersistableBundle result = super.toPersistableBundle();
- result.putString(SSID_KEY, mSsid);
+
+ final PersistableBundle ssidsBundle =
+ PersistableBundleUtils.fromList(new ArrayList<>(mSsids), STRING_SERIALIZER);
+ result.putPersistableBundle(SSIDS_KEY, ssidsBundle);
+
return result;
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), mSsid);
+ return Objects.hash(super.hashCode(), mSsids);
}
@Override
@@ -79,49 +141,162 @@
}
final VcnWifiUnderlyingNetworkTemplate rhs = (VcnWifiUnderlyingNetworkTemplate) other;
- return mSsid.equals(rhs.mSsid);
+ return mSsids.equals(rhs.mSsids);
}
/** @hide */
@Override
void dumpTransportSpecificFields(IndentingPrintWriter pw) {
- pw.println("mSsid: " + mSsid);
+ pw.println("mSsids: " + mSsids);
}
- /** Retrieve the required SSID, or {@code null} if there is no requirement on SSID. */
- @Nullable
- public String getSsid() {
- return mSsid;
+ /**
+ * Retrieve the matching SSIDs, or an empty set if any SSID is acceptable.
+ *
+ * @see Builder#setSsids(Set)
+ */
+ @NonNull
+ public Set<String> getSsids() {
+ return Collections.unmodifiableSet(mSsids);
}
/** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */
- public static class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> {
- @Nullable private String mSsid;
+ public static final class Builder {
+ private int mMeteredMatchCriteria = MATCH_ANY;
+ @NonNull private final Set<String> mSsids = new ArraySet<>();
+
+ private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinEntryDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+ private int mMinExitDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
/** Construct a Builder object. */
public Builder() {}
/**
- * Set the required SSID.
+ * Set the matching criteria for metered networks.
*
- * @param ssid the required SSID, or {@code null} if any SSID is acceptable.
+ * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
+ * without NET_CAPABILITY_NOT_METERED). A template where setMetered(MATCH_FORBIDDEN) will
+ * only match a network that is not metered (one with NET_CAPABILITY_NOT_METERED).
+ *
+ * @param matchCriteria the matching criteria for metered networks. Defaults to {@link
+ * #MATCH_ANY}.
+ * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
+ */
+ // The matching getter is defined in the super class. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMetered()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder setMetered(@MatchCriteria int matchCriteria) {
+ validateMatchCriteria(matchCriteria, "setMetered");
+
+ mMeteredMatchCriteria = matchCriteria;
+ return this;
+ }
+
+ /**
+ * Set the SSIDs with which a network can match this priority rule.
+ *
+ * @param ssids the matching SSIDs. Network with one of the matching SSIDs can match this
+ * priority rule. If the set is empty, any SSID will match. The default is an empty set.
*/
@NonNull
- public Builder setSsid(@Nullable String ssid) {
- mSsid = ssid;
+ public Builder setSsids(@NonNull Set<String> ssids) {
+ validateSsids(ssids);
+
+ mSsids.clear();
+ mSsids.addAll(ssids);
+ return this;
+ }
+
+ /**
+ * Set the minimum upstream bandwidths that this template will match.
+ *
+ * <p>This template will not match a network that does not provide at least the bandwidth
+ * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+ * Gateway Connection's underlying network, where it will continue to match until the
+ * bandwidth drops under the exit bandwidth.
+ *
+ * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+ * invalid case where a network fulfills the entry criteria, but at the same time fails the
+ * exit criteria.
+ *
+ * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+ * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+ *
+ * @param minEntryUpstreamBandwidthKbps the minimum accepted upstream bandwidth for networks
+ * that ARE NOT the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @param minExitUpstreamBandwidthKbps the minimum accepted upstream bandwidth for a network
+ * that IS the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ // The getter for the two integers are separated, and in the superclass. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMinEntryUpstreamBandwidthKbps()} and {@link
+ // VcnUnderlyingNetworkTemplate#getMinExitUpstreamBandwidthKbps()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setMinUpstreamBandwidthKbps(
+ int minEntryUpstreamBandwidthKbps, int minExitUpstreamBandwidthKbps) {
+ validateMinBandwidthKbps(minEntryUpstreamBandwidthKbps, minExitUpstreamBandwidthKbps);
+
+ mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+ mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+
+ return this;
+ }
+
+ /**
+ * Set the minimum upstream bandwidths that this template will match.
+ *
+ * <p>This template will not match a network that does not provide at least the bandwidth
+ * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+ * Gateway Connection's underlying network, where it will continue to match until the
+ * bandwidth drops under the exit bandwidth.
+ *
+ * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+ * invalid case where a network fulfills the entry criteria, but at the same time fails the
+ * exit criteria.
+ *
+ * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+ * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+ *
+ * @param minEntryDownstreamBandwidthKbps the minimum accepted downstream bandwidth for
+ * networks that ARE NOT the already-selected underlying network, or {@code 0} to
+ * disable this requirement. Disabled by default.
+ * @param minExitDownstreamBandwidthKbps the minimum accepted downstream bandwidth for a
+ * network that IS the already-selected underlying network, or {@code 0} to disable this
+ * requirement. Disabled by default.
+ * @return this {@link Builder} instance, for chaining
+ */
+ @NonNull
+ // The getter for the two integers are separated, and in the superclass. Please see {@link
+ // VcnUnderlyingNetworkTemplate#getMinEntryDownstreamBandwidthKbps()} and {@link
+ // VcnUnderlyingNetworkTemplate#getMinExitDownstreamBandwidthKbps()}
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setMinDownstreamBandwidthKbps(
+ int minEntryDownstreamBandwidthKbps, int minExitDownstreamBandwidthKbps) {
+ validateMinBandwidthKbps(
+ minEntryDownstreamBandwidthKbps, minExitDownstreamBandwidthKbps);
+
+ mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+ mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
+
return this;
}
/** Build the VcnWifiUnderlyingNetworkTemplate. */
@NonNull
public VcnWifiUnderlyingNetworkTemplate build() {
- return new VcnWifiUnderlyingNetworkTemplate(mNetworkQuality, mAllowMetered, mSsid);
- }
-
- /** @hide */
- @Override
- Builder self() {
- return this;
+ return new VcnWifiUnderlyingNetworkTemplate(
+ mMeteredMatchCriteria,
+ mMinEntryUpstreamBandwidthKbps,
+ mMinExitUpstreamBandwidthKbps,
+ mMinEntryDownstreamBandwidthKbps,
+ mMinExitDownstreamBandwidthKbps,
+ mSsids);
}
}
}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 9eaaa91..542de3f 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -131,6 +131,7 @@
mContext.getAttributionTag(), callback);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -152,6 +153,7 @@
mSubscriptionChangedListenerMap.remove(listener);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -194,6 +196,7 @@
mContext.getAttributionTag(), callback);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -216,6 +219,7 @@
mOpportunisticSubscriptionChangedListenerMap.remove(listener);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -304,6 +308,7 @@
sRegistry.notifyCarrierNetworkChange(active);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -329,6 +334,7 @@
sRegistry.notifyCarrierNetworkChangeWithSubId(subscriptionId, active);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -347,6 +353,7 @@
sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -364,6 +371,7 @@
sRegistry.notifyCallStateForAllSubs(state, incomingNumber);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -376,6 +384,7 @@
sRegistry.notifySubscriptionInfoChanged();
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -388,6 +397,7 @@
sRegistry.notifyOpportunisticSubscriptionInfoChanged();
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -404,6 +414,7 @@
sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -421,6 +432,7 @@
sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -439,6 +451,7 @@
sRegistry.notifyMessageWaitingChangedForPhoneId(slotIndex, subId, msgWaitingInd);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -454,6 +467,7 @@
sRegistry.notifyCallForwardingChangedForSubscriber(subId, callForwardInd);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -469,6 +483,7 @@
sRegistry.notifyDataActivityForSubscriber(subId, dataActivityType);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -490,6 +505,7 @@
slotIndex, subId, preciseState);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -508,6 +524,7 @@
sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -523,6 +540,7 @@
sRegistry.notifyEmergencyNumberList(slotIndex, subId);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -538,6 +556,7 @@
sRegistry.notifyOutgoingEmergencyCall(phoneId, subId, emergencyNumber);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -553,6 +572,7 @@
sRegistry.notifyOutgoingEmergencySms(phoneId, subId, emergencyNumber);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -570,6 +590,7 @@
sRegistry.notifyRadioPowerStateChanged(slotIndex, subId, radioPowerState);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -583,6 +604,7 @@
sRegistry.notifyPhoneCapabilityChanged(phoneCapability);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -615,6 +637,7 @@
SIM_ACTIVATION_TYPE_DATA, activationState);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -634,6 +657,7 @@
SIM_ACTIVATION_TYPE_VOICE, activationState);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -651,6 +675,7 @@
sRegistry.notifyUserMobileDataStateChangedForPhoneId(slotIndex, subId, state);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -669,6 +694,7 @@
sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, telephonyDisplayInfo);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -683,6 +709,7 @@
sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -698,6 +725,7 @@
sRegistry.notifySrvccStateChanged(subId, state);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -721,6 +749,7 @@
foregroundCallPreciseState, backgroundCallPreciseState);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -741,6 +770,7 @@
sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -755,6 +785,7 @@
sRegistry.notifyCellLocationForSubscriber(subId, cellLocation);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -769,7 +800,7 @@
try {
sRegistry.notifyCellInfoForSubscriber(subId, cellInfo);
} catch (RemoteException ex) {
-
+ throw ex.rethrowFromSystemServer();
}
}
@@ -781,7 +812,7 @@
try {
sRegistry.notifyActiveDataSubIdChanged(activeDataSubId);
} catch (RemoteException ex) {
-
+ throw ex.rethrowFromSystemServer();
}
}
@@ -814,6 +845,7 @@
sRegistry.notifyRegistrationFailed(slotIndex, subId, cellIdentity,
chosenPlmn, domain, causeCode, additionalCauseCode);
} catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
}
@@ -830,6 +862,7 @@
sRegistry.notifyBarringInfoChanged(slotIndex, subId, barringInfo);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -846,6 +879,7 @@
sRegistry.notifyPhysicalChannelConfigForSubscriber(slotIndex, subId, configs);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -862,6 +896,7 @@
sRegistry.notifyDataEnabled(slotIndex, subId, enabled, reason);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
@@ -880,6 +915,7 @@
allowedNetworkType);
} catch (RemoteException ex) {
// system process is dead
+ throw ex.rethrowFromSystemServer();
}
}
@@ -895,6 +931,7 @@
sRegistry.notifyLinkCapacityEstimateChanged(slotIndex, subId, linkCapacityEstimateList);
} catch (RemoteException ex) {
// system server crash
+ throw ex.rethrowFromSystemServer();
}
}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 2e7629a..2ae56f8 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -34,8 +34,6 @@
import android.os.Parcelable;
import android.os.UserHandle;
-import java.net.Inet4Address;
-import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -93,8 +91,8 @@
public String interfaze;
public String session;
public int mtu = -1;
- public List<LinkAddress> addresses = new ArrayList<LinkAddress>();
- public List<RouteInfo> routes = new ArrayList<RouteInfo>();
+ public List<LinkAddress> addresses = new ArrayList<>();
+ public List<RouteInfo> routes = new ArrayList<>();
public List<String> dnsServers;
public List<String> searchDomains;
public List<String> allowedApplications;
@@ -114,12 +112,32 @@
public VpnConfig() {
}
- public void updateAllowedFamilies(InetAddress address) {
- if (address instanceof Inet4Address) {
- allowIPv4 = true;
- } else {
- allowIPv6 = true;
- }
+ public VpnConfig(VpnConfig other) {
+ user = other.user;
+ interfaze = other.interfaze;
+ session = other.session;
+ mtu = other.mtu;
+ addresses = copyOf(other.addresses);
+ routes = copyOf(other.routes);
+ dnsServers = copyOf(other.dnsServers);
+ searchDomains = copyOf(other.searchDomains);
+ allowedApplications = copyOf(other.allowedApplications);
+ disallowedApplications = copyOf(other.disallowedApplications);
+ configureIntent = other.configureIntent;
+ startTime = other.startTime;
+ legacy = other.legacy;
+ blocking = other.blocking;
+ allowBypass = other.allowBypass;
+ allowIPv4 = other.allowIPv4;
+ allowIPv6 = other.allowIPv6;
+ isMetered = other.isMetered;
+ underlyingNetworks = other.underlyingNetworks != null ? Arrays.copyOf(
+ other.underlyingNetworks, other.underlyingNetworks.length) : null;
+ proxyInfo = other.proxyInfo;
+ }
+
+ private static <T> List<T> copyOf(List<T> list) {
+ return list != null ? new ArrayList<>(list) : null;
}
public void addLegacyRoutes(String routesStr) {
@@ -131,7 +149,6 @@
//each route is ip/prefix
RouteInfo info = new RouteInfo(new IpPrefix(route), null, null, RouteInfo.RTN_UNICAST);
this.routes.add(info);
- updateAllowedFamilies(info.getDestination().getAddress());
}
}
@@ -144,7 +161,6 @@
//each address is ip/prefix
LinkAddress addr = new LinkAddress(address);
this.addresses.add(addr);
- updateAllowedFamilies(addr.getAddress());
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 0f1c6f3..270b5d6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -25,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.UidTraffic;
import android.compat.annotation.UnsupportedAppUsage;
@@ -36,7 +37,6 @@
import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
import android.location.GnssSignalQuality;
-import android.net.INetworkStatsService;
import android.net.NetworkStats;
import android.net.Uri;
import android.net.wifi.WifiManager;
@@ -136,7 +136,9 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Queue;
+import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
@@ -11539,19 +11541,11 @@
private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1);
@VisibleForTesting
- protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
- try {
- if (!ArrayUtils.isEmpty(ifaces)) {
- INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
- if (statsService != null) {
- return statsService.getDetailedUidStats(ifaces);
- } else {
- Slog.e(TAG, "Failed to get networkStatsService ");
- }
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces) + e);
+ protected NetworkStats readNetworkStatsLocked(@NonNull NetworkStatsManager networkStatsManager,
+ String[] ifaces) {
+ Objects.requireNonNull(networkStatsManager);
+ if (!ArrayUtils.isEmpty(ifaces)) {
+ return networkStatsManager.getDetailedUidStats(Set.of(ifaces));
}
return null;
}
@@ -11561,7 +11555,8 @@
* @param info The energy information from the WiFi controller.
*/
public void updateWifiState(@Nullable final WifiActivityEnergyInfo info,
- final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
+ final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs,
+ @NonNull NetworkStatsManager networkStatsManager) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces));
}
@@ -11569,7 +11564,8 @@
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
synchronized (mWifiNetworkLock) {
- final NetworkStats latestStats = readNetworkStatsLocked(mWifiIfaces);
+ final NetworkStats latestStats = readNetworkStatsLocked(networkStatsManager,
+ mWifiIfaces);
if (latestStats != null) {
delta = NetworkStats.subtract(latestStats, mLastWifiNetworkStats, null, null,
mNetworkStatsPool.acquire());
@@ -11921,7 +11917,8 @@
* Distribute Cell radio energy info and network traffic to apps.
*/
public void noteModemControllerActivity(@Nullable final ModemActivityInfo activityInfo,
- final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
+ final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs,
+ @NonNull NetworkStatsManager networkStatsManager) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
@@ -11935,7 +11932,8 @@
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
synchronized (mModemNetworkLock) {
- final NetworkStats latestStats = readNetworkStatsLocked(mModemIfaces);
+ final NetworkStats latestStats = readNetworkStatsLocked(networkStatsManager,
+ mModemIfaces);
if (latestStats != null) {
delta = NetworkStats.subtract(latestStats, mLastModemNetworkStats, null, null,
mNetworkStatsPool.acquire());
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2cfec4b..07b16ed 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -242,6 +242,7 @@
"audioflinger-aidl-cpp",
"av-types-aidl-cpp",
"android.hardware.camera.device@3.2",
+ "libandroid_net",
"libandroidicu",
"libbpf_android",
"libnetdbpf",
diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp
index afad08a..1be1873 100644
--- a/core/jni/android_server_NetworkManagementSocketTagger.cpp
+++ b/core/jni/android_server_NetworkManagementSocketTagger.cpp
@@ -15,24 +15,23 @@
*/
#define LOG_TAG "NMST_QTagUidNative"
-#include <utils/Log.h>
-#include <nativehelper/JNIPlatformHelp.h>
-
-#include "jni.h"
-#include <utils/misc.h>
+#include <android/multinetwork.h>
#include <cutils/qtaguid.h>
-
#include <errno.h>
#include <fcntl.h>
-#include <sys/types.h>
+#include <nativehelper/JNIPlatformHelp.h>
#include <sys/socket.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include "jni.h"
namespace android {
-static jint QTagUid_tagSocketFd(JNIEnv* env, jclass,
- jobject fileDescriptor,
- jint tagNum, jint uid) {
+static jint tagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor,
+ jint tagNum, jint uid) {
int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (env->ExceptionCheck()) {
@@ -40,15 +39,14 @@
return (jint)-1;
}
- int res = qtaguid_tagSocket(userFd, tagNum, uid);
+ int res = android_tag_socket_with_uid(userFd, tagNum, uid);
if (res < 0) {
return (jint)-errno;
}
return (jint)res;
}
-static jint QTagUid_untagSocketFd(JNIEnv* env, jclass,
- jobject fileDescriptor) {
+static jint untagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor) {
int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (env->ExceptionCheck()) {
@@ -56,16 +54,14 @@
return (jint)-1;
}
- int res = qtaguid_untagSocket(userFd);
+ int res = android_untag_socket(userFd);
if (res < 0) {
return (jint)-errno;
}
return (jint)res;
}
-static jint QTagUid_setCounterSet(JNIEnv* env, jclass,
- jint setNum, jint uid) {
-
+static jint setCounterSet(JNIEnv* env, jclass, jint setNum, jint uid) {
int res = qtaguid_setCounterSet(setNum, uid);
if (res < 0) {
return (jint)-errno;
@@ -73,9 +69,7 @@
return (jint)res;
}
-static jint QTagUid_deleteTagData(JNIEnv* env, jclass,
- jint tagNum, jint uid) {
-
+static jint deleteTagData(JNIEnv* env, jclass, jint tagNum, jint uid) {
int res = qtaguid_deleteTagData(tagNum, uid);
if (res < 0) {
return (jint)-errno;
@@ -84,10 +78,10 @@
}
static const JNINativeMethod gQTagUidMethods[] = {
- { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)QTagUid_tagSocketFd},
- { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)QTagUid_untagSocketFd},
- { "native_setCounterSet", "(II)I", (void*)QTagUid_setCounterSet},
- { "native_deleteTagData", "(II)I", (void*)QTagUid_deleteTagData},
+ { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)tagSocketFd},
+ { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)untagSocketFd},
+ { "native_setCounterSet", "(II)I", (void*)setCounterSet},
+ { "native_deleteTagData", "(II)I", (void*)deleteTagData},
};
int register_android_server_NetworkManagementSocketTagger(JNIEnv* env) {
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 6063062..c42517d 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -37,7 +37,6 @@
visibility: [":__subpackages__"],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-GPL",
],
license_text: [
"NOTICE",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6ecfcec..6bcaace9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2114,8 +2114,9 @@
<permission android:name="android.permission.NFC_HANDOVER_STATUS"
android:protectionLevel="signature|privileged" />
- <!-- @hide Allows internal management of Bluetooth state when on wireless consent mode.
- <p>Not for use by third-party applications. -->
+ <!-- @SystemApi Allows internal management of Bluetooth state when on wireless consent mode.
+ <p>Not for use by third-party applications.
+ @hide -->
<permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
android:protectionLevel="signature" />
@@ -2723,6 +2724,10 @@
<permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
android:protectionLevel="signature|appop" />
+ <!-- @SystemApi @hide Allows starting activities across profiles in the same profile group. -->
+ <permission android:name="android.permission.START_CROSS_PROFILE_ACTIVITIES"
+ android:protectionLevel="signature|role" />
+
<!-- Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that they can
interact across profiles in the same profile group.
@hide -->
@@ -6234,6 +6239,11 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+ <service android:name="com.android.server.SmartStorageMaintIdler"
+ android:exported="true"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
<service android:name="com.android.server.ZramWriteback"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" >
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 48a1da1..1cc1894 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.app.usage.NetworkStatsManager;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
@@ -40,12 +41,15 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MobileRadioPowerCalculatorTest {
private static final double PRECISION = 0.00001;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+ @Mock
+ NetworkStatsManager mNetworkStatsManager;
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
@@ -90,7 +94,8 @@
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
new int[] {100, 200, 300, 400, 500}, 600);
- stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000);
+ stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000,
+ mNetworkStatsManager);
mStatsRule.setTime(12_000_000, 12_000_000);
@@ -150,7 +155,7 @@
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
new int[] {100, 200, 300, 400, 500}, 600);
- stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000);
+ stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000, mNetworkStatsManager);
mStatsRule.setTime(12_000_000, 12_000_000);
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index cee1a03..db0c934 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import android.annotation.NonNull;
+import android.app.usage.NetworkStatsManager;
import android.net.NetworkStats;
import android.os.Handler;
import android.os.Looper;
@@ -105,7 +107,8 @@
}
@Override
- protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
+ protected NetworkStats readNetworkStatsLocked(@NonNull NetworkStatsManager networkStatsManager,
+ String[] ifaces) {
return mNetworkStats;
}
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index fc44ddc..e7ce9a0 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -21,6 +21,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.app.usage.NetworkStatsManager;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
@@ -35,6 +36,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -43,6 +45,9 @@
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+ @Mock
+ NetworkStatsManager mNetworkStatsManager;
+
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0)
@@ -80,7 +85,8 @@
final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
- batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000);
+ batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000,
+ mNetworkStatsManager);
WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
@@ -113,7 +119,7 @@
final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
- batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000);
+ batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000, mNetworkStatsManager);
WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(calculator);
@@ -160,7 +166,8 @@
// Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
// on the packet counts.
- batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000);
+ batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000,
+ mNetworkStatsManager);
WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
@@ -180,7 +187,8 @@
// Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
// on the packet counts.
- batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000);
+ batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000,
+ mNetworkStatsManager);
WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(calculator);
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 5858e39..a8a6640 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -169,8 +169,7 @@
}
/**
- * Sets the Outline to the rounded rect defined by the input rect, and
- * corner radius.
+ * Sets the Outline to the rect defined by the input coordinates.
*/
public void setRect(int left, int top, int right, int bottom) {
setRoundRect(left, top, right, bottom, 0.0f);
@@ -184,7 +183,7 @@
}
/**
- * Sets the Outline to the rounded rect defined by the input rect, and corner radius.
+ * Sets the Outline to the rounded rect defined by the input coordinates and corner radius.
* <p>
* Passing a zero radius is equivalent to calling {@link #setRect(int, int, int, int)}
*/
diff --git a/identity/java/android/security/identity/CredentialDataRequest.java b/identity/java/android/security/identity/CredentialDataRequest.java
new file mode 100644
index 0000000..2a47a02
--- /dev/null
+++ b/identity/java/android/security/identity/CredentialDataRequest.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * An object representing a request for credential data.
+ */
+public class CredentialDataRequest {
+ CredentialDataRequest() {}
+
+ /**
+ * Gets the device-signed entries to request.
+ *
+ * @return the device-signed entries to request.
+ */
+ public @NonNull Map<String, Collection<String>> getDeviceSignedEntriesToRequest() {
+ return mDeviceSignedEntriesToRequest;
+ }
+
+ /**
+ * Gets the issuer-signed entries to request.
+ *
+ * @return the issuer-signed entries to request.
+ */
+ public @NonNull Map<String, Collection<String>> getIssuerSignedEntriesToRequest() {
+ return mIssuerSignedEntriesToRequest;
+ }
+
+ /**
+ * Gets whether to allow using an authentication key which use count has been exceeded.
+ *
+ * <p>By default this is set to true.
+ *
+ * @return whether to allow using an authentication key which use
+ * count has been exceeded if no other key is available.
+ */
+ public boolean isAllowUsingExhaustedKeys() {
+ return mAllowUsingExhaustedKeys;
+ }
+
+ /**
+ * Gets whether to allow using an authentication key which is expired.
+ *
+ * <p>By default this is set to false.
+ *
+ * @return whether to allow using an authentication key which is
+ * expired if no other key is available.
+ */
+ public boolean isAllowUsingExpiredKeys() {
+ return mAllowUsingExpiredKeys;
+ }
+
+ /**
+ * Gets whether to increment the use-count for the authentication key used.
+ *
+ * <p>By default this is set to true.
+ *
+ * @return whether to increment the use count of the authentication key used.
+ */
+ public boolean isIncrementUseCount() {
+ return mIncrementUseCount;
+ }
+
+ /**
+ * Gets the request message CBOR.
+ *
+ * <p>This data structure is described in the documentation for the
+ * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+ *
+ * @return the request message CBOR as described above.
+ */
+ public @Nullable byte[] getRequestMessage() {
+ return mRequestMessage;
+ }
+
+ /**
+ * Gets the reader signature.
+ *
+ * <p>This data structure is described in the documentation for the
+ * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+ *
+ * @return a {@code COSE_Sign1} structure as described above.
+ */
+ public @Nullable byte[] getReaderSignature() {
+ return mReaderSignature;
+ }
+
+ Map<String, Collection<String>> mDeviceSignedEntriesToRequest = new LinkedHashMap<>();
+ Map<String, Collection<String>> mIssuerSignedEntriesToRequest = new LinkedHashMap<>();
+ boolean mAllowUsingExhaustedKeys = true;
+ boolean mAllowUsingExpiredKeys = false;
+ boolean mIncrementUseCount = true;
+ byte[] mRequestMessage = null;
+ byte[] mReaderSignature = null;
+
+ /**
+ * A builder for {@link CredentialDataRequest}.
+ */
+ public static final class Builder {
+ private CredentialDataRequest mData;
+
+ /**
+ * Creates a new builder.
+ */
+ public Builder() {
+ mData = new CredentialDataRequest();
+ }
+
+ /**
+ * Sets the device-signed entries to request.
+ *
+ * @param entriesToRequest the device-signed entries to request.
+ */
+ public @NonNull Builder setDeviceSignedEntriesToRequest(
+ @NonNull Map<String, Collection<String>> entriesToRequest) {
+ mData.mDeviceSignedEntriesToRequest = entriesToRequest;
+ return this;
+ }
+
+ /**
+ * Sets the issuer-signed entries to request.
+ *
+ * @param entriesToRequest the issuer-signed entries to request.
+ * @return the builder.
+ */
+ public @NonNull Builder setIssuerSignedEntriesToRequest(
+ @NonNull Map<String, Collection<String>> entriesToRequest) {
+ mData.mIssuerSignedEntriesToRequest = entriesToRequest;
+ return this;
+ }
+
+ /**
+ * Sets whether to allow using an authentication key which use count has been exceeded.
+ *
+ * By default this is set to true.
+ *
+ * @param allowUsingExhaustedKeys whether to allow using an authentication key which use
+ * count has been exceeded if no other key is available.
+ * @return the builder.
+ */
+ public @NonNull Builder setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
+ mData.mAllowUsingExhaustedKeys = allowUsingExhaustedKeys;
+ return this;
+ }
+
+ /**
+ * Sets whether to allow using an authentication key which is expired.
+ *
+ * By default this is set to false.
+ *
+ * @param allowUsingExpiredKeys whether to allow using an authentication key which is
+ * expired if no other key is available.
+ * @return the builder.
+ */
+ public @NonNull Builder setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
+ mData.mAllowUsingExpiredKeys = allowUsingExpiredKeys;
+ return this;
+ }
+
+ /**
+ * Sets whether to increment the use-count for the authentication key used.
+ *
+ * By default this is set to true.
+ *
+ * @param incrementUseCount whether to increment the use count of the authentication
+ * key used.
+ * @return the builder.
+ */
+ public @NonNull Builder setIncrementUseCount(boolean incrementUseCount) {
+ mData.mIncrementUseCount = incrementUseCount;
+ return this;
+ }
+
+ /**
+ * Sets the request message CBOR.
+ *
+ * <p>This data structure is described in the documentation for the
+ * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+ *
+ * @param requestMessage the request message CBOR as described above.
+ * @return the builder.
+ */
+ public @NonNull Builder setRequestMessage(@NonNull byte[] requestMessage) {
+ mData.mRequestMessage = requestMessage;
+ return this;
+ }
+
+ /**
+ * Sets the reader signature.
+ *
+ * <p>This data structure is described in the documentation for the
+ * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+ *
+ * @param readerSignature a {@code COSE_Sign1} structure as described above.
+ * @return the builder.
+ */
+ public @NonNull Builder setReaderSignature(@NonNull byte[] readerSignature) {
+ mData.mReaderSignature = readerSignature;
+ return this;
+ }
+
+ /**
+ * Finishes building a {@link CredentialDataRequest}.
+ *
+ * @return the {@link CredentialDataRequest} object.
+ */
+ public @NonNull CredentialDataRequest build() {
+ return mData;
+ }
+ }
+}
diff --git a/identity/java/android/security/identity/CredentialDataResult.java b/identity/java/android/security/identity/CredentialDataResult.java
new file mode 100644
index 0000000..beb03af
--- /dev/null
+++ b/identity/java/android/security/identity/CredentialDataResult.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.util.Collection;
+
+
+/**
+ * An object that contains the result of retrieving data from a credential. This is used to return
+ * data requested in a {@link PresentationSession}.
+ */
+public abstract class CredentialDataResult {
+ /**
+ * @hide
+ */
+ protected CredentialDataResult() {}
+
+ /**
+ * Returns a CBOR structure containing the retrieved device-signed data.
+ *
+ * <p>This structure - along with the session transcript - may be cryptographically
+ * authenticated to prove to the reader that the data is from a trusted credential and
+ * {@link #getDeviceMac()} can be used to get a MAC.
+ *
+ * <p>The CBOR structure which is cryptographically authenticated is the
+ * {@code DeviceAuthenticationBytes} structure according to the following
+ * <a href="https://tools.ietf.org/html/rfc8610">CDDL</a> schema:
+ *
+ * <pre>
+ * DeviceAuthentication = [
+ * "DeviceAuthentication",
+ * SessionTranscript,
+ * DocType,
+ * DeviceNameSpacesBytes
+ * ]
+ *
+ * DocType = tstr
+ * SessionTranscript = any
+ * DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+ * DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
+ * </pre>
+ *
+ * <p>where
+ *
+ * <pre>
+ * DeviceNameSpaces = {
+ * * NameSpace => DeviceSignedItems
+ * }
+ *
+ * DeviceSignedItems = {
+ * + DataItemName => DataItemValue
+ * }
+ *
+ * NameSpace = tstr
+ * DataItemName = tstr
+ * DataItemValue = any
+ * </pre>
+ *
+ * <p>The returned data is the binary encoding of the {@code DeviceNameSpaces} structure
+ * as defined above.
+ *
+ * @return The bytes of the {@code DeviceNameSpaces} CBOR structure.
+ */
+ public abstract @NonNull byte[] getDeviceNameSpaces();
+
+ /**
+ * Returns a message authentication code over the {@code DeviceAuthenticationBytes} CBOR
+ * specified in {@link #getDeviceNameSpaces()}, to prove to the reader that the data
+ * is from a trusted credential.
+ *
+ * <p>The MAC proves to the reader that the data is from a trusted credential. This code is
+ * produced by using the key agreement and key derivation function from the ciphersuite
+ * with the authentication private key and the reader ephemeral public key to compute a
+ * shared message authentication code (MAC) key, then using the MAC function from the
+ * ciphersuite to compute a MAC of the authenticated data. See section 9.2.3.5 of
+ * ISO/IEC 18013-5 for details of this operation.
+ *
+ * <p>If the session transcript or reader ephemeral key wasn't set on the {@link
+ * PresentationSession} used to obtain this data no message authencation code will be produced
+ * and this method will return {@code null}.
+ *
+ * @return A COSE_Mac0 structure with the message authentication code as described above
+ * or {@code null} if the conditions specified above are not met.
+ */
+ public abstract @Nullable byte[] getDeviceMac();
+
+ /**
+ * Returns the static authentication data associated with the dynamic authentication
+ * key used to MAC the data returned by {@link #getDeviceNameSpaces()}.
+ *
+ * @return The static authentication data associated with dynamic authentication key used to
+ * MAC the data.
+ */
+ public abstract @NonNull byte[] getStaticAuthenticationData();
+
+ /**
+ * Gets the device-signed entries that was returned.
+ *
+ * @return an object to examine the entries returned.
+ */
+ public abstract @NonNull Entries getDeviceSignedEntries();
+
+ /**
+ * Gets the issuer-signed entries that was returned.
+ *
+ * @return an object to examine the entries returned.
+ */
+ public abstract @NonNull Entries getIssuerSignedEntries();
+
+ /**
+ * A class for representing data elements returned.
+ */
+ public interface Entries {
+ /** Value was successfully retrieved. */
+ int STATUS_OK = 0;
+
+ /** The entry does not exist. */
+ int STATUS_NO_SUCH_ENTRY = 1;
+
+ /** The entry was not requested. */
+ int STATUS_NOT_REQUESTED = 2;
+
+ /** The entry wasn't in the request message. */
+ int STATUS_NOT_IN_REQUEST_MESSAGE = 3;
+
+ /** The entry was not retrieved because user authentication failed. */
+ int STATUS_USER_AUTHENTICATION_FAILED = 4;
+
+ /** The entry was not retrieved because reader authentication failed. */
+ int STATUS_READER_AUTHENTICATION_FAILED = 5;
+
+ /**
+ * The entry was not retrieved because it was configured without any access
+ * control profile.
+ */
+ int STATUS_NO_ACCESS_CONTROL_PROFILES = 6;
+
+ /**
+ * Gets the names of namespaces with retrieved entries.
+ *
+ * @return collection of name of namespaces containing retrieved entries. May be empty if no
+ * data was retrieved.
+ */
+ @NonNull Collection<String> getNamespaces();
+
+ /**
+ * Get the names of all requested entries in a name space.
+ *
+ * <p>This includes the name of entries that wasn't successfully retrieved.
+ *
+ * @param namespaceName the namespace name to get entries for.
+ * @return A collection of names for the given namespace or the empty collection if no
+ * entries was returned for the given name space.
+ */
+ @NonNull Collection<String> getEntryNames(@NonNull String namespaceName);
+
+ /**
+ * Get the names of all entries that was successfully retrieved from a name space.
+ *
+ * <p>This only return entries for which {@link #getStatus(String, String)} will return
+ * {@link #STATUS_OK}.
+ *
+ * @param namespaceName the namespace name to get entries for.
+ * @return The entries in the given namespace that were successfully rerieved or the
+ * empty collection if no entries was returned for the given name space.
+ */
+ @NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName);
+
+ /**
+ * Gets the status of an entry.
+ *
+ * <p>This returns {@link #STATUS_OK} if the value was retrieved, {@link
+ * #STATUS_NO_SUCH_ENTRY} if the given entry wasn't retrieved, {@link
+ * #STATUS_NOT_REQUESTED} if it wasn't requested, {@link #STATUS_NOT_IN_REQUEST_MESSAGE} if
+ * the request message was set but the entry wasn't present in the request message, {@link
+ * #STATUS_USER_AUTHENTICATION_FAILED} if the value wasn't retrieved because the necessary
+ * user authentication wasn't performed, {@link #STATUS_READER_AUTHENTICATION_FAILED} if
+ * the supplied reader certificate chain didn't match the set of certificates the entry was
+ * provisioned with, or {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was
+ * configured without any access control profiles.
+ *
+ * @param namespaceName the namespace name of the entry.
+ * @param name the name of the entry to get the value for.
+ * @return the status indicating whether the value was retrieved and if not, why.
+ */
+ @Status int getStatus(@NonNull String namespaceName, @NonNull String name);
+
+ /**
+ * Gets the raw CBOR data for the value of an entry.
+ *
+ * <p>This should only be called on an entry for which the {@link #getStatus(String,
+ * String)} method returns {@link #STATUS_OK}.
+ *
+ * @param namespaceName the namespace name of the entry.
+ * @param name the name of the entry to get the value for.
+ * @return the raw CBOR data or {@code null} if no entry with the given name exists.
+ */
+ @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name);
+
+ /**
+ * The type of the entry status.
+ * @hide
+ */
+ @Retention(SOURCE)
+ @IntDef({STATUS_OK, STATUS_NO_SUCH_ENTRY, STATUS_NOT_REQUESTED,
+ STATUS_NOT_IN_REQUEST_MESSAGE, STATUS_USER_AUTHENTICATION_FAILED,
+ STATUS_READER_AUTHENTICATION_FAILED, STATUS_NO_ACCESS_CONTROL_PROFILES})
+ @interface Status {}
+ }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreCredentialDataResult.java b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
new file mode 100644
index 0000000..7afe3d4
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+class CredstoreCredentialDataResult extends CredentialDataResult {
+
+ ResultData mDeviceSignedResult;
+ ResultData mIssuerSignedResult;
+ CredstoreEntries mDeviceSignedEntries;
+ CredstoreEntries mIssuerSignedEntries;
+
+ CredstoreCredentialDataResult(ResultData deviceSignedResult, ResultData issuerSignedResult) {
+ mDeviceSignedResult = deviceSignedResult;
+ mIssuerSignedResult = issuerSignedResult;
+ mDeviceSignedEntries = new CredstoreEntries(deviceSignedResult);
+ mIssuerSignedEntries = new CredstoreEntries(issuerSignedResult);
+ }
+
+ @Override
+ public @NonNull byte[] getDeviceNameSpaces() {
+ return mDeviceSignedResult.getAuthenticatedData();
+ }
+
+ @Override
+ public @Nullable byte[] getDeviceMac() {
+ return mDeviceSignedResult.getMessageAuthenticationCode();
+ }
+
+ @Override
+ public @NonNull byte[] getStaticAuthenticationData() {
+ return mDeviceSignedResult.getStaticAuthenticationData();
+ }
+
+ @Override
+ public @NonNull CredentialDataResult.Entries getDeviceSignedEntries() {
+ return mDeviceSignedEntries;
+ }
+
+ @Override
+ public @NonNull CredentialDataResult.Entries getIssuerSignedEntries() {
+ return mIssuerSignedEntries;
+ }
+
+ static class CredstoreEntries implements CredentialDataResult.Entries {
+ ResultData mResultData;
+
+ CredstoreEntries(ResultData resultData) {
+ mResultData = resultData;
+ }
+
+ @Override
+ public @NonNull Collection<String> getNamespaces() {
+ return mResultData.getNamespaces();
+ }
+
+ @Override
+ public @NonNull Collection<String> getEntryNames(@NonNull String namespaceName) {
+ Collection<String> ret = mResultData.getEntryNames(namespaceName);
+ if (ret == null) {
+ ret = new LinkedList<String>();
+ }
+ return ret;
+ }
+
+ @Override
+ public @NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName) {
+ Collection<String> ret = mResultData.getRetrievedEntryNames(namespaceName);
+ if (ret == null) {
+ ret = new LinkedList<String>();
+ }
+ return ret;
+ }
+
+ @Override
+ @Status
+ public int getStatus(@NonNull String namespaceName, @NonNull String name) {
+ return mResultData.getStatus(namespaceName, name);
+ }
+
+ @Override
+ public @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name) {
+ return mResultData.getEntry(namespaceName, name);
+ }
+ }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index 6398cee..8e01105 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -58,14 +58,17 @@
private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
private Context mContext;
private ICredential mBinder;
+ private CredstorePresentationSession mSession;
CredstoreIdentityCredential(Context context, String credentialName,
@IdentityCredentialStore.Ciphersuite int cipherSuite,
- ICredential binder) {
+ ICredential binder,
+ @Nullable CredstorePresentationSession session) {
mContext = context;
mCredentialName = credentialName;
mCipherSuite = cipherSuite;
mBinder = binder;
+ mSession = session;
}
private KeyPair mEphemeralKeyPair = null;
@@ -239,6 +242,7 @@
private boolean mAllowUsingExhaustedKeys = true;
private boolean mAllowUsingExpiredKeys = false;
+ private boolean mIncrementKeyUsageCount = true;
@Override
public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
@@ -250,6 +254,11 @@
mAllowUsingExpiredKeys = allowUsingExpiredKeys;
}
+ @Override
+ public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
+ mIncrementKeyUsageCount = incrementKeyUsageCount;
+ }
+
private boolean mOperationHandleSet = false;
private long mOperationHandle = 0;
@@ -264,7 +273,8 @@
if (!mOperationHandleSet) {
try {
mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys,
- mAllowUsingExpiredKeys);
+ mAllowUsingExpiredKeys,
+ mIncrementKeyUsageCount);
mOperationHandleSet = true;
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
@@ -315,7 +325,8 @@
sessionTranscript != null ? sessionTranscript : new byte[0],
readerSignature != null ? readerSignature : new byte[0],
mAllowUsingExhaustedKeys,
- mAllowUsingExpiredKeys);
+ mAllowUsingExpiredKeys,
+ mIncrementKeyUsageCount);
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index d8d4742..fb0880c 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -126,7 +126,8 @@
ICredential credstoreCredential;
credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite);
return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite,
- credstoreCredential);
+ credstoreCredential,
+ null);
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
@@ -162,4 +163,23 @@
+ e.errorCode, e);
}
}
+
+ @Override
+ public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
+ throws CipherSuiteNotSupportedException {
+ try {
+ ISession credstoreSession = mStore.createPresentationSession(cipherSuite);
+ return new CredstorePresentationSession(mContext, cipherSuite, this, credstoreSession);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_CIPHER_SUITE_NOT_SUPPORTED) {
+ throw new CipherSuiteNotSupportedException(e.getMessage(), e);
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
}
diff --git a/identity/java/android/security/identity/CredstorePresentationSession.java b/identity/java/android/security/identity/CredstorePresentationSession.java
new file mode 100644
index 0000000..e3c6689
--- /dev/null
+++ b/identity/java/android/security/identity/CredstorePresentationSession.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+class CredstorePresentationSession extends PresentationSession {
+ private static final String TAG = "CredstorePresentationSession";
+
+ private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
+ private Context mContext;
+ private CredstoreIdentityCredentialStore mStore;
+ private ISession mBinder;
+ private Map<String, CredstoreIdentityCredential> mCredentialCache = new LinkedHashMap<>();
+ private KeyPair mEphemeralKeyPair = null;
+ private byte[] mSessionTranscript = null;
+ private boolean mOperationHandleSet = false;
+ private long mOperationHandle = 0;
+
+ CredstorePresentationSession(Context context,
+ @IdentityCredentialStore.Ciphersuite int cipherSuite,
+ CredstoreIdentityCredentialStore store,
+ ISession binder) {
+ mContext = context;
+ mCipherSuite = cipherSuite;
+ mStore = store;
+ mBinder = binder;
+ }
+
+ private void ensureEphemeralKeyPair() {
+ if (mEphemeralKeyPair != null) {
+ return;
+ }
+ try {
+ // This PKCS#12 blob is generated in credstore, using BoringSSL.
+ //
+ // The main reason for this convoluted approach and not just sending the decomposed
+ // key-pair is that this would require directly using (device-side) BouncyCastle which
+ // is tricky due to various API hiding efforts. So instead we have credstore generate
+ // this PKCS#12 blob. The blob is encrypted with no password (sadly, also, BoringSSL
+ // doesn't support not using encryption when building a PKCS#12 blob).
+ //
+ byte[] pkcs12 = mBinder.getEphemeralKeyPair();
+ String alias = "ephemeralKey";
+ char[] password = {};
+
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ByteArrayInputStream bais = new ByteArrayInputStream(pkcs12);
+ ks.load(bais, password);
+ PrivateKey privKey = (PrivateKey) ks.getKey(alias, password);
+
+ Certificate cert = ks.getCertificate(alias);
+ PublicKey pubKey = cert.getPublicKey();
+
+ mEphemeralKeyPair = new KeyPair(pubKey, privKey);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ } catch (android.os.RemoteException
+ | KeyStoreException
+ | CertificateException
+ | UnrecoverableKeyException
+ | NoSuchAlgorithmException
+ | IOException e) {
+ throw new RuntimeException("Unexpected exception ", e);
+ }
+ }
+
+ @Override
+ public @NonNull KeyPair getEphemeralKeyPair() {
+ ensureEphemeralKeyPair();
+ return mEphemeralKeyPair;
+ }
+
+ @Override
+ public void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+ throws InvalidKeyException {
+ try {
+ byte[] uncompressedForm =
+ Util.publicKeyEncodeUncompressedForm(readerEphemeralPublicKey);
+ mBinder.setReaderEphemeralPublicKey(uncompressedForm);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+
+ @Override
+ public void setSessionTranscript(@NonNull byte[] sessionTranscript) {
+ try {
+ mBinder.setSessionTranscript(sessionTranscript);
+ mSessionTranscript = sessionTranscript;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+
+ @Override
+ public @Nullable CredentialDataResult getCredentialData(@NonNull String credentialName,
+ @NonNull CredentialDataRequest request)
+ throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
+ InvalidRequestMessageException, EphemeralPublicKeyNotFoundException {
+ try {
+ // Cache the IdentityCredential to satisfy the property that AuthKey usage counts are
+ // incremented on only the _first_ getCredentialData() call.
+ //
+ CredstoreIdentityCredential credential = mCredentialCache.get(credentialName);
+ if (credential == null) {
+ ICredential credstoreCredential =
+ mBinder.getCredentialForPresentation(credentialName);
+ credential = new CredstoreIdentityCredential(mContext, credentialName,
+ mCipherSuite, credstoreCredential,
+ this);
+ mCredentialCache.put(credentialName, credential);
+
+ credential.setAllowUsingExhaustedKeys(request.isAllowUsingExhaustedKeys());
+ credential.setAllowUsingExpiredKeys(request.isAllowUsingExpiredKeys());
+ credential.setIncrementKeyUsageCount(request.isIncrementUseCount());
+ }
+
+ ResultData deviceSignedResult = credential.getEntries(
+ request.getRequestMessage(),
+ request.getDeviceSignedEntriesToRequest(),
+ mSessionTranscript,
+ request.getReaderSignature());
+
+ // By design this second getEntries() call consumes the same auth-key.
+
+ ResultData issuerSignedResult = credential.getEntries(
+ request.getRequestMessage(),
+ request.getIssuerSignedEntriesToRequest(),
+ mSessionTranscript,
+ request.getReaderSignature());
+
+ return new CredstoreCredentialDataResult(deviceSignedResult, issuerSignedResult);
+
+ } catch (SessionTranscriptMismatchException e) {
+ throw new RuntimeException("Unexpected ", e);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_NO_SUCH_CREDENTIAL) {
+ return null;
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
+ /**
+ * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+ * operation handle.
+ *
+ * @hide
+ */
+ @Override
+ public long getCredstoreOperationHandle() {
+ if (!mOperationHandleSet) {
+ try {
+ mOperationHandle = mBinder.getAuthChallenge();
+ mOperationHandleSet = true;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_NO_AUTHENTICATION_KEY_AVAILABLE) {
+ // The NoAuthenticationKeyAvailableException will be thrown when
+ // the caller proceeds to call getEntries().
+ }
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ return mOperationHandle;
+ }
+
+}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 1e68585..cdf746f 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -48,7 +48,9 @@
* encryption".
*
* @return ephemeral key pair to use to establish a secure channel with a reader.
+ * @deprecated Use {@link PresentationSession} instead.
*/
+ @Deprecated
public @NonNull abstract KeyPair createEphemeralKeyPair();
/**
@@ -58,7 +60,9 @@
* @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
* establish a secure session.
* @throws InvalidKeyException if the given key is invalid.
+ * @deprecated Use {@link PresentationSession} instead.
*/
+ @Deprecated
public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
throws InvalidKeyException;
@@ -72,7 +76,10 @@
*
* @param messagePlaintext unencrypted message to encrypt.
* @return encrypted message.
+ * @deprecated Applications should use {@link PresentationSession} and
+ * implement encryption/decryption themselves.
*/
+ @Deprecated
public @NonNull abstract byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext);
/**
@@ -86,7 +93,10 @@
* @param messageCiphertext encrypted message to decrypt.
* @return decrypted message.
* @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
+ * @deprecated Applications should use {@link PresentationSession} and
+ * implement encryption/decryption themselves.
*/
+ @Deprecated
public @NonNull abstract byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
throws MessageDecryptionException;
@@ -111,7 +121,9 @@
*
* @param allowUsingExhaustedKeys whether to allow using an authentication key which use count
* has been exceeded if no other key is available.
+ * @deprecated Use {@link PresentationSession} instead.
*/
+ @Deprecated
public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
/**
@@ -128,12 +140,36 @@
*
* @param allowUsingExpiredKeys whether to allow using an authentication key which use count
* has been exceeded if no other key is available.
+ * @deprecated Use {@link PresentationSession} instead.
*/
+ @Deprecated
public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
throw new UnsupportedOperationException();
}
/**
+ * @hide
+ *
+ * Sets whether the usage count of an authentication key should be increased. This must be
+ * called prior to calling
+ * {@link #getEntries(byte[], Map, byte[], byte[])} or using a
+ * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this object.
+ *
+ * <p>By default this is set to true.
+ *
+ * <p>This is only implemented in feature version 202201 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param incrementKeyUsageCount whether the usage count of the key should be increased.
+ * @deprecated Use {@link PresentationSession} instead.
+ */
+ public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
* operation handle.
*
@@ -149,15 +185,19 @@
* by using the {@link ResultData#getStatus(String, String)} method on each of the requested
* entries.
*
- * <p>It is the responsibility of the calling application to know if authentication is needed
- * and use e.g. {@link android.hardware.biometrics.BiometricPrompt} to make the user
- * authenticate using a {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which
- * references this object. If needed, this must be done before calling
- * {@link #getEntries(byte[], Map, byte[], byte[])}.
- *
* <p>It is permissible to call this method multiple times using the same instance but if this
* is done, the {@code sessionTranscript} parameter must be identical for each call. If this is
* not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
+ * Additionally, if this is done the same auth-key will be used.
+ *
+ * <p>The application should not make any assumptions on whether user authentication is needed.
+ * Instead, the application should request the data elements values first and then examine
+ * the returned {@link ResultData}. If {@link ResultData#STATUS_USER_AUTHENTICATION_FAILED}
+ * is returned the application should get a
+ * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
+ * object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
+ * authentication the application may call {@link #getEntries(byte[], Map, byte[], byte[])}
+ * again.
*
* <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
* from the verifier. The content can be defined in the way appropriate for the credential, but
@@ -269,7 +309,9 @@
* @throws InvalidRequestMessageException if the requestMessage is malformed.
* @throws EphemeralPublicKeyNotFoundException if the ephemeral public key was not found in
* the session transcript.
+ * @deprecated Use {@link PresentationSession} instead.
*/
+ @Deprecated
public abstract @NonNull ResultData getEntries(
@Nullable byte[] requestMessage,
@NonNull Map<String, Collection<String>> entriesToRequest,
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
index 6ccd0e8..dbb8aaa 100644
--- a/identity/java/android/security/identity/IdentityCredentialStore.java
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -209,6 +209,25 @@
@Deprecated
public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
+ /**
+ * Creates a new presentation session.
+ *
+ * <p>This method gets an object to be used for interaction with a remote verifier for
+ * presentation of one or more credentials.
+ *
+ * <p>This is only implemented in feature version 202201 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param cipherSuite the cipher suite to use for communicating with the verifier.
+ * @return The presentation session.
+ */
+ public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
+ throws CipherSuiteNotSupportedException {
+ throw new UnsupportedOperationException();
+ }
+
/** @hide */
@IntDef(value = {CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256})
@Retention(RetentionPolicy.SOURCE)
diff --git a/identity/java/android/security/identity/PresentationSession.java b/identity/java/android/security/identity/PresentationSession.java
new file mode 100644
index 0000000..afaafce
--- /dev/null
+++ b/identity/java/android/security/identity/PresentationSession.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+
+/**
+ * Class for presenting multiple documents to a remote verifier.
+ *
+ * Use {@link IdentityCredentialStore#createPresentationSession(int)} to create a {@link
+ * PresentationSession} instance.
+ */
+public abstract class PresentationSession {
+ /**
+ * @hide
+ */
+ protected PresentationSession() {}
+
+ /**
+ * Gets the ephemeral key pair to use to establish a secure channel with the verifier.
+ *
+ * <p>Applications should use this key-pair for the communications channel with the verifier
+ * using a protocol / cipher-suite appropriate for the application. One example of such a
+ * protocol is the one used for Mobile Driving Licenses, see ISO 18013-5.
+ *
+ * <p>The ephemeral key pair is tied to the {@link PresentationSession} instance so subsequent
+ * calls to this method will return the same key-pair.
+ *
+ * @return ephemeral key pair to use to establish a secure channel with a reader.
+ */
+ public @NonNull abstract KeyPair getEphemeralKeyPair();
+
+ /**
+ * Set the ephemeral public key provided by the verifier.
+ *
+ * <p>If called, this must be called before any calls to
+ * {@link #getCredentialData(String, CredentialDataRequest)}.
+ *
+ * <p>This method can only be called once per {@link PresentationSession} instance.
+ *
+ * @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
+ * establish a secure session.
+ * @throws InvalidKeyException if the given key is invalid.
+ */
+ public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+ throws InvalidKeyException;
+
+ /**
+ * Set the session transcript.
+ *
+ * <p>If called, this must be called before any calls to
+ * {@link #getCredentialData(String, CredentialDataRequest)}.
+ *
+ * <p>The X and Y coordinates of the public part of the key-pair returned by {@link
+ * #getEphemeralKeyPair()} must appear somewhere in the bytes of the passed in CBOR. Each of
+ * these coordinates must appear encoded with the most significant bits first and use the exact
+ * amount of bits indicated by the key size of the ephemeral keys. For example, if the
+ * ephemeral key is using the P-256 curve then the 32 bytes for the X coordinate encoded with
+ * the most significant bits first must appear somewhere and ditto for the 32 bytes for the Y
+ * coordinate.
+ *
+ * <p>This method can only be called once per {@link PresentationSession} instance.
+ *
+ * @param sessionTranscript the session transcript.
+ */
+ public abstract void setSessionTranscript(@NonNull byte[] sessionTranscript);
+
+ /**
+ * Retrieves data from a named credential in the current presentation session.
+ *
+ * <p>If an access control check fails for one of the requested entries or if the entry
+ * doesn't exist, the entry is simply not returned. The application can detect this
+ * by using the {@link CredentialDataResult.Entries#getStatus(String, String)} method on
+ * each of the requested entries.
+ *
+ * <p>The application should not make any assumptions on whether user authentication is needed.
+ * Instead, the application should request the data elements values first and then examine
+ * the returned {@link CredentialDataResult.Entries}. If
+ * {@link CredentialDataResult.Entries#STATUS_USER_AUTHENTICATION_FAILED} is returned the
+ * application should get a
+ * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
+ * object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
+ * authentication the application may call
+ * {@link #getCredentialData(String, CredentialDataRequest)} again.
+ *
+ * <p>It is permissible to call this method multiple times using the same credential name.
+ * If this is done the same auth-key will be used.
+ *
+ * <p>If the reader signature is set in the request parameter (via the
+ * {@link CredentialDataRequest.Builder#setReaderSignature(byte[])} method) it must contain
+ * the bytes of a {@code COSE_Sign1} structure as defined in RFC 8152. For the payload
+ * {@code nil} shall be used and the detached payload is the {@code ReaderAuthenticationBytes}
+ * CBOR described below.
+ * <pre>
+ * ReaderAuthentication = [
+ * "ReaderAuthentication",
+ * SessionTranscript,
+ * ItemsRequestBytes
+ * ]
+ *
+ * ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
+ *
+ * ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
+ * </pre>
+ *
+ * <p>where {@code ItemsRequestBytes} are the bytes of the request message set in
+ * the request parameter (via the
+ * {@link CredentialDataRequest.Builder#setRequestMessage(byte[])} method).
+ *
+ * <p>The public key corresponding to the key used to make the signature, can be found in the
+ * {@code x5chain} unprotected header element of the {@code COSE_Sign1} structure (as as
+ * described in
+ * <a href="https://tools.ietf.org/html/draft-ietf-cose-x509-08">draft-ietf-cose-x509-08</a>).
+ * There will be at least one certificate in said element and there may be more (and if so,
+ * each certificate must be signed by its successor).
+ *
+ * <p>Data elements protected by reader authentication are returned if, and only if,
+ * {@code requestMessage} is signed by the top-most certificate in the reader's certificate
+ * chain, and the data element is configured with an {@link AccessControlProfile} configured
+ * with an X.509 certificate for a key which appear in the certificate chain.
+ *
+ * <p>Note that the request message CBOR is used only for enforcing reader authentication, it's
+ * not used for determining which entries this API will return. The application is expected to
+ * have parsed the request message and filtered it according to user preference and/or consent.
+ *
+ * @param credentialName the name of the credential to retrieve.
+ * @param request the data to retrieve from the credential
+ * @return If the credential wasn't found, returns null. Otherwise a
+ * {@link CredentialDataResult} object containing entry data organized by namespace and
+ * a cryptographically authenticated representation of the same data, bound to the
+ * current session.
+ * @throws NoAuthenticationKeyAvailableException if authentication keys were never
+ * provisioned for the credential or if they
+ * are expired or exhausted their use-count.
+ * @throws InvalidRequestMessageException if the requestMessage is malformed.
+ * @throws InvalidReaderSignatureException if the reader signature is invalid, or it
+ * doesn't contain a certificate chain, or if
+ * the signature failed to validate.
+ * @throws EphemeralPublicKeyNotFoundException if the ephemeral public key was not found in
+ * the session transcript.
+ */
+ public abstract @Nullable CredentialDataResult getCredentialData(
+ @NonNull String credentialName, @NonNull CredentialDataRequest request)
+ throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
+ InvalidRequestMessageException, EphemeralPublicKeyNotFoundException;
+
+ /**
+ * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+ * operation handle.
+ *
+ * @hide
+ */
+ public abstract long getCredstoreOperationHandle();
+}
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
index 71860d2..d46f985 100644
--- a/identity/java/android/security/identity/ResultData.java
+++ b/identity/java/android/security/identity/ResultData.java
@@ -28,7 +28,10 @@
/**
* An object that contains the result of retrieving data from a credential. This is used to return
* data requested from a {@link IdentityCredential}.
+ *
+ * @deprecated Use {@link PresentationSession} instead.
*/
+@Deprecated
public abstract class ResultData {
/** Value was successfully retrieved. */
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 919a93b..05fb4c3 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.security.keystore.KeyProperties;
import android.security.maintenance.IKeystoreMaintenance;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
@@ -157,6 +158,11 @@
* Migrates a key given by the source descriptor to the location designated by the destination
* descriptor.
*
+ * If Domain::APP is selected in either source or destination, nspace must be set to
+ * {@link KeyProperties#NAMESPACE_APPLICATION}, implying the caller's UID.
+ * If the caller has the MIGRATE_ANY_KEY permission, Domain::APP may be used with
+ * other nspace values which then indicates the UID of a different application.
+ *
* @param source - The key to migrate may be specified by Domain.APP, Domain.SELINUX, or
* Domain.KEY_ID. The caller needs the permissions use, delete, and grant for the
* source namespace.
@@ -183,4 +189,20 @@
return SYSTEM_ERROR;
}
}
+
+ /**
+ * @see IKeystoreMaintenance#listEntries(int, long)
+ */
+ @Nullable
+ public static KeyDescriptor[] listEntries(int domain, long nspace) {
+ try {
+ return getService().listEntries(domain, nspace);
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "listEntries failed", e);
+ return null;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return null;
+ }
+ }
}
diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp
index 6942017..3ca457f 100644
--- a/libs/hwui/jni/NinePatch.cpp
+++ b/libs/hwui/jni/NinePatch.cpp
@@ -67,7 +67,7 @@
size_t chunkSize = env->GetArrayLength(obj);
if (chunkSize < (int) (sizeof(Res_png_9patch))) {
jniThrowRuntimeException(env, "Array too small for chunk.");
- return NULL;
+ return 0;
}
int8_t* storage = new int8_t[chunkSize];
diff --git a/libs/hwui/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp
index 72995ef..8cbb70e 100644
--- a/libs/hwui/jni/android_util_PathParser.cpp
+++ b/libs/hwui/jni/android_util_PathParser.cpp
@@ -61,7 +61,7 @@
} else {
delete pathData;
doThrowIAE(env, result.failureMessage.c_str());
- return NULL;
+ return 0;
}
}
diff --git a/libs/usb/tests/accessorytest/f_accessory.h b/libs/usb/tests/accessorytest/f_accessory.h
index 312f4ba..75e017c 100644
--- a/libs/usb/tests/accessorytest/f_accessory.h
+++ b/libs/usb/tests/accessorytest/f_accessory.h
@@ -1,148 +1,53 @@
-/*
- * Gadget Function Driver for Android USB accessories
- *
- * Copyright (C) 2011 Google, Inc.
- * Author: Mike Lockwood <lockwood@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __LINUX_USB_F_ACCESSORY_H
-#define __LINUX_USB_F_ACCESSORY_H
-
-/* Use Google Vendor ID when in accessory mode */
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_LINUX_USB_F_ACCESSORY_H
+#define _UAPI_LINUX_USB_F_ACCESSORY_H
#define USB_ACCESSORY_VENDOR_ID 0x18D1
-
-
-/* Product ID to use when in accessory mode */
#define USB_ACCESSORY_PRODUCT_ID 0x2D00
-
-/* Product ID to use when in accessory mode and adb is enabled */
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
-
-/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */
-#define ACCESSORY_STRING_MANUFACTURER 0
-#define ACCESSORY_STRING_MODEL 1
-#define ACCESSORY_STRING_DESCRIPTION 2
-#define ACCESSORY_STRING_VERSION 3
-#define ACCESSORY_STRING_URI 4
-#define ACCESSORY_STRING_SERIAL 5
-
-/* Control request for retrieving device's protocol version
- *
- * requestType: USB_DIR_IN | USB_TYPE_VENDOR
- * request: ACCESSORY_GET_PROTOCOL
- * value: 0
- * index: 0
- * data version number (16 bits little endian)
- * 1 for original accessory support
- * 2 adds audio and HID support
- */
-#define ACCESSORY_GET_PROTOCOL 51
-
-/* Control request for host to send a string to the device
- *
- * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
- * request: ACCESSORY_SEND_STRING
- * value: 0
- * index: string ID
- * data zero terminated UTF8 string
- *
- * The device can later retrieve these strings via the
- * ACCESSORY_GET_STRING_* ioctls
- */
-#define ACCESSORY_SEND_STRING 52
-
-/* Control request for starting device in accessory mode.
- * The host sends this after setting all its strings to the device.
- *
- * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
- * request: ACCESSORY_START
- * value: 0
- * index: 0
- * data none
- */
-#define ACCESSORY_START 53
-
-/* Control request for registering a HID device.
- * Upon registering, a unique ID is sent by the accessory in the
- * value parameter. This ID will be used for future commands for
- * the device
- *
- * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
- * request: ACCESSORY_REGISTER_HID_DEVICE
- * value: Accessory assigned ID for the HID device
- * index: total length of the HID report descriptor
- * data none
- */
-#define ACCESSORY_REGISTER_HID 54
-
-/* Control request for unregistering a HID device.
- *
- * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
- * request: ACCESSORY_REGISTER_HID
- * value: Accessory assigned ID for the HID device
- * index: 0
- * data none
- */
-#define ACCESSORY_UNREGISTER_HID 55
-
-/* Control request for sending the HID report descriptor.
- * If the HID descriptor is longer than the endpoint zero max packet size,
- * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC
- * commands. The data for the descriptor must be sent sequentially
- * if multiple packets are needed.
- *
- * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
- * request: ACCESSORY_SET_HID_REPORT_DESC
- * value: Accessory assigned ID for the HID device
- * index: offset of data in descriptor
- * (needed when HID descriptor is too big for one packet)
- * data the HID report descriptor
- */
-#define ACCESSORY_SET_HID_REPORT_DESC 56
-
-/* Control request for sending HID events.
- *
- * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
- * request: ACCESSORY_SEND_HID_EVENT
- * value: Accessory assigned ID for the HID device
- * index: 0
- * data the HID report for the event
- */
-#define ACCESSORY_SEND_HID_EVENT 57
-
-/* Control request for setting the audio mode.
- *
- * requestType: USB_DIR_OUT | USB_TYPE_VENDOR
- * request: ACCESSORY_SET_AUDIO_MODE
- * value: 0 - no audio
- * 1 - device to host, 44100 16-bit stereo PCM
- * index: 0
- * data the HID report for the event
- */
-#define ACCESSORY_SET_AUDIO_MODE 58
-
-
-
-/* ioctls for retrieving strings set by the host */
-#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
-#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
-#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256])
-#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256])
-#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256])
-#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256])
-/* returns 1 if there is a start request pending */
-#define ACCESSORY_IS_START_REQUESTED _IO('M', 7)
-/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */
-#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8)
-
-#endif /* __LINUX_USB_F_ACCESSORY_H */
+#define ACCESSORY_STRING_MANUFACTURER 0
+#define ACCESSORY_STRING_MODEL 1
+#define ACCESSORY_STRING_DESCRIPTION 2
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_STRING_VERSION 3
+#define ACCESSORY_STRING_URI 4
+#define ACCESSORY_STRING_SERIAL 5
+#define ACCESSORY_GET_PROTOCOL 51
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_SEND_STRING 52
+#define ACCESSORY_START 53
+#define ACCESSORY_REGISTER_HID 54
+#define ACCESSORY_UNREGISTER_HID 55
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_SET_HID_REPORT_DESC 56
+#define ACCESSORY_SEND_HID_EVENT 57
+#define ACCESSORY_SET_AUDIO_MODE 58
+#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
+#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256])
+#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256])
+#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256])
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256])
+#define ACCESSORY_IS_START_REQUESTED _IO('M', 7)
+#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
diff --git a/media/OWNERS b/media/OWNERS
index 0aff43e..5f50137 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -3,7 +3,6 @@
essick@google.com
etalvala@google.com
hdmoon@google.com
-hkuang@google.com
hunga@google.com
insun@google.com
jaewan@google.com
diff --git a/media/java/Android.bp b/media/java/Android.bp
index eeaf6e9..c7c1d54 100644
--- a/media/java/Android.bp
+++ b/media/java/Android.bp
@@ -8,7 +8,7 @@
}
filegroup {
- name: "framework-media-sources",
+ name: "framework-media-non-updatable-sources",
srcs: [
"**/*.java",
"**/*.aidl",
diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java
index 19ea2de..d1bb41e 100644
--- a/media/java/android/media/BtProfileConnectionInfo.java
+++ b/media/java/android/media/BtProfileConnectionInfo.java
@@ -34,7 +34,7 @@
/** @hide */
@IntDef({
BluetoothProfile.A2DP,
- BluetoothProfile.A2DP_SINK, // Can only be set by BtHelper
+ BluetoothProfile.A2DP_SINK,
BluetoothProfile.HEADSET, // Can only be set by BtHelper
BluetoothProfile.HEARING_AID,
BluetoothProfile.LE_AUDIO,
@@ -105,6 +105,16 @@
}
/**
+ * Constructor for A2dp sink info
+ * The {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+ *
+ * @param volume of device -1 to ignore value
+ */
+ public static @NonNull BtProfileConnectionInfo a2dpSinkInfo(int volume) {
+ return new BtProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false);
+ }
+
+ /**
* Constructor for hearing aid info
*
* @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY}
diff --git a/media/java/android/media/tv/OWNERS b/media/java/android/media/tv/OWNERS
index 33acd0d..fa04293 100644
--- a/media/java/android/media/tv/OWNERS
+++ b/media/java/android/media/tv/OWNERS
@@ -1,6 +1,6 @@
-nchalko@google.com
quxiangfang@google.com
shubang@google.com
+hgchen@google.com
# For android remote service
per-file ITvRemoteServiceInput.aidl = file:/media/lib/tvremote/OWNERS
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index efd9d10..420f4ef 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -18,6 +18,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -27,6 +28,8 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
+import android.media.AudioDeviceInfo;
+import android.media.AudioFormat.Encoding;
import android.media.PlaybackParams;
import android.net.Uri;
import android.os.Binder;
@@ -59,6 +62,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -2998,6 +3002,16 @@
return false;
}
+ /**
+ * Override default audio sink from audio policy.
+ *
+ * @param audioType device type of the audio sink to override with.
+ * @param audioAddress device address of the audio sink to override with.
+ * @param samplingRate desired sampling rate. Use default when it's 0.
+ * @param channelMask desired channel mask. Use default when it's
+ * AudioFormat.CHANNEL_OUT_DEFAULT.
+ * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
+ */
public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
int channelMask, int format) {
try {
@@ -3007,5 +3021,27 @@
throw new RuntimeException(e);
}
}
+
+ /**
+ * Override default audio sink from audio policy.
+ *
+ * @param device {@link android.media.AudioDeviceInfo} to use.
+ * @param samplingRate desired sampling rate. Use default when it's 0.
+ * @param channelMask desired channel mask. Use default when it's
+ * AudioFormat.CHANNEL_OUT_DEFAULT.
+ * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
+ */
+ public void overrideAudioSink(@NonNull AudioDeviceInfo device,
+ @IntRange(from = 0) int samplingRate,
+ int channelMask, @Encoding int format) {
+ Objects.requireNonNull(device);
+ try {
+ mInterface.overrideAudioSink(
+ AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
+ device.getAddress(), samplingRate, channelMask, format);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
}
diff --git a/media/java/android/media/tv/tunerresourcemanager/OWNER b/media/java/android/media/tv/tunerresourcemanager/OWNER
index 76b84d9..0eb1c31 100644
--- a/media/java/android/media/tv/tunerresourcemanager/OWNER
+++ b/media/java/android/media/tv/tunerresourcemanager/OWNER
@@ -1,4 +1,3 @@
-amyjojo@google.com
-nchalko@google.com
quxiangfang@google.com
-shubang@google.com
\ No newline at end of file
+shubang@google.com
+kemiyagi@google.com
\ No newline at end of file
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 773cdc9..50bb79c 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -275,118 +275,104 @@
float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
std::vector<std::any>& garbage)
{
- // oldTrack and newTrack are placeholders to be released by garbage without the lock.
- sp<AudioTrack> oldTrack;
- sp<AudioTrack> newTrack;
- status_t status = NO_ERROR;
+ ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
+ " priority=%d, loop=%d, rate=%f)",
+ __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
+ priority, loop, rate);
- {
- ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
- " priority=%d, loop=%d, rate=%f)",
- __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
- priority, loop, rate);
+ // initialize track
+ const audio_stream_type_t streamType =
+ AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
+ const int32_t channelCount = sound->getChannelCount();
+ const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
+ size_t frameCount = 0;
- // initialize track
- const audio_stream_type_t streamType =
- AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
- const int32_t channelCount = sound->getChannelCount();
- const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
- size_t frameCount = 0;
+ if (loop) {
+ const audio_format_t format = sound->getFormat();
+ const size_t frameSize = audio_is_linear_pcm(format)
+ ? channelCount * audio_bytes_per_sample(format) : 1;
+ frameCount = sound->getSizeInBytes() / frameSize;
+ }
- if (loop) {
- const audio_format_t format = sound->getFormat();
- const size_t frameSize = audio_is_linear_pcm(format)
- ? channelCount * audio_bytes_per_sample(format) : 1;
- frameCount = sound->getSizeInBytes() / frameSize;
- }
-
- // check if the existing track has the same sound id.
- if (mAudioTrack != nullptr && mSoundID == sound->getSoundID()) {
+ if (mAudioTrack != nullptr) {
+ if (mSoundID == sound->getSoundID()
+ && mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
+ // Reuse the old track if the soundID matches.
// the sample rate may fail to change if the audio track is a fast track.
- if (mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
- newTrack = mAudioTrack;
- ALOGV("%s: reusing track %p for sound %d",
- __func__, mAudioTrack.get(), sound->getSoundID());
- }
- }
- if (newTrack == nullptr) {
- // mToggle toggles each time a track is started on a given stream.
- // The toggle is concatenated with the Stream address and passed to AudioTrack
- // as callback user data. This enables the detection of callbacks received from the old
- // audio track while the new one is being started and avoids processing them with
- // wrong audio audio buffer size (mAudioBufferSize)
- auto toggle = mToggle ^ 1;
- // NOLINTNEXTLINE(performance-no-int-to-ptr)
- void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
- audio_channel_mask_t soundChannelMask = sound->getChannelMask();
- // When sound contains a valid channel mask, use it as is.
- // Otherwise, use stream count to calculate channel mask.
- audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
- ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
-
- // do not create a new audio track if current track is compatible with sound parameters
-
- android::content::AttributionSourceState attributionSource;
- attributionSource.packageName = mStreamManager->getOpPackageName();
- attributionSource.token = sp<BBinder>::make();
- // TODO b/182469354 make consistent with AudioRecord, add util for native source
- newTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
- channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
- staticCallback, userData,
- 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
- AudioTrack::TRANSFER_DEFAULT,
- nullptr /*offloadInfo*/, attributionSource,
- mStreamManager->getAttributes(),
- false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);
- // Set caller name so it can be logged in destructor.
- // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
- newTrack->setCallerName("soundpool");
- oldTrack = mAudioTrack;
- status = newTrack->initCheck();
- if (status != NO_ERROR) {
- ALOGE("%s: error creating AudioTrack", __func__);
- // newTrack goes out of scope, so reference count drops to zero
- goto exit;
- }
- // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
- mToggle = toggle;
- mAudioTrack = newTrack;
- ALOGV("%s: using new track %p for sound %d",
- __func__, newTrack.get(), sound->getSoundID());
- }
- if (mMuted) {
- newTrack->setVolume(0.0f, 0.0f);
+ ALOGV("%s: reusing track %p for sound %d",
+ __func__, mAudioTrack.get(), sound->getSoundID());
} else {
- newTrack->setVolume(leftVolume, rightVolume);
+ // If reuse not possible, move mAudioTrack to garbage, set to nullptr.
+ garbage.emplace_back(std::move(mAudioTrack));
+ mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
}
- newTrack->setLoop(0, frameCount, loop);
- mAudioTrack->start();
- mSound = sound;
- mSoundID = sound->getSoundID();
- mPriority = priority;
- mLoop = loop;
- mLeftVolume = leftVolume;
- mRightVolume = rightVolume;
- mRate = rate;
- mState = PLAYING;
- mStopTimeNs = 0;
- mStreamID = nextStreamID; // prefer this to be the last, as it is an atomic sync point
}
+ if (mAudioTrack == nullptr) {
+ // mToggle toggles each time a track is started on a given stream.
+ // The toggle is concatenated with the Stream address and passed to AudioTrack
+ // as callback user data. This enables the detection of callbacks received from the old
+ // audio track while the new one is being started and avoids processing them with
+ // wrong audio audio buffer size (mAudioBufferSize)
+ auto toggle = mToggle ^ 1;
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
+ audio_channel_mask_t soundChannelMask = sound->getChannelMask();
+ // When sound contains a valid channel mask, use it as is.
+ // Otherwise, use stream count to calculate channel mask.
+ audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
+ ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
-exit:
- ALOGV("%s: delete oldTrack %p", __func__, oldTrack.get());
- if (status != NO_ERROR) {
- // TODO: should we consider keeping the soundID if the old track is OK?
- // Do not attempt to restart this track (should we remove the stream id?)
- mState = IDLE;
- mSoundID = 0;
- mSound.reset();
- mAudioTrack.clear(); // actual release from garbage
+ // do not create a new audio track if current track is compatible with sound parameters
+
+ android::content::AttributionSourceState attributionSource;
+ attributionSource.packageName = mStreamManager->getOpPackageName();
+ attributionSource.token = sp<BBinder>::make();
+ // TODO b/182469354 make consistent with AudioRecord, add util for native source
+ mAudioTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
+ channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
+ staticCallback, userData,
+ 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
+ AudioTrack::TRANSFER_DEFAULT,
+ nullptr /*offloadInfo*/, attributionSource,
+ mStreamManager->getAttributes(),
+ false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);
+ // Set caller name so it can be logged in destructor.
+ // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
+ mAudioTrack->setCallerName("soundpool");
+
+ if (status_t status = mAudioTrack->initCheck();
+ status != NO_ERROR) {
+ ALOGE("%s: error %d creating AudioTrack", __func__, status);
+ // TODO: should we consider keeping the soundID and reusing the old track?
+ mState = IDLE;
+ mSoundID = 0;
+ mSound.reset();
+ garbage.emplace_back(std::move(mAudioTrack)); // remove mAudioTrack.
+ mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
+ return;
+ }
+ // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
+ mToggle = toggle;
+ ALOGV("%s: using new track %p for sound %d",
+ __func__, mAudioTrack.get(), sound->getSoundID());
}
-
- // move tracks to garbage to be released later outside of lock.
- if (newTrack) garbage.emplace_back(std::move(newTrack));
- if (oldTrack) garbage.emplace_back(std::move(oldTrack));
+ if (mMuted) {
+ mAudioTrack->setVolume(0.f, 0.f);
+ } else {
+ mAudioTrack->setVolume(leftVolume, rightVolume);
+ }
+ mAudioTrack->setLoop(0, frameCount, loop);
+ mAudioTrack->start();
+ mSound = sound;
+ mSoundID = sound->getSoundID();
+ mPriority = priority;
+ mLoop = loop;
+ mLeftVolume = leftVolume;
+ mRightVolume = rightVolume;
+ mRate = rate;
+ mState = PLAYING;
+ mStopTimeNs = 0;
+ mStreamID = nextStreamID; // prefer this to be the last, as it is an atomic sync point
}
/* static */
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java
new file mode 100644
index 0000000..fd66d3b
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import static org.junit.Assert.assertEquals;
+
+import android.bluetooth.BluetoothProfile;
+import android.media.BtProfileConnectionInfo;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BtProfileConnectionInfoTest {
+
+ @Test
+ public void testCoverageA2dp() {
+ final boolean supprNoisy = false;
+ final int volume = 42;
+ final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpInfo(supprNoisy, volume);
+ assertEquals(info.getProfile(), BluetoothProfile.A2DP);
+ assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+ assertEquals(info.getVolume(), volume);
+ }
+
+ @Test
+ public void testCoverageA2dpSink() {
+ final int volume = 42;
+ final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpSinkInfo(volume);
+ assertEquals(info.getProfile(), BluetoothProfile.A2DP_SINK);
+ assertEquals(info.getVolume(), volume);
+ }
+
+ @Test
+ public void testCoveragehearingAid() {
+ final boolean supprNoisy = true;
+ final BtProfileConnectionInfo info = BtProfileConnectionInfo.hearingAidInfo(supprNoisy);
+ assertEquals(info.getProfile(), BluetoothProfile.HEARING_AID);
+ assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+ }
+
+ @Test
+ public void testCoverageLeAudio() {
+ final boolean supprNoisy = false;
+ final boolean isLeOutput = true;
+ final BtProfileConnectionInfo info = BtProfileConnectionInfo.leAudio(supprNoisy,
+ isLeOutput);
+ assertEquals(info.getProfile(), BluetoothProfile.LE_AUDIO);
+ assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+ assertEquals(info.getIsLeOutput(), isLeOutput);
+ }
+}
+
diff --git a/media/tests/TunerTest/OWNERS b/media/tests/TunerTest/OWNERS
index 73ea663..7554889 100644
--- a/media/tests/TunerTest/OWNERS
+++ b/media/tests/TunerTest/OWNERS
@@ -1,4 +1,4 @@
-amyjojo@google.com
-nchalko@google.com
quxiangfang@google.com
shubang@google.com
+hgchen@google.com
+kemiyagi@google.com
\ No newline at end of file
diff --git a/native/android/libandroid_net.map.txt b/native/android/libandroid_net.map.txt
index a6c1b50..32fd734 100644
--- a/native/android/libandroid_net.map.txt
+++ b/native/android/libandroid_net.map.txt
@@ -18,6 +18,10 @@
android_getprocnetwork; # llndk
android_setprocdns; # llndk
android_getprocdns; # llndk
+ # These functions have been part of the NDK since API 33.
+ android_tag_socket_with_uid; # llndk
+ android_tag_socket; # llndk
+ android_untag_socket; # llndk
local:
*;
};
diff --git a/native/android/net.c b/native/android/net.c
index e2f36a7..d7c22e1 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -161,3 +161,15 @@
void android_res_cancel(int nsend_fd) {
resNetworkCancel(nsend_fd);
}
+
+int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) {
+ return tagSocket(sockfd, tag, uid);
+}
+
+int android_tag_socket(int sockfd, int tag) {
+ return tagSocket(sockfd, tag, -1);
+}
+
+int android_untag_socket(int sockfd) {
+ return untagSocket(sockfd);
+}
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
index f684a4d..d33666d 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
@@ -545,6 +545,15 @@
}
/**
+ * Collects tagged summary results and sets summary enumeration mode.
+ * @throws RemoteException
+ */
+ void startTaggedSummaryEnumeration() throws RemoteException {
+ mSummary = mSession.getTaggedSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp);
+ mEnumerationIndex = 0;
+ }
+
+ /**
* Collects history results for uid and resets history enumeration index.
*/
void startHistoryEnumeration(int uid, int tag, int state) {
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index a316b8a..f74edb1 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -54,6 +54,7 @@
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Provides access to network usage history and statistics. Usage data is collected in
@@ -141,7 +142,15 @@
setAugmentWithSubscriptionPlan(true);
}
- /** @hide */
+ /**
+ * Set poll on open flag to indicate the poll is needed before service gets statistics
+ * result. This is default enabled. However, for any non-privileged caller, the poll might
+ * be omitted in case of rate limiting.
+ *
+ * @param pollOnOpen true if poll is needed.
+ * @hide
+ */
+ // @SystemApi(client = MODULE_LIBRARIES)
public void setPollOnOpen(boolean pollOnOpen) {
if (pollOnOpen) {
mFlags |= FLAG_POLL_ON_OPEN;
@@ -368,7 +377,7 @@
* @return Statistics which is described above.
* @hide
*/
- @Nullable
+ @NonNull
// @SystemApi(client = MODULE_LIBRARIES)
@WorkerThread
public NetworkStats querySummary(@NonNull NetworkTemplate template, long startTime,
@@ -385,6 +394,39 @@
}
/**
+ * Query tagged network usage statistics summaries.
+ *
+ * The results will only include tagged traffic made by UIDs belonging to the calling user
+ * profile. The results are aggregated over time, so that all buckets will have the same
+ * start and end timestamps as the passed arguments. Not aggregated over state, uid,
+ * default network, metered, or roaming.
+ * This may take a long time, and apps should avoid calling this on their main thread.
+ *
+ * @param template Template used to match networks. See {@link NetworkTemplate}.
+ * @param startTime Start of period, in milliseconds since the Unix epoch, see
+ * {@link System#currentTimeMillis}.
+ * @param endTime End of period, in milliseconds since the Unix epoch, see
+ * {@link System#currentTimeMillis}.
+ * @return Statistics which is described above.
+ * @hide
+ */
+ @NonNull
+ // @SystemApi(client = MODULE_LIBRARIES)
+ @WorkerThread
+ public NetworkStats queryTaggedSummary(@NonNull NetworkTemplate template, long startTime,
+ long endTime) throws SecurityException {
+ try {
+ NetworkStats result =
+ new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
+ result.startTaggedSummaryEnumeration();
+ return result;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return null; // To make the compiler happy.
+ }
+
+ /**
* Query network usage statistics details for a given uid.
* This may take a long time, and apps should avoid calling this on their main thread.
*
@@ -535,6 +577,31 @@
return result;
}
+ /**
+ * Query realtime network usage statistics details with interfaces constrains.
+ * Return snapshot of current UID statistics, including any {@link TrafficStats#UID_TETHERING},
+ * video calling data usage and count of network operations that set by
+ * {@link TrafficStats#incrementOperationCount}. The returned data doesn't include any
+ * statistics that is reported by {@link NetworkStatsProvider}.
+ *
+ * @param requiredIfaces A list of interfaces the stats should be restricted to, or
+ * {@link NetworkStats#INTERFACES_ALL}.
+ *
+ * @hide
+ */
+ //@SystemApi
+ @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+ @NonNull public android.net.NetworkStats getDetailedUidStats(
+ @NonNull Set<String> requiredIfaces) {
+ Objects.requireNonNull(requiredIfaces, "requiredIfaces cannot be null");
+ try {
+ return mService.getDetailedUidStats(requiredIfaces.toArray(new String[0]));
+ } catch (RemoteException e) {
+ if (DBG) Log.d(TAG, "Remote exception when get detailed uid stats");
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public void registerUsageCallback(NetworkTemplate template, int networkType,
long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
@@ -804,4 +871,74 @@
return msg.getData().getParcelable(key);
}
}
+
+ /**
+ * Mark given UID as being in foreground for stats purposes.
+ *
+ * @hide
+ */
+ // @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public void setUidForeground(int uid, boolean uidForeground) {
+ try {
+ mService.setUidForeground(uid, uidForeground);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Advise persistence threshold; may be overridden internally.
+ *
+ * @hide
+ */
+ // @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public void advisePersistThreshold(long thresholdBytes) {
+ try {
+ mService.advisePersistThreshold(thresholdBytes);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Force update of statistics.
+ *
+ * @hide
+ */
+ // @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public void forceUpdate() {
+ try {
+ mService.forceUpdate();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the warning and limit to all registered custom network stats providers.
+ * Note that invocation of any interface will be sent to all providers.
+ *
+ * @hide
+ */
+ // @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
+ long limit) {
+ try {
+ mService.setStatsProviderWarningAndLimitAsync(iface, warning, limit);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
index 12937b5..a4babb5 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
@@ -94,4 +94,16 @@
/** Registers a network stats provider */
INetworkStatsProviderCallback registerNetworkStatsProvider(String tag,
in INetworkStatsProvider provider);
+
+ /** Mark given UID as being in foreground for stats purposes. */
+ void setUidForeground(int uid, boolean uidForeground);
+
+ /** Advise persistence threshold; may be overridden internally. */
+ void advisePersistThreshold(long thresholdBytes);
+
+ /**
+ * Set the warning and limit to all registered custom network stats providers.
+ * Note that invocation of any interface will be sent to all providers.
+ */
+ void setStatsProviderWarningAndLimitAsync(String iface, long warning, long limit);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
index dfedf66..babe0bf 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
@@ -46,6 +46,10 @@
*/
@UnsupportedAppUsage
NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
+
+ /** Return network layer usage summary per UID for tagged traffic that matches template. */
+ NetworkStats getTaggedSummaryForAllUid(in NetworkTemplate template, long start, long end);
+
/** Return historical network layer stats for specific UID traffic that matches template. */
@UnsupportedAppUsage
NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int set, int tag, int fields);
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
index 0d15dff..49aa99b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
@@ -17,6 +17,7 @@
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -25,7 +26,6 @@
import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.net.annotations.PolicyDirection;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -41,6 +41,8 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
@@ -88,6 +90,11 @@
@SystemApi(client = MODULE_LIBRARIES)
public static final int DIRECTION_FWD = 2;
+ /** @hide */
+ @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PolicyDirection {}
+
/**
* The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
*
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index b00fea4..9d532e7 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -383,6 +383,95 @@
this.operations += another.operations;
}
+ /**
+ * @return interface name of this entry.
+ * @hide
+ */
+ @Nullable public String getIface() {
+ return iface;
+ }
+
+ /**
+ * @return the uid of this entry.
+ */
+ public int getUid() {
+ return uid;
+ }
+
+ /**
+ * @return the set state of this entry. Should be one of the following
+ * values: {@link #SET_DEFAULT}, {@link #SET_FOREGROUND}.
+ */
+ @State public int getSet() {
+ return set;
+ }
+
+ /**
+ * @return the tag value of this entry.
+ */
+ public int getTag() {
+ return tag;
+ }
+
+ /**
+ * @return the metered state. Should be one of the following
+ * values: {link #METERED_YES}, {link #METERED_NO}.
+ */
+ @Meteredness public int getMetered() {
+ return metered;
+ }
+
+ /**
+ * @return the roaming state. Should be one of the following
+ * values: {link #ROAMING_YES}, {link #ROAMING_NO}.
+ */
+ @Roaming public int getRoaming() {
+ return roaming;
+ }
+
+ /**
+ * @return the default network state. Should be one of the following
+ * values: {link #DEFAULT_NETWORK_YES}, {link #DEFAULT_NETWORK_NO}.
+ */
+ @DefaultNetwork public int getDefaultNetwork() {
+ return defaultNetwork;
+ }
+
+ /**
+ * @return the number of received bytes.
+ */
+ public long getRxBytes() {
+ return rxBytes;
+ }
+
+ /**
+ * @return the number of received packets.
+ */
+ public long getRxPackets() {
+ return rxPackets;
+ }
+
+ /**
+ * @return the number of transmitted bytes.
+ */
+ public long getTxBytes() {
+ return txBytes;
+ }
+
+ /**
+ * @return the number of transmitted packets.
+ */
+ public long getTxPackets() {
+ return txPackets;
+ }
+
+ /**
+ * @return the count of network operations performed for this entry.
+ */
+ public long getOperations() {
+ return operations;
+ }
+
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
@@ -593,7 +682,7 @@
* @hide
*/
@UnsupportedAppUsage
- public Entry getValues(int i, Entry recycle) {
+ public Entry getValues(int i, @Nullable Entry recycle) {
final Entry entry = recycle != null ? recycle : new Entry();
entry.iface = iface[i];
entry.uid = uid[i];
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index 7935d28..9f9d73f 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -46,8 +46,6 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastDataInput;
-import com.android.internal.util.FastDataOutput;
import com.android.internal.util.FileRotator;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkStatsUtils;
@@ -58,6 +56,7 @@
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -83,9 +82,6 @@
/** File header magic number: "ANET" */
private static final int FILE_MAGIC = 0x414E4554;
- /** Default buffer size from BufferedInputStream */
- private static final int BUFFER_SIZE = 8192;
-
private static final int VERSION_NETWORK_INIT = 1;
private static final int VERSION_UID_INIT = 1;
@@ -439,8 +435,7 @@
@Override
public void read(InputStream in) throws IOException {
- final FastDataInput dataIn = new FastDataInput(in, BUFFER_SIZE);
- read(dataIn);
+ read((DataInput) new DataInputStream(in));
}
private void read(DataInput in) throws IOException {
@@ -479,9 +474,8 @@
@Override
public void write(OutputStream out) throws IOException {
- final FastDataOutput dataOut = new FastDataOutput(out, BUFFER_SIZE);
- write(dataOut);
- dataOut.flush();
+ write((DataOutput) new DataOutputStream(out));
+ out.flush();
}
private void write(DataOutput out) throws IOException {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
index d8feb88..1af32bf 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -27,8 +28,8 @@
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Build;
+import android.os.IBinder;
import android.os.RemoteException;
-import android.os.ServiceManager;
import com.android.server.NetworkManagementSocketTagger;
@@ -36,6 +37,8 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;
@@ -53,6 +56,7 @@
* use {@link NetworkStatsManager} instead.
*/
public class TrafficStats {
+ private static final String TAG = TrafficStats.class.getSimpleName();
/**
* The return value to indicate that the device does not support the statistic.
*/
@@ -173,12 +177,25 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private synchronized static INetworkStatsService getStatsService() {
if (sStatsService == null) {
- sStatsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ sStatsService = getStatsBinder();
}
return sStatsService;
}
+ @Nullable
+ private static INetworkStatsService getStatsBinder() {
+ try {
+ final Method getServiceMethod = Class.forName("android.os.ServiceManager")
+ .getDeclaredMethod("getService", new Class[]{String.class});
+ final IBinder binder = (IBinder) getServiceMethod.invoke(
+ null, Context.NETWORK_STATS_SERVICE);
+ return INetworkStatsService.Stub.asInterface(binder);
+ } catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException
+ | InvocationTargetException e) {
+ throw new NullPointerException("Cannot get INetworkStatsService: " + e);
+ }
+ }
+
/**
* Snapshot of {@link NetworkStats} when the currently active profiling
* session started, or {@code null} if no session active.
@@ -265,6 +282,18 @@
}
/**
+ * Set active tag to use when accounting {@link Socket} traffic originating
+ * from the current thread. The tag used internally is well-defined to
+ * distinguish all download provider traffic.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static void setThreadStatsTagDownload() {
+ setThreadStatsTag(TAG_SYSTEM_DOWNLOAD);
+ }
+
+ /**
* Get the active tag used when accounting {@link Socket} traffic originating
* from the current thread. Only one active tag per thread is supported.
* {@link #tagSocket(Socket)}.
diff --git a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
index e35f6a6..1eb52fb 100644
--- a/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
+++ b/packages/ConnectivityT/framework-t/src/com/android/server/NetworkManagementSocketTagger.java
@@ -26,6 +26,7 @@
/**
* Assigns tags to sockets for traffic stats.
+ * @hide
*/
public final class NetworkManagementSocketTagger extends SocketTagger {
private static final String TAG = "NetworkManagementSocketTagger";
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
deleted file mode 100644
index 0e9a9da..0000000
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.net;
-
-import android.annotation.NonNull;
-import android.net.NetworkStats;
-import android.net.NetworkTemplate;
-
-public abstract class NetworkStatsManagerInternal {
- /** Return network layer usage total for traffic that matches template. */
- public abstract long getNetworkTotalBytes(NetworkTemplate template, long start, long end);
-
- /** Return network layer usage per-UID for traffic that matches template. */
- public abstract NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end);
-
- /** Mark given UID as being in foreground for stats purposes. */
- public abstract void setUidForeground(int uid, boolean uidForeground);
-
- /** Advise persistance threshold; may be overridden internally. */
- public abstract void advisePersistThreshold(long thresholdBytes);
-
- /** Force update of statistics. */
- public abstract void forceUpdate();
-
- /**
- * Set the warning and limit to all registered custom network stats providers.
- * Note that invocation of any interface will be sent to all providers.
- */
- public abstract void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
- long limit);
-}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 97281ed..e15acf3 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -44,6 +44,7 @@
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.net.TrafficStats.UID_TETHERING;
import static android.net.TrafficStats.UNSUPPORTED;
import static android.os.Trace.TRACE_TAG_NETWORK;
import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED;
@@ -70,7 +71,7 @@
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
@@ -89,13 +90,13 @@
import android.database.ContentObserver;
import android.net.DataUsageRequest;
import android.net.INetd;
-import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
import android.net.NetworkIdentitySet;
+import android.net.NetworkPolicyManager;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStateSnapshot;
@@ -106,6 +107,7 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
+import android.net.TetherStatsParcel;
import android.net.TetheringManager;
import android.net.TrafficStats;
import android.net.UnderlyingNetworkInfo;
@@ -120,12 +122,12 @@
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
@@ -148,12 +150,12 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FileRotator;
+import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.BestClock;
import com.android.net.module.util.BinderUtils;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkStatsUtils;
import com.android.net.module.util.PermissionUtils;
-import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import java.io.File;
@@ -207,8 +209,15 @@
private static final String TAG_NETSTATS_ERROR = "netstats_error";
+ /**
+ * EventLog tags used when logging into the event log. Note the values must be sync with
+ * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct
+ * name translation.
+ */
+ private static final int LOG_TAG_NETSTATS_MOBILE_SAMPLE = 51100;
+ private static final int LOG_TAG_NETSTATS_WIFI_SAMPLE = 51101;
+
private final Context mContext;
- private final INetworkManagementService mNetworkManager;
private final NetworkStatsFactory mStatsFactory;
private final AlarmManager mAlarmManager;
private final Clock mClock;
@@ -223,6 +232,9 @@
private final ContentObserver mContentObserver;
private final ContentResolver mContentResolver;
+ protected INetd mNetd;
+ private final AlertObserver mAlertObserver = new AlertObserver();
+
@VisibleForTesting
public static final String ACTION_NETWORK_STATS_POLL =
"com.android.server.action.NETWORK_STATS_POLL";
@@ -405,20 +417,20 @@
}
}
- public static NetworkStatsService create(Context context,
- INetworkManagementService networkManager) {
+ /** Creates a new NetworkStatsService */
+ public static NetworkStatsService create(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock =
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
final INetd netd = INetd.Stub.asInterface(
(IBinder) context.getSystemService(Context.NETD_SERVICE));
- final NetworkStatsService service = new NetworkStatsService(context, networkManager,
+ final NetworkStatsService service = new NetworkStatsService(context,
+ INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
alarmManager, wakeLock, getDefaultClock(),
new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(netd),
new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
new Dependencies());
- service.registerLocalService();
return service;
}
@@ -426,14 +438,12 @@
// This must not be called outside of tests, even within the same package, as this constructor
// does not register the local service. Use the create() helper above.
@VisibleForTesting
- NetworkStatsService(Context context, INetworkManagementService networkManager,
- AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock,
- NetworkStatsSettings settings, NetworkStatsFactory factory,
- NetworkStatsObservers statsObservers, File systemDir, File baseDir,
- @NonNull Dependencies deps) {
+ NetworkStatsService(Context context, INetd netd, AlarmManager alarmManager,
+ PowerManager.WakeLock wakeLock, Clock clock, NetworkStatsSettings settings,
+ NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir,
+ File baseDir, @NonNull Dependencies deps) {
mContext = Objects.requireNonNull(context, "missing Context");
- mNetworkManager = Objects.requireNonNull(networkManager,
- "missing INetworkManagementService");
+ mNetd = Objects.requireNonNull(netd, "missing Netd");
mAlarmManager = Objects.requireNonNull(alarmManager, "missing AlarmManager");
mClock = Objects.requireNonNull(clock, "missing Clock");
mSettings = Objects.requireNonNull(settings, "missing NetworkStatsSettings");
@@ -501,9 +511,24 @@
}
}
- private void registerLocalService() {
- LocalServices.addService(NetworkStatsManagerInternal.class,
- new NetworkStatsManagerInternalImpl());
+ /**
+ * Observer that watches for {@link INetdUnsolicitedEventListener} alerts.
+ */
+ @VisibleForTesting
+ public class AlertObserver extends BaseNetdUnsolicitedEventListener {
+ @Override
+ public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) {
+ PermissionUtils.enforceNetworkStackPermission(mContext);
+
+ if (LIMIT_GLOBAL_ALERT.equals(alertName)) {
+ // kick off background poll to collect network stats unless there is already
+ // 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,
+ mSettings.getPollDelay());
+ }
+ }
+ }
}
public void systemReady() {
@@ -551,9 +576,9 @@
mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
try {
- mNetworkManager.registerObserver(mAlertObserver);
- } catch (RemoteException e) {
- // ignored; service lives in system_server
+ mNetd.registerUnsolicitedEventListener(mAlertObserver);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.wtf(TAG, "Error registering event listener :", e);
}
// schedule periodic pall alarm based on {@link NetworkStatsSettings#getPollInterval()}.
@@ -641,13 +666,13 @@
}
/**
- * Register for a global alert that is delivered through {@link INetworkManagementEventObserver}
+ * Register for a global alert that is delivered through {@link AlertObserver}
* or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has
* been transferred.
*/
private void registerGlobalAlert() {
try {
- mNetworkManager.setGlobalAlert(mGlobalAlertBytes);
+ mNetd.bandwidthSetGlobalAlert(mGlobalAlertBytes);
} catch (IllegalStateException e) {
Log.w(TAG, "problem registering for global alert: " + e);
} catch (RemoteException e) {
@@ -766,8 +791,18 @@
}
return stats;
} catch (NullPointerException e) {
- // TODO: Track down and fix the cause of this crash and remove this catch block.
- Log.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
+ throw e;
+ }
+ }
+
+ @Override
+ public NetworkStats getTaggedSummaryForAllUid(
+ NetworkTemplate template, long start, long end) {
+ try {
+ final NetworkStats tagStats = getUidTagComplete()
+ .getSummary(template, start, end, mAccessLevel, mCallingUid);
+ return tagStats;
+ } catch (NullPointerException e) {
throw e;
}
}
@@ -827,7 +862,7 @@
if (LOGD) Log.d(TAG, "Resolving plan for " + template);
final long token = Binder.clearCallingIdentity();
try {
- plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
+ plan = mContext.getSystemService(NetworkPolicyManager.class)
.getSubscriptionPlan(template);
} finally {
Binder.restoreCallingIdentity(token);
@@ -922,6 +957,7 @@
@Override
public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
+ enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
try {
final String[] ifacesToQuery =
mStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
@@ -965,7 +1001,8 @@
}
@VisibleForTesting
- void setUidForeground(int uid, boolean uidForeground) {
+ public void setUidForeground(int uid, boolean uidForeground) {
+ PermissionUtils.enforceNetworkStackPermission(mContext);
synchronized (mStatsLock) {
final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
final int oldSet = mActiveUidCounterSet.get(uid, SET_DEFAULT);
@@ -1001,7 +1038,7 @@
@Override
public void forceUpdate() {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
final long token = Binder.clearCallingIdentity();
try {
@@ -1011,7 +1048,9 @@
}
}
- private void advisePersistThreshold(long thresholdBytes) {
+ /** Advise persistence threshold; may be overridden internally. */
+ public void advisePersistThreshold(long thresholdBytes) {
+ PermissionUtils.enforceNetworkStackPermission(mContext);
// clamp threshold into safe range
mPersistThreshold = NetworkStatsUtils.constrain(thresholdBytes,
128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
@@ -1226,26 +1265,6 @@
};
/**
- * Observer that watches for {@link INetworkManagementService} alerts.
- */
- private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
- @Override
- public void limitReached(String limitName, String iface) {
- // only someone like NMS should be calling us
- PermissionUtils.enforceNetworkStackPermission(mContext);
-
- if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
- // kick off background poll to collect network stats unless there is already
- // 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,
- mSettings.getPollDelay());
- }
- }
- }
- };
-
- /**
* Handle collapsed RAT type changed event.
*/
@VisibleForTesting
@@ -1610,7 +1629,7 @@
xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
- EventLogTags.writeNetstatsMobileSample(
+ EventLog.writeEvent(LOG_TAG_NETSTATS_MOBILE_SAMPLE,
devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1622,7 +1641,7 @@
xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
- EventLogTags.writeNetstatsWifiSample(
+ EventLog.writeEvent(LOG_TAG_NETSTATS_WIFI_SAMPLE,
devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1668,52 +1687,19 @@
removeUidsLocked(CollectionUtils.toIntArray(uids));
}
- private class NetworkStatsManagerInternalImpl extends NetworkStatsManagerInternal {
- @Override
- public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
- Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
- try {
- return NetworkStatsService.this.getNetworkTotalBytes(template, start, end);
- } finally {
- Trace.traceEnd(TRACE_TAG_NETWORK);
- }
+ /**
+ * Set the warning and limit to all registered custom network stats providers.
+ * Note that invocation of any interface will be sent to all providers.
+ */
+ public void setStatsProviderWarningAndLimitAsync(
+ @NonNull String iface, long warning, long limit) {
+ PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (LOGV) {
+ Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
+ + iface + "," + warning + "," + limit + ")");
}
-
- @Override
- public NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
- Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
- try {
- return NetworkStatsService.this.getNetworkUidBytes(template, start, end);
- } finally {
- Trace.traceEnd(TRACE_TAG_NETWORK);
- }
- }
-
- @Override
- public void setUidForeground(int uid, boolean uidForeground) {
- NetworkStatsService.this.setUidForeground(uid, uidForeground);
- }
-
- @Override
- public void advisePersistThreshold(long thresholdBytes) {
- NetworkStatsService.this.advisePersistThreshold(thresholdBytes);
- }
-
- @Override
- public void forceUpdate() {
- NetworkStatsService.this.forceUpdate();
- }
-
- @Override
- public void setStatsProviderWarningAndLimitAsync(
- @NonNull String iface, long warning, long limit) {
- if (LOGV) {
- Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
- + iface + "," + warning + "," + limit + ")");
- }
- invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
- warning, limit));
- }
+ invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
+ warning, limit));
}
@Override
@@ -1956,13 +1942,29 @@
*/
// TODO: Remove this by implementing {@link NetworkStatsProvider} for non-offloaded
// tethering stats.
- private NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
+ private @NonNull NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
+ // We only need to return per-UID stats. Per-device stats are already counted by
+ // interface counters.
+ if (how != STATS_PER_UID) {
+ return new NetworkStats(SystemClock.elapsedRealtime(), 0);
+ }
+
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
try {
- return mNetworkManager.getNetworkStatsTethering(how);
+ final TetherStatsParcel[] tetherStatsParcels = mNetd.tetherGetStats();
+ for (TetherStatsParcel tetherStats : tetherStatsParcels) {
+ try {
+ stats.combineValues(new NetworkStats.Entry(tetherStats.iface, UID_TETHERING,
+ SET_DEFAULT, TAG_NONE, tetherStats.rxBytes, tetherStats.rxPackets,
+ tetherStats.txBytes, tetherStats.txPackets, 0L));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalStateException("invalid tethering stats " + e);
+ }
+ }
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem reading network stats", e);
- return new NetworkStats(0L, 10);
}
+ return stats;
}
// TODO: It is copied from ConnectivityService, consider refactor these check permission
@@ -2001,10 +2003,12 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
Objects.requireNonNull(provider, "provider is null");
Objects.requireNonNull(tag, "tag is null");
+ final NetworkPolicyManager netPolicyManager = mContext
+ .getSystemService(NetworkPolicyManager.class);
try {
NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
tag, provider, mStatsProviderSem, mAlertObserver,
- mStatsProviderCbList);
+ mStatsProviderCbList, netPolicyManager);
mStatsProviderCbList.add(callback);
Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
+ getCallingUid() + "/" + getCallingPid());
@@ -2044,8 +2048,9 @@
@NonNull final INetworkStatsProvider mProvider;
@NonNull private final Semaphore mSemaphore;
- @NonNull final INetworkManagementEventObserver mAlertObserver;
+ @NonNull final AlertObserver mAlertObserver;
@NonNull final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
+ @NonNull final NetworkPolicyManager mNetworkPolicyManager;
@NonNull private final Object mProviderStatsLock = new Object();
@@ -2058,8 +2063,9 @@
NetworkStatsProviderCallbackImpl(
@NonNull String tag, @NonNull INetworkStatsProvider provider,
@NonNull Semaphore semaphore,
- @NonNull INetworkManagementEventObserver alertObserver,
- @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList)
+ @NonNull AlertObserver alertObserver,
+ @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList,
+ @NonNull NetworkPolicyManager networkPolicyManager)
throws RemoteException {
mTag = tag;
mProvider = provider;
@@ -2067,6 +2073,7 @@
mSemaphore = semaphore;
mAlertObserver = alertObserver;
mStatsProviderCbList = cbList;
+ mNetworkPolicyManager = networkPolicyManager;
}
@NonNull
@@ -2106,15 +2113,14 @@
// This binder object can only have been obtained by a process that holds
// NETWORK_STATS_PROVIDER. Thus, no additional permission check is required.
BinderUtils.withCleanCallingIdentity(() ->
- mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */));
+ mAlertObserver.onQuotaLimitReached(LIMIT_GLOBAL_ALERT, null /* unused */));
}
@Override
public void notifyWarningOrLimitReached() {
Log.d(TAG, mTag + ": notifyWarningOrLimitReached");
BinderUtils.withCleanCallingIdentity(() ->
- LocalServices.getService(NetworkPolicyManagerInternal.class)
- .onStatsProviderWarningOrLimitReached(mTag));
+ mNetworkPolicyManager.onStatsProviderWarningOrLimitReached());
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 011ca0b..cff45c6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -16,8 +16,6 @@
package com.android.settingslib.net;
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.telephony.TelephonyManager.SIM_STATE_READY;
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
@@ -27,12 +25,9 @@
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
-import android.os.ServiceManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
@@ -51,25 +46,20 @@
private static final String TAG = "DataUsageController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter(
PERIOD_BUILDER, Locale.getDefault());
private final Context mContext;
- private final INetworkStatsService mStatsService;
private final NetworkPolicyManager mPolicyManager;
private final NetworkStatsManager mNetworkStatsManager;
- private INetworkStatsSession mSession;
private Callback mCallback;
private NetworkNameProvider mNetworkController;
private int mSubscriptionId;
public DataUsageController(Context context) {
mContext = context;
- mStatsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
mPolicyManager = NetworkPolicyManager.from(mContext);
mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class);
mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -112,8 +102,7 @@
}
public DataUsageInfo getWifiDataUsageInfo() {
- NetworkTemplate template = NetworkTemplate.buildTemplateWifi(
- NetworkTemplate.WIFI_NETWORKID_ALL, null);
+ NetworkTemplate template = new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build();
return getDataUsageInfo(template);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
index 3f95a07..afd44d5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
@@ -17,6 +17,7 @@
package com.android.settingslib.net;
import android.content.Context;
+import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -26,6 +27,7 @@
import com.android.internal.util.ArrayUtils;
import java.util.List;
+import java.util.Set;
/**
* Utils class for data usage
@@ -73,10 +75,15 @@
private static NetworkTemplate getMobileTemplateForSubId(
TelephonyManager telephonyManager, int subId) {
- // The null subscriberId means that no any mobile/carrier network will be matched.
- // Using old API: buildTemplateMobileAll for the null subscriberId to avoid NPE.
+ // Create template that matches any mobile network when the subscriberId is null.
String subscriberId = telephonyManager.getSubscriberId(subId);
- return subscriberId != null ? NetworkTemplate.buildTemplateCarrierMetered(subscriberId)
- : NetworkTemplate.buildTemplateMobileAll(subscriberId);
+ return subscriberId != null
+ ? new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+ .setSubscriberIds(Set.of(subscriberId))
+ .setMeteredness(NetworkStats.METERED_YES)
+ .build()
+ : new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
+ .setMeteredness(NetworkStats.METERED_YES)
+ .build();
}
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
index 7e389a1..919f602 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
@@ -20,6 +20,7 @@
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
+import android.net.NetworkStats;
import android.net.NetworkTemplate;
import androidx.test.InstrumentationRegistry;
@@ -32,6 +33,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Set;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NetworkPolicyEditorTest {
@@ -44,7 +47,9 @@
@Before
public void setUp() {
- mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered("123456789123456");
+ mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+ .setMeteredness(NetworkStats.METERED_YES)
+ .setSubscriberIds(Set.of("123456789123456")).build();
NetworkPolicyManager policyManager = NetworkPolicyManager.from(InstrumentationRegistry
.getContext());
mNetworkPolicyEditor = new NetworkPolicyEditor(policyManager);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
index 9be783d..f0456b3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -18,26 +18,21 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.RemoteException;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.text.format.DateUtils;
import org.junit.Before;
import org.junit.Test;
@@ -47,6 +42,8 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowSubscriptionManager;
+import java.util.Set;
+
@RunWith(RobolectricTestRunner.class)
public class DataUsageControllerTest {
@@ -54,8 +51,6 @@
private static final String SUB_ID_2 = "Test Subscriber 2";
@Mock
- private INetworkStatsSession mSession;
- @Mock
private TelephonyManager mTelephonyManager;
@Mock
private SubscriptionManager mSubscriptionManager;
@@ -68,7 +63,6 @@
private NetworkTemplate mWifiNetworkTemplate;
private DataUsageController mController;
- private NetworkStatsHistory mNetworkStatsHistory;
private final int mDefaultSubscriptionId = 1234;
@Before
@@ -80,17 +74,16 @@
.thenReturn(mSubscriptionManager);
when(mContext.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
mController = new DataUsageController(mContext);
- mNetworkStatsHistory = spy(
- new NetworkStatsHistory(DateUtils.DAY_IN_MILLIS /* bucketDuration */));
- doReturn(mNetworkStatsHistory)
- .when(mSession).getHistoryForNetwork(any(NetworkTemplate.class), anyInt());
ShadowSubscriptionManager.setDefaultDataSubscriptionId(mDefaultSubscriptionId);
doReturn(SUB_ID).when(mTelephonyManager).getSubscriberId();
- mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID);
- mNetworkTemplate2 = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID_2);
- mWifiNetworkTemplate = NetworkTemplate.buildTemplateWifi(
- NetworkTemplate.WIFI_NETWORKID_ALL, null);
+ mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+ .setMeteredness(android.net.NetworkStats.METERED_YES)
+ .setSubscriberIds(Set.of(SUB_ID)).build();
+ mNetworkTemplate2 = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+ .setMeteredness(android.net.NetworkStats.METERED_YES)
+ .setSubscriberIds(Set.of(SUB_ID_2)).build();
+ mWifiNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build();
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index e8d5844..5b0f659 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -30,6 +30,7 @@
import android.content.Context;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
+import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.text.format.DateUtils;
@@ -40,6 +41,8 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
+import java.util.Set;
+
@RunWith(RobolectricTestRunner.class)
public class NetworkCycleDataForUidLoaderTest {
private static final String SUB_ID = "Test Subscriber";
@@ -62,7 +65,9 @@
when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
.thenReturn(mNetworkPolicyManager);
when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
- mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID);
+ mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+ .setMeteredness(NetworkStats.METERED_YES)
+ .setSubscriberIds(Set.of(SUB_ID)).build();
}
@Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 618a37e..5b9b74a 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -340,6 +340,9 @@
<!-- Permission required for CTS test - CrossProfileAppsHostSideTest -->
<uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
+ <!-- Permission required for CTS test - CrossProfileAppsHostSideTest -->
+ <uses-permission android:name="android.permission.START_CROSS_PROFILE_ACTIVITIES"/>
+
<!-- permissions required for CTS test - PhoneStateListenerTest -->
<uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" />
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 450e988..c8b4f11 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -46,6 +46,7 @@
import android.bluetooth.IBluetoothManagerCallback;
import android.bluetooth.IBluetoothProfileServiceConnection;
import android.bluetooth.IBluetoothStateChangeCallback;
+import android.bluetooth.IBluetoothLeCallControl;
import android.content.ActivityNotFoundException;
import android.content.AttributionSource;
import android.content.BroadcastReceiver;
@@ -1328,11 +1329,15 @@
+ bluetoothProfile);
}
- if (bluetoothProfile != BluetoothProfile.HEADSET) {
+ Intent intent;
+ if (bluetoothProfile == BluetoothProfile.HEADSET) {
+ intent = new Intent(IBluetoothHeadset.class.getName());
+ } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) {
+ intent = new Intent(IBluetoothLeCallControl.class.getName());
+ } else {
return false;
}
- Intent intent = new Intent(IBluetoothHeadset.class.getName());
psc = new ProfileServiceConnections(intent);
if (!psc.bindService()) {
return false;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index a2c2dbd..3951680 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -39,6 +39,8 @@
import static android.net.NetworkStats.TAG_NONE;
import static android.net.TrafficStats.UID_TETHERING;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
+
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
@@ -133,12 +135,6 @@
private static final int MAX_UID_RANGES_PER_COMMAND = 10;
- /**
- * Name representing {@link #setGlobalAlert(long)} limit when delivered to
- * {@link INetworkManagementEventObserver#limitReached(String, String)}.
- */
- public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
-
static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
static final boolean MODIFY_OPERATION_ADD = true;
diff --git a/services/core/java/com/android/server/SmartStorageMaintIdler.java b/services/core/java/com/android/server/SmartStorageMaintIdler.java
new file mode 100644
index 0000000..2dff72f
--- /dev/null
+++ b/services/core/java/com/android/server/SmartStorageMaintIdler.java
@@ -0,0 +1,89 @@
+/*
+ * 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 com.android.server;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import java.util.concurrent.TimeUnit;
+
+public class SmartStorageMaintIdler extends JobService {
+ private static final String TAG = "SmartStorageMaintIdler";
+
+ private static final ComponentName SMART_STORAGE_MAINT_SERVICE =
+ new ComponentName("android", SmartStorageMaintIdler.class.getName());
+
+ private static final int SMART_MAINT_JOB_ID = 2808;
+
+ private boolean mStarted;
+ private JobParameters mJobParams;
+ private final Runnable mFinishCallback = new Runnable() {
+ @Override
+ public void run() {
+ Slog.i(TAG, "Got smart storage maintenance service completion callback");
+ if (mStarted) {
+ jobFinished(mJobParams, false);
+ mStarted = false;
+ }
+ // ... and try again in a next period
+ scheduleSmartIdlePass(SmartStorageMaintIdler.this,
+ StorageManagerService.SMART_IDLE_MAINT_PERIOD);
+ }
+ };
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ mJobParams = params;
+ StorageManagerService ms = StorageManagerService.sSelf;
+ if (ms != null) {
+ mStarted = true;
+ ms.runSmartIdleMaint(mFinishCallback);
+ }
+ return ms != null;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ mStarted = false;
+ return false;
+ }
+
+ /**
+ * Schedule the smart storage idle maintenance job
+ */
+ public static void scheduleSmartIdlePass(Context context, int nHours) {
+ StorageManagerService ms = StorageManagerService.sSelf;
+ if ((ms == null) || ms.isPassedLifetimeThresh()) {
+ return;
+ }
+
+ JobScheduler tm = context.getSystemService(JobScheduler.class);
+
+ long nextScheduleTime = TimeUnit.HOURS.toMillis(nHours);
+
+ JobInfo.Builder builder = new JobInfo.Builder(SMART_MAINT_JOB_ID,
+ SMART_STORAGE_MAINT_SERVICE);
+
+ builder.setMinimumLatency(nextScheduleTime);
+ tm.schedule(builder.build());
+ }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 2d6170b..53c8635 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -79,6 +79,7 @@
import android.content.res.ObbInfo;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.BatteryManager;
import android.os.Binder;
import android.os.DropBoxManager;
import android.os.Environment;
@@ -158,6 +159,8 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -339,7 +342,44 @@
@Nullable public static String sMediaStoreAuthorityProcessName;
+ // Run period in hour for smart idle maintenance
+ static final int SMART_IDLE_MAINT_PERIOD = 1;
+
private final AtomicFile mSettingsFile;
+ private final AtomicFile mHourlyWriteFile;
+
+ private static final int MAX_HOURLY_WRITE_RECORDS = 72;
+
+ /**
+ * Default config values for smart idle maintenance
+ * Actual values will be controlled by DeviceConfig
+ */
+ // Decide whether smart idle maintenance is enabled or not
+ private static final boolean DEFAULT_SMART_IDLE_MAINT_ENABLED = false;
+ // Storage lifetime percentage threshold to decide to turn off the feature
+ private static final int DEFAULT_LIFETIME_PERCENT_THRESHOLD = 70;
+ // Minimum required number of dirty + free segments to trigger GC
+ private static final int DEFAULT_MIN_SEGMENTS_THRESHOLD = 512;
+ // Determine how much portion of current dirty segments will be GCed
+ private static final float DEFAULT_DIRTY_RECLAIM_RATE = 0.5F;
+ // Multiplier to amplify the target segment number for GC
+ private static final float DEFAULT_SEGMENT_RECLAIM_WEIGHT = 1.0F;
+ // Low battery level threshold to decide to turn off the feature
+ private static final float DEFAULT_LOW_BATTERY_LEVEL = 20F;
+ // Decide whether charging is required to turn on the feature
+ private static final boolean DEFAULT_CHARGING_REQUIRED = true;
+
+ private volatile int mLifetimePercentThreshold;
+ private volatile int mMinSegmentsThreshold;
+ private volatile float mDirtyReclaimRate;
+ private volatile float mSegmentReclaimWeight;
+ private volatile float mLowBatteryLevel;
+ private volatile boolean mChargingRequired;
+ private volatile boolean mNeedGC;
+
+ private volatile boolean mPassedLifetimeThresh;
+ // Tracking storage hourly write amounts
+ private volatile int[] mStorageHourlyWrites;
/**
* <em>Never</em> hold the lock while performing downcalls into vold, since
@@ -901,6 +941,10 @@
}
private void handleSystemReady() {
+ if (prepareSmartIdleMaint()) {
+ SmartStorageMaintIdler.scheduleSmartIdlePass(mContext, SMART_IDLE_MAINT_PERIOD);
+ }
+
// Start scheduling nominally-daily fstrim operations
MountServiceIdler.scheduleIdlePass(mContext);
@@ -1907,6 +1951,10 @@
mSettingsFile = new AtomicFile(
new File(Environment.getDataSystemDirectory(), "storage.xml"), "storage-settings");
+ mHourlyWriteFile = new AtomicFile(
+ new File(Environment.getDataSystemDirectory(), "storage-hourly-writes"));
+
+ mStorageHourlyWrites = new int[MAX_HOURLY_WRITE_RECORDS];
synchronized (mLock) {
readSettingsLocked();
@@ -2572,7 +2620,7 @@
// fstrim time is still updated. If file based checkpoints are used, we run
// idle maintenance (GC + fstrim) regardless of checkpoint status.
if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
- mVold.runIdleMaint(new IVoldTaskListener.Stub() {
+ mVold.runIdleMaint(mNeedGC, new IVoldTaskListener.Stub() {
@Override
public void onStatus(int status, PersistableBundle extras) {
// Not currently used
@@ -2623,6 +2671,176 @@
abortIdleMaint(null);
}
+ private boolean prepareSmartIdleMaint() {
+ /**
+ * We can choose whether going with a new storage smart idle maintenance job
+ * or falling back to the traditional way using DeviceConfig
+ */
+ boolean smartIdleMaintEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "smart_idle_maint_enabled",
+ DEFAULT_SMART_IDLE_MAINT_ENABLED);
+ if (smartIdleMaintEnabled) {
+ mLifetimePercentThreshold = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "lifetime_threshold", DEFAULT_LIFETIME_PERCENT_THRESHOLD);
+ mMinSegmentsThreshold = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "min_segments_threshold", DEFAULT_MIN_SEGMENTS_THRESHOLD);
+ mDirtyReclaimRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "dirty_reclaim_rate", DEFAULT_DIRTY_RECLAIM_RATE);
+ mSegmentReclaimWeight = DeviceConfig.getFloat(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "segment_reclaim_weight", DEFAULT_SEGMENT_RECLAIM_WEIGHT);
+ mLowBatteryLevel = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "low_battery_level", DEFAULT_LOW_BATTERY_LEVEL);
+ mChargingRequired = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "charging_required", DEFAULT_CHARGING_REQUIRED);
+
+ // If we use the smart idle maintenance, we need to turn off GC in the traditional idle
+ // maintenance to avoid the conflict
+ mNeedGC = false;
+
+ loadStorageHourlyWrites();
+ try {
+ mVold.refreshLatestWrite();
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ refreshLifetimeConstraint();
+ }
+ return smartIdleMaintEnabled;
+ }
+
+ // Return whether storage lifetime exceeds the threshold
+ public boolean isPassedLifetimeThresh() {
+ return mPassedLifetimeThresh;
+ }
+
+ private void loadStorageHourlyWrites() {
+ FileInputStream fis = null;
+
+ try {
+ fis = mHourlyWriteFile.openRead();
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ mStorageHourlyWrites = (int[])ois.readObject();
+ } catch (FileNotFoundException e) {
+ // Missing data is okay, probably first boot
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Failed reading write records", e);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ }
+ }
+
+ private int getAverageHourlyWrite() {
+ return Arrays.stream(mStorageHourlyWrites).sum() / MAX_HOURLY_WRITE_RECORDS;
+ }
+
+ private void updateStorageHourlyWrites(int latestWrite) {
+ FileOutputStream fos = null;
+
+ System.arraycopy(mStorageHourlyWrites,0, mStorageHourlyWrites, 1,
+ MAX_HOURLY_WRITE_RECORDS - 1);
+ mStorageHourlyWrites[0] = latestWrite;
+ try {
+ fos = mHourlyWriteFile.startWrite();
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ oos.writeObject(mStorageHourlyWrites);
+ mHourlyWriteFile.finishWrite(fos);
+ } catch (IOException e) {
+ if (fos != null) {
+ mHourlyWriteFile.failWrite(fos);
+ }
+ }
+ }
+
+ private boolean checkChargeStatus() {
+ IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ Intent batteryStatus = mContext.registerReceiver(null, ifilter);
+
+ if (mChargingRequired) {
+ int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
+ if (status != BatteryManager.BATTERY_STATUS_CHARGING &&
+ status != BatteryManager.BATTERY_STATUS_FULL) {
+ Slog.w(TAG, "Battery is not being charged");
+ return false;
+ }
+ }
+
+ int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+ float chargePercent = level * 100f / (float)scale;
+
+ if (chargePercent < mLowBatteryLevel) {
+ Slog.w(TAG, "Battery level is " + chargePercent + ", which is lower than threshold: " +
+ mLowBatteryLevel);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean refreshLifetimeConstraint() {
+ int storageLifeTime = 0;
+
+ try {
+ storageLifeTime = mVold.getStorageLifeTime();
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ return false;
+ }
+
+ if (storageLifeTime == -1) {
+ Slog.w(TAG, "Failed to get storage lifetime");
+ return false;
+ } else if (storageLifeTime > mLifetimePercentThreshold) {
+ Slog.w(TAG, "Ended smart idle maintenance, because of lifetime(" + storageLifeTime +
+ ")" + ", lifetime threshold(" + mLifetimePercentThreshold + ")");
+ mPassedLifetimeThresh = true;
+ return false;
+ }
+ Slog.i(TAG, "Storage lifetime: " + storageLifeTime);
+ return true;
+ }
+
+ void runSmartIdleMaint(Runnable callback) {
+ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
+
+ try {
+ // Block based checkpoint process runs fstrim. So, if checkpoint is in progress
+ // (first boot after OTA), We skip the smart idle maintenance
+ if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
+ if (!refreshLifetimeConstraint() || !checkChargeStatus()) {
+ return;
+ }
+
+ int latestHourlyWrite = mVold.getWriteAmount();
+ if (latestHourlyWrite == -1) {
+ Slog.w(TAG, "Failed to get storage hourly write");
+ return;
+ }
+
+ updateStorageHourlyWrites(latestHourlyWrite);
+ int avgHourlyWrite = getAverageHourlyWrite();
+
+ Slog.i(TAG, "Set smart idle maintenance: " + "latest hourly write: " +
+ latestHourlyWrite + ", average hourly write: " + avgHourlyWrite +
+ ", min segment threshold: " + mMinSegmentsThreshold +
+ ", dirty reclaim rate: " + mDirtyReclaimRate +
+ ", segment reclaim weight:" + mSegmentReclaimWeight);
+ mVold.setGCUrgentPace(avgHourlyWrite, mMinSegmentsThreshold, mDirtyReclaimRate,
+ mSegmentReclaimWeight);
+ } else {
+ Slog.i(TAG, "Skipping smart idle maintenance - block based checkpoint in progress");
+ }
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ } finally {
+ if (callback != null) {
+ callback.run();
+ }
+ }
+ }
+
@Override
public void setDebugFlags(int flags, int mask) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 7ba032f..c6bca19 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -16,6 +16,7 @@
package com.android.server.am;
import android.annotation.Nullable;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
@@ -702,8 +703,10 @@
if (wifiInfo.isValid()) {
final long wifiChargeUC = measuredEnergyDeltas != null ?
measuredEnergyDeltas.wifiChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
- mStats.updateWifiState(
- extractDeltaLocked(wifiInfo), wifiChargeUC, elapsedRealtime, uptime);
+ final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
+ NetworkStatsManager.class);
+ mStats.updateWifiState(extractDeltaLocked(wifiInfo),
+ wifiChargeUC, elapsedRealtime, uptime, networkStatsManager);
} else {
Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
}
@@ -712,8 +715,10 @@
if (modemInfo != null) {
final long mobileRadioChargeUC = measuredEnergyDeltas != null
? measuredEnergyDeltas.mobileRadioChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
+ final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
+ NetworkStatsManager.class);
mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime,
- uptime);
+ uptime, networkStatsManager);
}
if (updateFlags == UPDATE_ALL) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9a349ef..d75ddba 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.app.StatsManager;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.content.ContentResolver;
import android.content.Context;
@@ -2025,8 +2026,11 @@
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
+ final NetworkStatsManager networkStatsManager = mContext.getSystemService(
+ NetworkStatsManager.class);
mHandler.post(() -> {
- mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime);
+ mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime,
+ networkStatsManager);
});
}
}
@@ -2063,9 +2067,11 @@
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
+ final NetworkStatsManager networkStatsManager = mContext.getSystemService(
+ NetworkStatsManager.class);
mHandler.post(() -> {
mStats.noteModemControllerActivity(info, POWER_DATA_UNAVAILABLE, elapsedRealtime,
- uptime);
+ uptime, networkStatsManager);
});
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 4fc6614..2eb59b2 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1205,6 +1205,7 @@
mDeviceInventory.onSetBtActiveDevice((BtDeviceInfo) msg.obj,
mAudioService.getBluetoothContextualVolumeStream());
}
+ break;
case MSG_BT_HEADSET_CNCT_FAILED:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 0a114b9..fba8c05 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1088,6 +1088,12 @@
private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device,
String eventSource) {
if (device != AudioSystem.DEVICE_NONE) {
+
+ /* Audio Policy sees Le Audio similar to A2DP. Let's make sure
+ * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set
+ */
+ mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
+
AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE,
address, name, AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java b/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
index 8c93891..6654c0c 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
@@ -189,7 +189,8 @@
removed.add(id);
}
}
- if (modified.isEmpty() && removed.isEmpty() && mComplete == chunk.isComplete()) {
+ if (modified.isEmpty() && removed.isEmpty() && mComplete == chunk.isComplete()
+ && !chunk.isPurge()) {
return null;
}
mComplete = chunk.isComplete();
@@ -239,9 +240,10 @@
}
// Determine number of chunks we need to send.
- int numChunks = 0;
+ int numChunks = purge ? 1 : 0;
if (modified != null) {
- numChunks = roundUpFraction(modified.size(), maxNumModifiedPerChunk);
+ numChunks = Math.max(numChunks,
+ roundUpFraction(modified.size(), maxNumModifiedPerChunk));
}
if (removed != null) {
numChunks = Math.max(numChunks, roundUpFraction(removed.size(), maxNumRemovedPerChunk));
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 093ecd5..3d5abfe 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -535,9 +535,6 @@
mEmulatorClipboardMonitor.accept(clip);
final int userId = UserHandle.getUserId(uid);
- if (clip != null) {
- startClassificationLocked(clip, userId);
- }
// Update this user
setPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);
@@ -593,6 +590,17 @@
@GuardedBy("mLock")
private void setPrimaryClipInternalLocked(PerUserClipboard clipboard, @Nullable ClipData clip,
int uid, @Nullable String sourcePackage) {
+ final int userId = UserHandle.getUserId(uid);
+ if (clip != null) {
+ startClassificationLocked(clip, userId);
+ }
+
+ setPrimaryClipInternalNoClassifyLocked(clipboard, clip, uid, sourcePackage);
+ }
+
+ @GuardedBy("mLock")
+ private void setPrimaryClipInternalNoClassifyLocked(PerUserClipboard clipboard,
+ @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
revokeUris(clipboard);
clipboard.activePermissionOwners.clear();
if (clip == null && clipboard.primaryClip == null) {
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index cc9efbc..fce6737 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -199,6 +199,7 @@
private final NetworkTemplate mNetworkTemplate;
private final UsageCallback mUsageCallback;
private NetworkCapabilities mNetworkCapabilities;
+ private final NetworkStatsManager mStatsManager;
public MultipathTracker(Network network, NetworkCapabilities nc) {
this.network = network;
@@ -238,6 +239,13 @@
updateMultipathBudget();
}
};
+ mStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+ // Query stats from NetworkStatsService will trigger a poll by default.
+ // But since MultipathPolicyTracker listens NPMS events that triggered by
+ // stats updated event, and will query stats
+ // after the event. A polling -> updated -> query -> polling loop will be introduced
+ // if polls on open. Hence, set flag to false to prevent a polling loop.
+ mStatsManager.setPollOnOpen(false);
updateMultipathBudget();
}
@@ -262,8 +270,7 @@
private long getNetworkTotalBytes(long start, long end) {
try {
final android.app.usage.NetworkStats.Bucket ret =
- mContext.getSystemService(NetworkStatsManager.class)
- .querySummaryForDevice(mNetworkTemplate, start, end);
+ mStatsManager.querySummaryForDevice(mNetworkTemplate, start, end);
return ret.getRxBytes() + ret.getTxBytes();
} catch (RuntimeException e) {
Log.w(TAG, "Failed to get data usage: " + e);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 2c666b5..9a9c3ea 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1208,8 +1208,11 @@
for (RouteInfo route : mConfig.routes) {
lp.addRoute(route);
InetAddress address = route.getDestination().getAddress();
- allowIPv4 |= address instanceof Inet4Address;
- allowIPv6 |= address instanceof Inet6Address;
+
+ if (route.getType() == RouteInfo.RTN_UNICAST) {
+ allowIPv4 |= address instanceof Inet4Address;
+ allowIPv6 |= address instanceof Inet6Address;
+ }
}
}
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index 2e2d812..8097f4e 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -1,8 +1,6 @@
+# Bug component: 137631
elaurent@google.com
-hdmoon@google.com
-insun@google.com
-jaewan@google.com
-jinpark@google.com
-klhyun@google.com
lajos@google.com
-sungsoo@google.com
+
+# go/android-fwk-media-solutions for info on areas of ownership.
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 03a63b9..8ef42ff 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,11 +16,8 @@
package com.android.server.net;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Network;
-import android.net.NetworkTemplate;
-import android.net.netstats.provider.NetworkStatsProvider;
import android.os.PowerExemptionManager.ReasonCode;
import android.telephony.SubscriptionPlan;
@@ -56,11 +53,6 @@
*/
public abstract SubscriptionPlan getSubscriptionPlan(Network network);
- /**
- * Return the active {@link SubscriptionPlan} for the given template.
- */
- public abstract SubscriptionPlan getSubscriptionPlan(NetworkTemplate template);
-
public static final int QUOTA_TYPE_JOBS = 1;
public static final int QUOTA_TYPE_MULTIPATH = 2;
@@ -99,13 +91,4 @@
*/
public abstract void setMeteredRestrictedPackagesAsync(
Set<String> packageNames, int userId);
-
- /**
- * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
- * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
- * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
- *
- * @param tag the human readable identifier of the custom network stats provider.
- */
- public abstract void onStatsProviderWarningOrLimitReached(@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 2ca057d..e1cbdb7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -130,7 +130,7 @@
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -151,6 +151,8 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -181,7 +183,6 @@
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
import android.net.TrafficStats;
@@ -443,7 +444,7 @@
private final Context mContext;
private final IActivityManager mActivityManager;
- private NetworkStatsManagerInternal mNetworkStats;
+ private NetworkStatsManager mNetworkStats;
private final INetworkManagementService mNetworkManager;
private UsageStatsManagerInternal mUsageStats;
private AppStandbyInternal mAppStandby;
@@ -455,6 +456,8 @@
private ConnectivityManager mConnManager;
private PowerManagerInternal mPowerManagerInternal;
private PowerWhitelistManager mPowerWhitelistManager;
+ @NonNull
+ private final Dependencies mDeps;
/** Current cached value of the current Battery Saver mode's setting for restrict background. */
@GuardedBy("mUidRulesFirstLock")
@@ -706,7 +709,7 @@
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
INetworkManagementService networkManagement) {
this(context, activityManager, networkManagement, AppGlobals.getPackageManager(),
- getDefaultClock(), getDefaultSystemDir(), false);
+ getDefaultClock(), getDefaultSystemDir(), false, new Dependencies(context));
}
private static @NonNull File getDefaultSystemDir() {
@@ -718,9 +721,59 @@
Clock.systemUTC());
}
+ static class Dependencies {
+ final Context mContext;
+ final NetworkStatsManager mNetworkStatsManager;
+ Dependencies(Context context) {
+ mContext = context;
+ mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+ // Query stats from NetworkStatsService will trigger a poll by default.
+ // But since NPMS listens stats updated event, and will query stats
+ // after the event. A polling -> updated -> query -> polling loop will be introduced
+ // if polls on open. Hence, while NPMS manages it's poll requests explicitly, set
+ // flag to false to prevent a polling loop.
+ mNetworkStatsManager.setPollOnOpen(false);
+ }
+
+ long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+ Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
+ try {
+ final NetworkStats.Bucket ret = mNetworkStatsManager
+ .querySummaryForDevice(template, start, end);
+ return ret.getRxBytes() + ret.getTxBytes();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failed to read network stats: " + e);
+ return 0;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_NETWORK);
+ }
+ }
+
+ @NonNull
+ List<NetworkStats.Bucket> getNetworkUidBytes(
+ @NonNull NetworkTemplate template, long start, long end) {
+ Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
+ final List<NetworkStats.Bucket> buckets = new ArrayList<>();
+ try {
+ final NetworkStats stats = mNetworkStatsManager.querySummary(template, start, end);
+ while (stats.hasNextBucket()) {
+ final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+ stats.getNextBucket(bucket);
+ buckets.add(bucket);
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failed to read network stats: " + e);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_NETWORK);
+ }
+ return buckets;
+ }
+ }
+
+ @VisibleForTesting
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
INetworkManagementService networkManagement, IPackageManager pm, Clock clock,
- File systemDir, boolean suppressDefaultPolicy) {
+ File systemDir, boolean suppressDefaultPolicy, Dependencies deps) {
mContext = Objects.requireNonNull(context, "missing context");
mActivityManager = Objects.requireNonNull(activityManager, "missing activityManager");
mNetworkManager = Objects.requireNonNull(networkManagement, "missing networkManagement");
@@ -741,10 +794,12 @@
mUidEventHandler = new Handler(mUidEventThread.getLooper(), mUidEventHandlerCallback);
mSuppressDefaultPolicy = suppressDefaultPolicy;
+ mDeps = Objects.requireNonNull(deps, "missing Dependencies");
mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"), "net-policy");
mAppOps = context.getSystemService(AppOpsManager.class);
+ mNetworkStats = context.getSystemService(NetworkStatsManager.class);
mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
// Expose private service for system components to use.
LocalServices.addService(NetworkPolicyManagerInternal.class,
@@ -844,7 +899,6 @@
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
mAppStandby = LocalServices.getService(AppStandbyInternal.class);
- mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class);
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
@@ -1167,21 +1221,34 @@
};
/**
- * Receiver that watches for {@link INetworkStatsService} updates, which we
+ * Receiver that watches for {@link NetworkStatsManager} updates, which we
* use to check against {@link NetworkPolicy#warningBytes}.
*/
- final private BroadcastReceiver mStatsReceiver = new BroadcastReceiver() {
+ private final NetworkStatsBroadcastReceiver mStatsReceiver =
+ new NetworkStatsBroadcastReceiver();
+ private class NetworkStatsBroadcastReceiver extends BroadcastReceiver {
+ private boolean mIsAnyIntentReceived = false;
@Override
public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified
// READ_NETWORK_USAGE_HISTORY permission above.
+ mIsAnyIntentReceived = true;
+
synchronized (mNetworkPoliciesSecondLock) {
updateNetworkRulesNL();
updateNetworkEnabledNL();
updateNotificationsNL();
}
}
+
+ /**
+ * Return whether any {@code ACTION_NETWORK_STATS_UPDATED} intent is received.
+ * Used to determine if NetworkStatsService is ready.
+ */
+ public boolean isAnyIntentReceived() {
+ return mIsAnyIntentReceived;
+ }
};
/**
@@ -1405,15 +1472,17 @@
long maxBytes = 0;
int maxUid = 0;
- final NetworkStats stats = getNetworkUidBytes(template, start, end);
- NetworkStats.Entry entry = null;
- for (int i = 0; i < stats.size(); i++) {
- entry = stats.getValues(i, entry);
- final long bytes = entry.rxBytes + entry.txBytes;
+ // Skip if not ready. NetworkStatsService will block public API calls until it is
+ // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+ if (!mStatsReceiver.isAnyIntentReceived()) return null;
+
+ final List<NetworkStats.Bucket> stats = mDeps.getNetworkUidBytes(template, start, end);
+ for (final NetworkStats.Bucket entry : stats) {
+ final long bytes = entry.getRxBytes() + entry.getTxBytes();
totalBytes += bytes;
if (bytes > maxBytes) {
maxBytes = bytes;
- maxUid = entry.uid;
+ maxUid = entry.getUid();
}
}
@@ -3383,6 +3452,35 @@
return result;
}
+ /**
+ * Get subscription plan for the given networkTemplate.
+ *
+ * @param template the networkTemplate to get the subscription plan for.
+ */
+ @Override
+ public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
+ enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ synchronized (mNetworkPoliciesSecondLock) {
+ final int subId = findRelevantSubIdNL(template);
+ return getPrimarySubscriptionPlanLocked(subId);
+ }
+ }
+
+ /**
+ * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+ * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
+ * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+ */
+ @Override
+ public void onStatsProviderWarningOrLimitReached() {
+ enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ // This API may be called before the system is ready.
+ synchronized (mNetworkPoliciesSecondLock) {
+ if (!mSystemReady) return;
+ }
+ mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
+ }
+
@Override
public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
@@ -5369,25 +5467,10 @@
@Deprecated
private long getTotalBytes(NetworkTemplate template, long start, long end) {
- return getNetworkTotalBytes(template, start, end);
- }
-
- private long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
- try {
- return mNetworkStats.getNetworkTotalBytes(template, start, end);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failed to read network stats: " + e);
- return 0;
- }
- }
-
- private NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
- try {
- return mNetworkStats.getNetworkUidBytes(template, start, end);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failed to read network stats: " + e);
- return new NetworkStats(SystemClock.elapsedRealtime(), 0);
- }
+ // Skip if not ready. NetworkStatsService will block public API calls until it is
+ // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+ if (!mStatsReceiver.isAnyIntentReceived()) return 0;
+ return mDeps.getNetworkTotalBytes(template, start, end);
}
private boolean isBandwidthControlEnabled() {
@@ -5607,14 +5690,6 @@
}
@Override
- public SubscriptionPlan getSubscriptionPlan(NetworkTemplate template) {
- synchronized (mNetworkPoliciesSecondLock) {
- final int subId = findRelevantSubIdNL(template);
- return getPrimarySubscriptionPlanLocked(subId);
- }
- }
-
- @Override
public long getSubscriptionOpportunisticQuota(Network network, int quotaType) {
final long quotaBytes;
synchronized (mNetworkPoliciesSecondLock) {
@@ -5656,12 +5731,6 @@
mHandler.obtainMessage(MSG_METERED_RESTRICTED_PACKAGES_CHANGED,
userId, 0, packageNames).sendToTarget();
}
-
- @Override
- public void onStatsProviderWarningOrLimitReached(@NonNull String tag) {
- Log.v(TAG, "onStatsProviderWarningOrLimitReached: " + tag);
- mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
- }
}
private void setMeteredRestrictedPackagesInternal(Set<String> packageNames, int userId) {
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index ef0079e..ca67597 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -35,7 +35,6 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils;
import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
@@ -313,12 +312,12 @@
return mStartWallTime;
}
- public PowerStatsLogger(Context context, File dataStoragePath,
+ public PowerStatsLogger(Context context, Looper looper, File dataStoragePath,
String meterFilename, String meterCacheFilename,
String modelFilename, String modelCacheFilename,
String residencyFilename, String residencyCacheFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
- super(Looper.getMainLooper());
+ super(looper);
mStartWallTime = currentTimeMillis() - SystemClock.elapsedRealtime();
if (DEBUG) Slog.d(TAG, "mStartWallTime: " + mStartWallTime);
mPowerStatsHALWrapper = powerStatsHALWrapper;
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index bb52c1d..9953ca8 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -28,6 +28,7 @@
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.UserHandle;
import android.power.PowerStatsInternal;
import android.util.Slog;
@@ -79,6 +80,9 @@
private StatsPullAtomCallbackImpl mPullAtomCallback;
@Nullable
private PowerStatsInternal mPowerStatsInternal;
+ @Nullable
+ @GuardedBy("this")
+ private Looper mLooper;
@VisibleForTesting
static class Injector {
@@ -127,12 +131,12 @@
}
}
- PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String meterFilename, String meterCacheFilename,
+ PowerStatsLogger createPowerStatsLogger(Context context, Looper looper,
+ File dataStoragePath, String meterFilename, String meterCacheFilename,
String modelFilename, String modelCacheFilename,
String residencyFilename, String residencyCacheFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
- return new PowerStatsLogger(context, dataStoragePath,
+ return new PowerStatsLogger(context, looper, dataStoragePath,
meterFilename, meterCacheFilename,
modelFilename, modelCacheFilename,
residencyFilename, residencyCacheFilename,
@@ -229,11 +233,11 @@
mDataStoragePath = mInjector.createDataStoragePath();
// Only start logger and triggers if initialization is successful.
- mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, mDataStoragePath,
- mInjector.createMeterFilename(), mInjector.createMeterCacheFilename(),
- mInjector.createModelFilename(), mInjector.createModelCacheFilename(),
- mInjector.createResidencyFilename(), mInjector.createResidencyCacheFilename(),
- getPowerStatsHal());
+ mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, getLooper(),
+ mDataStoragePath, mInjector.createMeterFilename(),
+ mInjector.createMeterCacheFilename(), mInjector.createModelFilename(),
+ mInjector.createModelCacheFilename(), mInjector.createResidencyFilename(),
+ mInjector.createResidencyCacheFilename(), getPowerStatsHal());
mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
} else {
@@ -245,6 +249,17 @@
return mInjector.getPowerStatsHALWrapperImpl();
}
+ private Looper getLooper() {
+ synchronized (this) {
+ if (mLooper == null) {
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ return thread.getLooper();
+ }
+ return mLooper;
+ }
+ }
+
public PowerStatsService(Context context) {
this(context, new Injector());
}
@@ -260,9 +275,7 @@
private final Handler mHandler;
LocalService() {
- HandlerThread thread = new HandlerThread(TAG);
- thread.start();
- mHandler = new Handler(thread.getLooper());
+ mHandler = new Handler(getLooper());
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 0f37450..e7d05b6 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -527,12 +527,13 @@
callback.onFailure();
return;
}
- textClassifierServiceConsumer.accept(serviceState.mService);
+ consumeServiceNoExceptLocked(textClassifierServiceConsumer, serviceState.mService);
} else {
serviceState.mPendingRequests.add(
new PendingRequest(
methodName,
- () -> textClassifierServiceConsumer.accept(serviceState.mService),
+ () -> consumeServiceNoExceptLocked(
+ textClassifierServiceConsumer, serviceState.mService),
callback::onFailure, callback.asBinder(),
this,
serviceState,
@@ -541,6 +542,16 @@
}
}
+ private static void consumeServiceNoExceptLocked(
+ @NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer,
+ @Nullable ITextClassifierService service) {
+ try {
+ textClassifierServiceConsumer.accept(service);
+ } catch (RuntimeException | Error e) {
+ Slog.e(LOG_TAG, "Exception when consume textClassifierService: " + e);
+ }
+ }
+
private static ITextClassifierCallback wrap(ITextClassifierCallback orig) {
return new CallbackWrapper(orig);
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index bd8d13b..c96c1ee 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -20,8 +20,8 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static com.android.server.VcnManagementService.LOCAL_LOG;
@@ -44,7 +44,8 @@
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;
-import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
import java.util.Set;
/** @hide */
@@ -76,7 +77,7 @@
public static int calculatePriorityClass(
VcnContext vcnContext,
UnderlyingNetworkRecord networkRecord,
- LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
@@ -94,7 +95,7 @@
}
int priorityIndex = 0;
- for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkPriorities) {
+ for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkTemplates) {
if (checkMatchesPriorityRule(
vcnContext,
nwPriority,
@@ -119,10 +120,32 @@
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
PersistableBundle carrierConfig) {
- // TODO: Check Network Quality reported by metric monitors/probers.
-
final NetworkCapabilities caps = networkRecord.networkCapabilities;
- if (!networkPriority.allowMetered() && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+ final boolean isSelectedUnderlyingNetwork =
+ currentlySelected != null
+ && Objects.equals(currentlySelected.network, networkRecord.network);
+
+ final int meteredMatch = networkPriority.getMetered();
+ final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+ if (meteredMatch == MATCH_REQUIRED && !isMetered
+ || meteredMatch == MATCH_FORBIDDEN && isMetered) {
+ return false;
+ }
+
+ // Fails bandwidth requirements if either (a) less than exit threshold, or (b), not
+ // selected, but less than entry threshold
+ if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps()
+ || (caps.getLinkUpstreamBandwidthKbps()
+ < networkPriority.getMinEntryUpstreamBandwidthKbps()
+ && !isSelectedUnderlyingNetwork)) {
+ return false;
+ }
+
+ if (caps.getLinkDownstreamBandwidthKbps()
+ < networkPriority.getMinExitDownstreamBandwidthKbps()
+ || (caps.getLinkDownstreamBandwidthKbps()
+ < networkPriority.getMinEntryDownstreamBandwidthKbps()
+ && !isSelectedUnderlyingNetwork)) {
return false;
}
@@ -166,19 +189,19 @@
}
// TODO: Move the Network Quality check to the network metric monitor framework.
- if (networkPriority.getNetworkQuality()
- > getWifiQuality(networkRecord, currentlySelected, carrierConfig)) {
+ if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) {
return false;
}
- if (networkPriority.getSsid() != null && networkPriority.getSsid() != caps.getSsid()) {
+ if (!networkPriority.getSsids().isEmpty()
+ && !networkPriority.getSsids().contains(caps.getSsid())) {
return false;
}
return true;
}
- private static int getWifiQuality(
+ private static boolean isWifiRssiAcceptable(
UnderlyingNetworkRecord networkRecord,
UnderlyingNetworkRecord currentlySelected,
PersistableBundle carrierConfig) {
@@ -189,14 +212,14 @@
if (isSelectedNetwork
&& caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
- return NETWORK_QUALITY_OK;
+ return true;
}
if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
- return NETWORK_QUALITY_OK;
+ return true;
}
- return NETWORK_QUALITY_ANY;
+ return false;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -226,26 +249,31 @@
.getSystemService(TelephonyManager.class)
.createForSubscriptionId(subId);
- if (!networkPriority.getAllowedOperatorPlmnIds().isEmpty()) {
+ if (!networkPriority.getOperatorPlmnIds().isEmpty()) {
final String plmnId = subIdSpecificTelephonyMgr.getNetworkOperator();
- if (!networkPriority.getAllowedOperatorPlmnIds().contains(plmnId)) {
+ if (!networkPriority.getOperatorPlmnIds().contains(plmnId)) {
return false;
}
}
- if (!networkPriority.getAllowedSpecificCarrierIds().isEmpty()) {
+ if (!networkPriority.getSimSpecificCarrierIds().isEmpty()) {
final int carrierId = subIdSpecificTelephonyMgr.getSimSpecificCarrierId();
- if (!networkPriority.getAllowedSpecificCarrierIds().contains(carrierId)) {
+ if (!networkPriority.getSimSpecificCarrierIds().contains(carrierId)) {
return false;
}
}
- if (!networkPriority.allowRoaming() && !caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
+ final int roamingMatch = networkPriority.getRoaming();
+ final boolean isRoaming = !caps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ if (roamingMatch == MATCH_REQUIRED && !isRoaming
+ || roamingMatch == MATCH_FORBIDDEN && isRoaming) {
return false;
}
- if (networkPriority.requireOpportunistic()) {
- if (!isOpportunistic(snapshot, caps.getSubscriptionIds())) {
+ final int opportunisticMatch = networkPriority.getOpportunistic();
+ final boolean isOpportunistic = isOpportunistic(snapshot, caps.getSubscriptionIds());
+ if (opportunisticMatch == MATCH_REQUIRED) {
+ if (!isOpportunistic) {
return false;
}
@@ -265,6 +293,8 @@
.contains(SubscriptionManager.getActiveDataSubscriptionId())) {
return false;
}
+ } else if (opportunisticMatch == MATCH_FORBIDDEN && !isOpportunistic) {
+ return false;
}
return true;
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index df2f0d5..c0488b1 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -32,7 +32,7 @@
import com.android.server.vcn.VcnContext;
import java.util.Comparator;
-import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Objects;
/**
@@ -77,7 +77,7 @@
static Comparator<UnderlyingNetworkRecord> getComparator(
VcnContext vcnContext,
- LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
@@ -87,7 +87,7 @@
NetworkPriorityClassifier.calculatePriorityClass(
vcnContext,
left,
- underlyingNetworkPriorities,
+ underlyingNetworkTemplates,
subscriptionGroup,
snapshot,
currentlySelected,
@@ -96,7 +96,7 @@
NetworkPriorityClassifier.calculatePriorityClass(
vcnContext,
right,
- underlyingNetworkPriorities,
+ underlyingNetworkTemplates,
subscriptionGroup,
snapshot,
currentlySelected,
@@ -133,7 +133,7 @@
void dump(
VcnContext vcnContext,
IndentingPrintWriter pw,
- LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
@@ -145,7 +145,7 @@
NetworkPriorityClassifier.calculatePriorityClass(
vcnContext,
this,
- underlyingNetworkPriorities,
+ underlyingNetworkTemplates,
subscriptionGroup,
snapshot,
currentlySelected,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0792d9b..df98390 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -231,6 +231,7 @@
import android.net.ConnectivityManager;
import android.net.ConnectivitySettingsManager;
import android.net.IIpConnectivityMetrics;
+import android.net.ProfileNetworkPreference;
import android.net.ProxyInfo;
import android.net.Uri;
import android.net.VpnManager;
@@ -17538,10 +17539,14 @@
}
int networkPreference = preferentialNetworkServiceEnabled
? PROFILE_NETWORK_PREFERENCE_ENTERPRISE : PROFILE_NETWORK_PREFERENCE_DEFAULT;
+ ProfileNetworkPreference.Builder preferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ preferenceBuilder.setPreference(networkPreference);
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceBuilder.build());
mInjector.binderWithCleanCallingIdentity(() ->
- mInjector.getConnectivityManager().setProfileNetworkPreference(
- UserHandle.of(userId),
- networkPreference,
+ mInjector.getConnectivityManager().setProfileNetworkPreferences(
+ UserHandle.of(userId), preferences,
null /* executor */, null /* listener */));
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2aee778..2303495 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1837,7 +1837,7 @@
t.traceBegin("StartNetworkStatsService");
try {
- networkStats = NetworkStatsService.create(context, networkManagement);
+ networkStats = NetworkStatsService.create(context);
ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
} catch (Throwable e) {
reportWtf("starting NetworkStats Service", e);
diff --git a/services/midi/OWNERS b/services/midi/OWNERS
new file mode 100644
index 0000000..f4d51f9
--- /dev/null
+++ b/services/midi/OWNERS
@@ -0,0 +1 @@
+philburk@google.com
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index bcb2cf8..187b012 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -93,6 +93,7 @@
<uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/>
<uses-permission android:name="android.permission.READ_PROJECTION_STATE"/>
<uses-permission android:name="android.permission.KILL_UID"/>
+ <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK"/>
<uses-permission
android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
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 3ac30d02..a63aa6a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -37,6 +37,8 @@
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.PasswordMetrics.computeForPasswordOrPin;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
import static android.net.InetAddresses.parseNumericAddress;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
@@ -99,7 +101,7 @@
import android.content.pm.UserInfo;
import android.graphics.Color;
import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
+import android.net.ProfileNetworkPreference;
import android.net.Uri;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -4044,12 +4046,15 @@
mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
dpms.handleStartUser(managedProfileUserId);
- verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
- eq(UserHandle.of(managedProfileUserId)),
- anyInt(),
- any(),
- any()
- );
+ ProfileNetworkPreference preferenceDetails =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+ .build();
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+ null, null);
}
@Test
@@ -4061,12 +4066,15 @@
mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
dpms.handleStopUser(managedProfileUserId);
- verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
- eq(UserHandle.of(managedProfileUserId)),
- eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT),
- any(),
- any()
- );
+ ProfileNetworkPreference preferenceDetails =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+ .build();
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+ null, null);
}
@Test
@@ -4084,21 +4092,29 @@
dpm.setPreferentialNetworkServiceEnabled(false);
assertThat(dpm.isPreferentialNetworkServiceEnabled()).isFalse();
- verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
- eq(UserHandle.of(managedProfileUserId)),
- eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT),
- any(),
- any()
- );
+
+ ProfileNetworkPreference preferenceDetails =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+ .build();
+ List<ProfileNetworkPreference> preferences = new ArrayList<>();
+ preferences.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+ null, null);
dpm.setPreferentialNetworkServiceEnabled(true);
assertThat(dpm.isPreferentialNetworkServiceEnabled()).isTrue();
- verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
- eq(UserHandle.of(managedProfileUserId)),
- eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE),
- any(),
- any()
- );
+
+ ProfileNetworkPreference preferenceDetails2 =
+ new ProfileNetworkPreference.Builder()
+ .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE)
+ .build();
+ List<ProfileNetworkPreference> preferences2 = new ArrayList<>();
+ preferences2.add(preferenceDetails);
+ verify(getServices().connectivityManager, times(1))
+ .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences2,
+ null, null);
}
@Test
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 b811e28..9a6f61e 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -49,12 +49,8 @@
import static android.net.NetworkPolicyManager.uidPoliciesToString;
import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.METERED_YES;
-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.buildTemplateCarrierMetered;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.MB_IN_BYTES;
@@ -75,6 +71,7 @@
import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
+import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -108,6 +105,8 @@
import android.app.IUidObserver;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.Intent;
@@ -125,8 +124,6 @@
import android.net.NetworkCapabilities;
import android.net.NetworkPolicy;
import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
import android.net.wifi.WifiInfo;
@@ -138,7 +135,6 @@
import android.os.PowerSaveState;
import android.os.RemoteException;
import android.os.SimpleClock;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -263,12 +259,13 @@
private @Mock CarrierConfigManager mCarrierConfigManager;
private @Mock TelephonyManager mTelephonyManager;
private @Mock UserManager mUserManager;
+ private @Mock NetworkStatsManager mStatsManager;
+ private TestDependencies mDeps;
private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor =
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
private ActivityManagerInternal mActivityManagerInternal;
- private NetworkStatsManagerInternal mStatsService;
private IUidObserver mUidObserver;
private INetworkManagementEventObserver mNetworkObserver;
@@ -335,8 +332,47 @@
.setBatterySaverEnabled(false).build();
final PowerManagerInternal pmInternal = addLocalServiceMock(PowerManagerInternal.class);
when(pmInternal.getLowPowerState(anyInt())).thenReturn(state);
+ }
- mStatsService = addLocalServiceMock(NetworkStatsManagerInternal.class);
+ private class TestDependencies extends NetworkPolicyManagerService.Dependencies {
+ private final SparseArray<NetworkStats.Bucket> mMockedStats = new SparseArray<>();
+
+ TestDependencies(Context context) {
+ super(context);
+ }
+
+ @Override
+ long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+ int total = 0;
+ for (int i = 0; i < mMockedStats.size(); i++) {
+ NetworkStats.Bucket bucket = mMockedStats.valueAt(i);
+ total += bucket.getRxBytes() + bucket.getTxBytes();
+ }
+ return total;
+ }
+
+ @Override
+ List<NetworkStats.Bucket> getNetworkUidBytes(NetworkTemplate template, long start,
+ long end) {
+ final List<NetworkStats.Bucket> ret = new ArrayList<>();
+ for (int i = 0; i < mMockedStats.size(); i++) {
+ ret.add(mMockedStats.valueAt(i));
+ }
+ return ret;
+ }
+
+ private void setMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+ final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
+ when(bucket.getUid()).thenReturn(uid);
+ when(bucket.getRxBytes()).thenReturn(rxBytes);
+ when(bucket.getTxBytes()).thenReturn(txBytes);
+ mMockedStats.set(uid, bucket);
+ }
+
+ private void increaseMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+ final NetworkStats.Bucket bucket = mMockedStats.get(uid);
+ setMockedTotalBytes(uid, bucket.getRxBytes() + rxBytes, bucket.getTxBytes() + txBytes);
+ }
}
@Before
@@ -376,6 +412,8 @@
return mConnManager;
case Context.USER_SERVICE:
return mUserManager;
+ case Context.NETWORK_STATS_SERVICE:
+ return mStatsManager;
default:
return super.getSystemService(name);
}
@@ -400,8 +438,9 @@
}).when(mActivityManager).registerUidObserver(any(), anyInt(), anyInt(), any(String.class));
mFutureIntent = newRestrictBackgroundChangedFuture();
+ mDeps = new TestDependencies(mServiceContext);
mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
- mNetworkManager, mIpm, mClock, mPolicyDir, true);
+ mNetworkManager, mIpm, mClock, mPolicyDir, true, mDeps);
mService.bindConnectivityManager();
mPolicyListener = new NetworkPolicyListenerAnswer(mService);
@@ -456,6 +495,9 @@
verify(mNetworkManager).registerObserver(networkObserver.capture());
mNetworkObserver = networkObserver.getValue();
+ // Simulate NetworkStatsService broadcast stats updated to signal its readiness.
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_UPDATED));
+
NetworkPolicy defaultPolicy = mService.buildDefaultCarrierPolicy(0, "");
mDefaultWarningBytes = defaultPolicy.warningBytes;
mDefaultLimitBytes = defaultPolicy.limitBytes;
@@ -479,7 +521,6 @@
LocalServices.removeServiceForTest(DeviceIdleInternal.class);
LocalServices.removeServiceForTest(AppStandbyInternal.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
- LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
}
@After
@@ -1108,10 +1149,7 @@
when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
// pretend that 512 bytes total have happened
- stats = new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, 256L, 2L, 256L, 2L);
- when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
- .thenReturn(stats.getTotalBytes());
+ mDeps.setMockedTotalBytes(UID_A, 256L, 256L);
mPolicyListener.expect().onMeteredIfacesChanged(any());
setNetworkPolicies(new NetworkPolicy(
@@ -1124,26 +1162,6 @@
@Test
public void testNotificationWarningLimitSnooze() throws Exception {
- // Create a place to store fake usage
- final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<Long>() {
- @Override
- public Long answer(InvocationOnMock invocation) throws Throwable {
- final NetworkStatsHistory.Entry entry = history.getValues(
- invocation.getArgument(1), invocation.getArgument(2), null);
- return entry.rxBytes + entry.txBytes;
- }
- });
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<NetworkStats>() {
- @Override
- public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
- return stats;
- }
- });
-
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
@@ -1161,9 +1179,7 @@
// Normal usage means no notification
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1178,9 +1194,7 @@
// Push over warning
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1196,9 +1210,7 @@
// Push over warning, but with a config that isn't from an identified carrier
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1215,9 +1227,7 @@
// Push over limit
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1810), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1810), 0);
reset(mTelephonyManager, mNetworkManager, mNotifManager);
TelephonyManager tmSub = expectMobileDefaults();
@@ -1248,26 +1258,6 @@
@Test
public void testNotificationRapid() throws Exception {
- // Create a place to store fake usage
- final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<Long>() {
- @Override
- public Long answer(InvocationOnMock invocation) throws Throwable {
- final NetworkStatsHistory.Entry entry = history.getValues(
- invocation.getArgument(1), invocation.getArgument(2), null);
- return entry.rxBytes + entry.txBytes;
- }
- });
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenAnswer(new Answer<NetworkStats>() {
- @Override
- public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
- return stats;
- }
- });
-
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
@@ -1285,9 +1275,7 @@
// Using 20% data in 20% time is normal
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
reset(mNotifManager);
mService.updateNetworks();
@@ -1297,16 +1285,9 @@
// Using 80% data in 20% time is alarming; but spread equally among
// three UIDs means we get generic alert
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
- stats.clear();
- stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
- stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
- stats.insertEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(480), 0);
+ mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+ mDeps.setMockedTotalBytes(UID_C, DataUnit.MEGABYTES.toBytes(480), 0);
reset(mNotifManager);
mService.updateNetworks();
@@ -1325,14 +1306,9 @@
// Using 80% data in 20% time is alarming; but mostly done by one UID
// means we get specific alert
{
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
- stats.clear();
- stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
- stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
- DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(960), 0);
+ mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+ mDeps.setMockedTotalBytes(UID_C, 0, 0);
reset(mNotifManager);
mService.updateNetworks();
@@ -1362,13 +1338,10 @@
// bring up wifi network with metered policy
snapshots = List.of(buildWifi());
- stats = new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
+ mDeps.setMockedTotalBytes(UID_A, 0L, 0L);
{
when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
- when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
- currentTimeMillis())).thenReturn(stats.getTotalBytes());
mPolicyListener.expect().onMeteredIfacesChanged(any());
setNetworkPolicies(new NetworkPolicy(
@@ -1647,18 +1620,6 @@
final NetworkPolicyManagerInternal internal = LocalServices
.getService(NetworkPolicyManagerInternal.class);
- // Create a place to store fake usage
- final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenAnswer(invocation -> {
- final NetworkStatsHistory.Entry entry = history.getValues(
- invocation.getArgument(1), invocation.getArgument(2), null);
- return entry.rxBytes + entry.txBytes;
- });
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenReturn(stats);
-
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
@@ -1669,9 +1630,7 @@
setCurrentTimeMillis(end);
// Get some data usage in place
- history.clear();
- history.recordData(start, end,
- new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+ mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
// No data plan
{
@@ -1786,22 +1745,11 @@
true);
}
- private void increaseMockedTotalBytes(NetworkStats stats, long rxBytes, long txBytes) {
- stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
- rxBytes, 1, txBytes, 1, 0);
- when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
- .thenReturn(stats.getTotalBytes());
- when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
- .thenReturn(stats);
- }
-
private void triggerOnStatsProviderWarningOrLimitReached() throws InterruptedException {
- final NetworkPolicyManagerInternal npmi = LocalServices
- .getService(NetworkPolicyManagerInternal.class);
- npmi.onStatsProviderWarningOrLimitReached("TEST");
+ mService.onStatsProviderWarningOrLimitReached();
// Wait for processing of MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED.
postMsgAndWaitForCompletion();
- verify(mStatsService).forceUpdate();
+ verify(mStatsManager).forceUpdate();
// Wait for processing of MSG_*_INTERFACE_QUOTAS.
postMsgAndWaitForCompletion();
}
@@ -1814,13 +1762,12 @@
public void testStatsProviderWarningAndLimitReached() throws Exception {
final int CYCLE_DAY = 15;
- final NetworkStats stats = new NetworkStats(0L, 1);
- increaseMockedTotalBytes(stats, 2999, 2000);
+ mDeps.setMockedTotalBytes(UID_A, 2999, 2000);
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
Long.MAX_VALUE);
// Set warning to 7KB and limit to 10KB.
@@ -1830,32 +1777,32 @@
postMsgAndWaitForCompletion();
// Verifies that remaining quotas are set to providers.
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
- reset(mStatsService);
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
+ reset(mStatsManager);
// Increase the usage and simulates that limit reached fires earlier by provider,
// but actually the quota is not yet reached. Verifies that the limit reached leads to
// a force update and new quotas should be set.
- increaseMockedTotalBytes(stats, 1000, 999);
+ mDeps.increaseMockedTotalBytes(UID_A, 1000, 999);
triggerOnStatsProviderWarningOrLimitReached();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
- reset(mStatsService);
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
+ reset(mStatsManager);
// Increase the usage and simulate warning reached, the new warning should be unlimited
// since service will disable warning quota to stop lower layer from keep triggering
// warning reached event.
- increaseMockedTotalBytes(stats, 1000L, 1000);
+ mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
triggerOnStatsProviderWarningOrLimitReached();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(
TEST_IFACE, Long.MAX_VALUE, 1002L);
- reset(mStatsService);
+ reset(mStatsManager);
// Increase the usage that over the warning and limit, the new limit should set to 1 to
// block the network traffic.
- increaseMockedTotalBytes(stats, 1000L, 1000);
+ mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
triggerOnStatsProviderWarningOrLimitReached();
- verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
- reset(mStatsService);
+ verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
+ reset(mStatsManager);
}
private void enableRestrictedMode(boolean enable) throws Exception {
@@ -2145,7 +2092,7 @@
}
private void verifyAdvisePersistThreshold() throws Exception {
- verify(mStatsService).advisePersistThreshold(anyLong());
+ verify(mStatsManager).advisePersistThreshold(anyLong());
}
private static class TestAbstractFuture<T> extends AbstractFuture<T> {
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 26b34fd..304fe5a 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -30,6 +30,7 @@
import android.hardware.power.stats.State;
import android.hardware.power.stats.StateResidency;
import android.hardware.power.stats.StateResidencyResult;
+import android.os.Looper;
import androidx.test.InstrumentationRegistry;
@@ -145,12 +146,12 @@
}
@Override
- PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String meterFilename, String meterCacheFilename,
+ PowerStatsLogger createPowerStatsLogger(Context context, Looper looper,
+ File dataStoragePath, String meterFilename, String meterCacheFilename,
String modelFilename, String modelCacheFilename,
String residencyFilename, String residencyCacheFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
- mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath,
+ mPowerStatsLogger = new PowerStatsLogger(context, looper, dataStoragePath,
meterFilename, meterCacheFilename,
modelFilename, modelCacheFilename,
residencyFilename, residencyCacheFilename,
diff --git a/services/tests/servicestests/src/com/android/server/wm/OWNERS b/services/tests/servicestests/src/com/android/server/wm/OWNERS
new file mode 100644
index 0000000..361760d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b109c46..e59c2e4 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2459,6 +2459,10 @@
*
* Note: If {@code *} is specified for the original code, any ImsReasonInfo with the matching
* {@code MESSAGE} will be remapped to {@code NEW_CODE}.
+ * If {@code *} is specified for the message, any ImsReasonInfo with the matching
+ * {@code ORIGINAL_CODE} will be remapped to {@code NEW_CODE}.
+ * The wildcard for {@code ORIGINAL_CODE} takes precedence to the wildcard for {@code MESSAGE}.
+ * A mapping with both wildcards has no effect.
*
* Example: "501|call completion elsewhere|1014"
* When the {@link ImsReasonInfo#getCode()} is {@link ImsReasonInfo#CODE_USER_TERMINATED} and
@@ -4724,7 +4728,7 @@
/**
* A priority list of ePDG addresses to be used. Possible values are {@link
* #EPDG_ADDRESS_STATIC}, {@link #EPDG_ADDRESS_PLMN}, {@link #EPDG_ADDRESS_PCO}, {@link
- * #EPDG_ADDRESS_CELLULAR_LOC}
+ * #EPDG_ADDRESS_CELLULAR_LOC}, {@link #EPDG_ADDRESS_VISITED_COUNTRY}
*/
public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY =
KEY_PREFIX + "epdg_address_priority_int_array";
@@ -4899,7 +4903,8 @@
EPDG_ADDRESS_STATIC,
EPDG_ADDRESS_PLMN,
EPDG_ADDRESS_PCO,
- EPDG_ADDRESS_CELLULAR_LOC
+ EPDG_ADDRESS_CELLULAR_LOC,
+ EPDG_ADDRESS_VISITED_COUNTRY
})
public @interface EpdgAddressType {}
@@ -4913,6 +4918,8 @@
public static final int EPDG_ADDRESS_PCO = 2;
/** Use cellular location to chose epdg server */
public static final int EPDG_ADDRESS_CELLULAR_LOC = 3;
+ /* Use Visited Country FQDN rule*/
+ public static final int EPDG_ADDRESS_VISITED_COUNTRY = 4;
/** @hide */
@IntDef({ID_TYPE_FQDN, ID_TYPE_RFC822_ADDR, ID_TYPE_KEY_ID})
@@ -5846,10 +5853,10 @@
/* Default value is 2 seconds. */
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
- sDefaults.putInt(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000);
- sDefaults.putInt(
+ sDefaults.putLong(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000L);
+ sDefaults.putLong(
KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
- 120000);
+ 120000L);
sDefaults.putAll(ImsServiceEntitlement.getDefaults());
sDefaults.putAll(Gps.getDefaults());
sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index ba95841..39ab7eb 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -592,6 +592,7 @@
int RIL_UNSOL_UNTHROTTLE_APN = 1052;
int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED = 1053;
int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED = 1054;
+ int RIL_UNSOL_SLICING_CONFIG_CHANGED = 1055;
/* The following unsols are not defined in RIL.h */
int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 97ebba6..8be7324 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -26,7 +26,19 @@
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
// SPDX-license-identifier-CPL-1.0
- default_applicable_licenses: ["frameworks_base_license"],
+ default_applicable_licenses: ["frameworks_base_test-base_license"],
+}
+
+license {
+ name: "frameworks_base_test-base_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-CPL-1.0",
+ ],
+ license_text: [
+ "src/junit/cpl-v10.html",
+ ],
}
java_sdk_library {
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index 0f56bb3..2a19af9 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -18,12 +18,19 @@
// =====================================
package {
// See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-CPL-1.0
- default_applicable_licenses: ["frameworks_base_license"],
+ default_applicable_licenses: ["frameworks_base_test-runner_license"],
+}
+
+license {
+ name: "frameworks_base_test-runner_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-CPL-1.0",
+ ],
+ license_text: [
+ "src/junit/cpl-v10.html",
+ ],
}
java_sdk_library {
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index d03aee2..2fbcf9d 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -15,60 +15,105 @@
*/
package android.net.vcn;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
-public class VcnCellUnderlyingNetworkTemplateTest {
+public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
// Package private for use in VcnGatewayConnectionConfigTest
- static VcnCellUnderlyingNetworkTemplate getTestNetworkPriority() {
+ static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
return new VcnCellUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setAllowedOperatorPlmnIds(ALLOWED_PLMN_IDS)
- .setAllowedSpecificCarrierIds(ALLOWED_CARRIER_IDS)
- .setAllowRoaming(true /* allowRoaming */)
- .setRequireOpportunistic(true /* requireOpportunistic */)
+ .setMetered(MATCH_FORBIDDEN)
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
+ .setOperatorPlmnIds(ALLOWED_PLMN_IDS)
+ .setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS)
+ .setRoaming(MATCH_FORBIDDEN)
+ .setOpportunistic(MATCH_REQUIRED)
.build();
}
@Test
public void testBuilderAndGetters() {
- final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
- assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
- assertTrue(networkPriority.allowMetered());
- assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedOperatorPlmnIds());
- assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getAllowedSpecificCarrierIds());
- assertTrue(networkPriority.allowRoaming());
- assertTrue(networkPriority.requireOpportunistic());
+ final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+ assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+ assertEquals(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitDownstreamBandwidthKbps());
+ assertEquals(ALLOWED_PLMN_IDS, networkPriority.getOperatorPlmnIds());
+ assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds());
+ assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming());
+ assertEquals(MATCH_REQUIRED, networkPriority.getOpportunistic());
}
@Test
public void testBuilderAndGettersForDefaultValues() {
final VcnCellUnderlyingNetworkTemplate networkPriority =
new VcnCellUnderlyingNetworkTemplate.Builder().build();
- assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
- assertFalse(networkPriority.allowMetered());
- assertEquals(new HashSet<String>(), networkPriority.getAllowedOperatorPlmnIds());
- assertEquals(new HashSet<Integer>(), networkPriority.getAllowedSpecificCarrierIds());
- assertFalse(networkPriority.allowRoaming());
- assertFalse(networkPriority.requireOpportunistic());
+ assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+ // Explicitly expect 0, as documented in Javadoc on setter methods.
+ assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
+ assertEquals(new HashSet<String>(), networkPriority.getOperatorPlmnIds());
+ assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds());
+ assertEquals(MATCH_ANY, networkPriority.getRoaming());
+ assertEquals(MATCH_ANY, networkPriority.getOpportunistic());
+ }
+
+ @Test
+ public void testBuilderRequiresStricterEntryCriteria() {
+ try {
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
+ }
}
@Test
public void testPersistableBundle() {
- final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
+ final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
assertEquals(
networkPriority,
VcnUnderlyingNetworkTemplate.fromPersistableBundle(
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 1f2905d..2aef9ae 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -17,8 +17,8 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
-import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES;
-import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_PRIORITIES_KEY;
+import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES;
+import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_TEMPLATES_KEY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -40,8 +40,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedHashSet;
+import java.util.List;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -54,17 +55,17 @@
};
public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
- private static final LinkedHashSet<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_PRIORITIES =
- new LinkedHashSet();
+ private static final List<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_TEMPLATES =
+ new ArrayList();
static {
Arrays.sort(EXPOSED_CAPS);
Arrays.sort(UNDERLYING_CAPS);
- UNDERLYING_NETWORK_PRIORITIES.add(
- VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority());
- UNDERLYING_NETWORK_PRIORITIES.add(
- VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
+ UNDERLYING_NETWORK_TEMPLATES.add(
+ VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+ UNDERLYING_NETWORK_TEMPLATES.add(
+ VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
}
public static final long[] RETRY_INTERVALS_MS =
@@ -95,7 +96,7 @@
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
final VcnGatewayConnectionConfig.Builder builder =
- newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
+ newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES);
return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
}
@@ -174,10 +175,10 @@
}
@Test
- public void testBuilderRequiresNonNullNetworkPriorities() {
+ public void testBuilderRequiresNonNullNetworkTemplates() {
try {
newBuilder().setVcnUnderlyingNetworkPriorities(null);
- fail("Expected exception due to invalid underlyingNetworkPriorities");
+ fail("Expected exception due to invalid underlyingNetworkTemplates");
} catch (NullPointerException e) {
}
}
@@ -219,7 +220,7 @@
Arrays.sort(exposedCaps);
assertArrayEquals(EXPOSED_CAPS, exposedCaps);
- assertEquals(UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities());
+ assertEquals(UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities());
assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams());
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
@@ -234,13 +235,13 @@
}
@Test
- public void testParsePersistableBundleWithoutVcnUnderlyingNetworkPriorities() {
+ public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
- configBundle.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, null);
+ configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null);
final VcnGatewayConnectionConfig config = new VcnGatewayConnectionConfig(configBundle);
assertEquals(
- DEFAULT_UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities());
+ DEFAULT_UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities());
}
private static IkeTunnelConnectionParams buildTunnelConnectionParams(String ikePsk) {
@@ -285,39 +286,36 @@
assertNotEquals(config, anotherConfig);
}
- private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkPriorities(
- LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPriorities) {
+ private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkTemplates(
+ List<VcnUnderlyingNetworkTemplate> networkTemplates) {
return buildTestConfigWithExposedCaps(
new VcnGatewayConnectionConfig.Builder(
- "buildTestConfigWithVcnUnderlyingNetworkPriorities",
+ "buildTestConfigWithVcnUnderlyingNetworkTemplates",
TUNNEL_CONNECTION_PARAMS)
- .setVcnUnderlyingNetworkPriorities(networkPriorities),
+ .setVcnUnderlyingNetworkPriorities(networkTemplates),
EXPOSED_CAPS);
}
@Test
- public void testVcnUnderlyingNetworkPrioritiesEquality() throws Exception {
+ public void testVcnUnderlyingNetworkTemplatesEquality() throws Exception {
final VcnGatewayConnectionConfig config =
- buildTestConfigWithVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
+ buildTestConfigWithVcnUnderlyingNetworkTemplates(UNDERLYING_NETWORK_TEMPLATES);
- final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesEqual =
- new LinkedHashSet();
- networkPrioritiesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority());
- networkPrioritiesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
+ final List<VcnUnderlyingNetworkTemplate> networkTemplatesEqual = new ArrayList();
+ networkTemplatesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+ networkTemplatesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
final VcnGatewayConnectionConfig configEqual =
- buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesEqual);
+ buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesEqual);
- final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesNotEqual =
- new LinkedHashSet();
- networkPrioritiesNotEqual.add(
- VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
+ final List<VcnUnderlyingNetworkTemplate> networkTemplatesNotEqual = new ArrayList();
+ networkTemplatesNotEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
final VcnGatewayConnectionConfig configNotEqual =
- buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesNotEqual);
+ buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesNotEqual);
- assertEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesEqual);
+ assertEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesEqual);
assertEquals(config, configEqual);
- assertNotEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesNotEqual);
+ assertNotEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesNotEqual);
assertNotEquals(config, configNotEqual);
}
}
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
new file mode 100644
index 0000000..399e136
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+public class VcnUnderlyingNetworkTemplateTestBase {
+ // Public for use in NetworkPriorityClassifierTest
+ public static final int TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS = 200;
+ public static final int TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS = 100;
+ public static final int TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS = 400;
+ public static final int TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS = 300;
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
index 652057f..4063178 100644
--- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -15,60 +15,94 @@
*/
package android.net.vcn;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
-public class VcnWifiUnderlyingNetworkTemplateTest {
+import java.util.Set;
+
+public class VcnWifiUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
private static final String SSID = "TestWifi";
- private static final int INVALID_NETWORK_QUALITY = -1;
// Package private for use in VcnGatewayConnectionConfigTest
- static VcnWifiUnderlyingNetworkTemplate getTestNetworkPriority() {
+ static VcnWifiUnderlyingNetworkTemplate getTestNetworkTemplate() {
return new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setSsid(SSID)
+ .setMetered(MATCH_FORBIDDEN)
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
+ .setSsids(Set.of(SSID))
.build();
}
@Test
public void testBuilderAndGetters() {
- final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
- assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
- assertTrue(networkPriority.allowMetered());
- assertEquals(SSID, networkPriority.getSsid());
+ final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+ assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+ assertEquals(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitDownstreamBandwidthKbps());
+ assertEquals(Set.of(SSID), networkPriority.getSsids());
}
@Test
public void testBuilderAndGettersForDefaultValues() {
final VcnWifiUnderlyingNetworkTemplate networkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder().build();
- assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
- assertFalse(networkPriority.allowMetered());
- assertNull(SSID, networkPriority.getSsid());
+ assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+ // Explicitly expect 0, as documented in Javadoc on setter methods..
+ assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
+ assertTrue(networkPriority.getSsids().isEmpty());
}
@Test
- public void testBuildWithInvalidNetworkQuality() {
+ public void testBuilderRequiresStricterEntryCriteria() {
try {
new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(INVALID_NETWORK_QUALITY);
- fail("Expected to fail due to the invalid network quality");
- } catch (Exception expected) {
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
}
}
@Test
public void testPersistableBundle() {
- final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
+ final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
assertEquals(
networkPriority,
VcnUnderlyingNetworkTemplate.fromPersistableBundle(
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index f23d5bf..6c849b5 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -16,7 +16,12 @@
package com.android.server.vcn.routeselection;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_ANY;
@@ -74,6 +79,12 @@
private static final int CARRIER_ID = 1;
private static final int CARRIER_ID_OTHER = 2;
+ private static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024;
+ private static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048;
+
+ private static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100;
+ private static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200;
+
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
@@ -81,6 +92,8 @@
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setSignalStrength(WIFI_RSSI)
.setSsid(SSID)
+ .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+ .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
.build();
private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
@@ -91,6 +104,8 @@
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setSubscriptionIds(Set.of(SUB_ID))
.setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
+ .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+ .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
.build();
private static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface");
@@ -144,8 +159,7 @@
public void testMatchWithoutNotMeteredBit() {
final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(false /* allowMetered */)
+ .setMetered(MATCH_FORBIDDEN)
.build();
assertFalse(
@@ -159,12 +173,133 @@
null /* carrierConfig */));
}
+ private void verifyMatchesPriorityRuleForUpstreamBandwidth(
+ int entryUpstreamBandwidth,
+ int exitUpstreamBandwidth,
+ UnderlyingNetworkRecord currentlySelected,
+ boolean expectMatch) {
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinUpstreamBandwidthKbps(entryUpstreamBandwidth, exitUpstreamBandwidth)
+ .build();
+
+ assertEquals(
+ expectMatch,
+ checkMatchesPriorityRule(
+ mVcnContext,
+ wifiNetworkPriority,
+ mWifiNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ currentlySelected,
+ null /* carrierConfig */));
+ }
+
+ private void verifyMatchesPriorityRuleForDownstreamBandwidth(
+ int entryDownstreamBandwidth,
+ int exitDownstreamBandwidth,
+ UnderlyingNetworkRecord currentlySelected,
+ boolean expectMatch) {
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinDownstreamBandwidthKbps(
+ entryDownstreamBandwidth, exitDownstreamBandwidth)
+ .build();
+
+ assertEquals(
+ expectMatch,
+ checkMatchesPriorityRule(
+ mVcnContext,
+ wifiNetworkPriority,
+ mWifiNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ currentlySelected,
+ null /* carrierConfig */));
+ }
+
+ @Test
+ public void testMatchWithEntryUpstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ null /* currentlySelected */,
+ true);
+ }
+
+ @Test
+ public void testMatchWithEntryUpstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ null /* currentlySelected */,
+ false);
+ }
+
+ @Test
+ public void testMatchWithEntryDownstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ null /* currentlySelected */,
+ true);
+ }
+
+ @Test
+ public void testMatchWithEntryDownstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ null /* currentlySelected */,
+ false);
+ }
+
+ @Test
+ public void testMatchWithExitUpstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ mWifiNetworkRecord,
+ true);
+ }
+
+ @Test
+ public void testMatchWithExitUpstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ mWifiNetworkRecord,
+ false);
+ }
+
+ @Test
+ public void testMatchWithExitDownstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ mWifiNetworkRecord,
+ true);
+ }
+
+ @Test
+ public void testMatchWithExitDownstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ mWifiNetworkRecord,
+ false);
+ }
+
private void verifyMatchWifi(
boolean isSelectedNetwork, PersistableBundle carrierConfig, boolean expectMatch) {
final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
.build();
final UnderlyingNetworkRecord selectedNetworkRecord =
isSelectedNetwork ? mWifiNetworkRecord : null;
@@ -213,9 +348,13 @@
final String nwPrioritySsid = useMatchedSsid ? SSID : SSID_OTHER;
final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
new VcnWifiUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setSsid(nwPrioritySsid)
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
+ .setSsids(Set.of(nwPrioritySsid))
.build();
assertEquals(
@@ -239,9 +378,12 @@
private static VcnCellUnderlyingNetworkTemplate.Builder getCellNetworkPriorityBuilder() {
return new VcnCellUnderlyingNetworkTemplate.Builder()
- .setNetworkQuality(NETWORK_QUALITY_OK)
- .setAllowMetered(true /* allowMetered */)
- .setAllowRoaming(true /* allowRoaming */);
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS);
}
@Test
@@ -258,9 +400,7 @@
@Test
public void testMatchOpportunisticCell() {
final VcnCellUnderlyingNetworkTemplate opportunisticCellNetworkPriority =
- getCellNetworkPriorityBuilder()
- .setRequireOpportunistic(true /* requireOpportunistic */)
- .build();
+ getCellNetworkPriorityBuilder().setOpportunistic(MATCH_REQUIRED).build();
when(mSubscriptionSnapshot.isOpportunistic(SUB_ID)).thenReturn(true);
when(mSubscriptionSnapshot.getAllSubIdsInGroup(SUB_GROUP)).thenReturn(new ArraySet<>());
@@ -279,7 +419,7 @@
final String networkPriorityPlmnId = useMatchedPlmnId ? PLMN_ID : PLMN_ID_OTHER;
final VcnCellUnderlyingNetworkTemplate networkPriority =
getCellNetworkPriorityBuilder()
- .setAllowedOperatorPlmnIds(Set.of(networkPriorityPlmnId))
+ .setOperatorPlmnIds(Set.of(networkPriorityPlmnId))
.build();
assertEquals(
@@ -308,7 +448,7 @@
final int networkPriorityCarrierId = useMatchedCarrierId ? CARRIER_ID : CARRIER_ID_OTHER;
final VcnCellUnderlyingNetworkTemplate networkPriority =
getCellNetworkPriorityBuilder()
- .setAllowedSpecificCarrierIds(Set.of(networkPriorityCarrierId))
+ .setSimSpecificCarrierIds(Set.of(networkPriorityCarrierId))
.build();
assertEquals(
@@ -336,7 +476,7 @@
@Test
public void testMatchWifiFailWithoutNotRoamingBit() {
final VcnCellUnderlyingNetworkTemplate networkPriority =
- getCellNetworkPriorityBuilder().setAllowRoaming(false /* allowRoaming */).build();
+ getCellNetworkPriorityBuilder().setRoaming(MATCH_FORBIDDEN).build();
assertFalse(
checkMatchesCellPriorityRule(
@@ -353,7 +493,7 @@
calculatePriorityClass(
mVcnContext,
networkRecord,
- VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
SUB_GROUP,
mSubscriptionSnapshot,
null /* currentlySelected */,
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index 8975713..fbfbf68 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -19,12 +19,11 @@
#include "android-base/file.h"
#include "android-base/stringprintf.h"
#include "android-base/utf8.h"
-
+#include "format/proto/ProtoDeserialize.h"
#include "io/StringStream.h"
#include "io/ZipArchive.h"
#include "java/AnnotationProcessor.h"
#include "test/Test.h"
-#include "format/proto/ProtoDeserialize.h"
namespace aapt {
@@ -59,55 +58,56 @@
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
"integration-tests", "CompileTest", "res"});
+ const std::string kOutDir = testing::TempDir();
// Resource files without periods in the file name should not throw errors
const std::string path0 = BuildPath({kResDir, "values", "values.xml"});
- const std::string path0_out = BuildPath({kResDir, "values_values.arsc.flat"});
+ const std::string path0_out = BuildPath({kOutDir, "values_values.arsc.flat"});
::android::base::utf8::unlink(path0_out.c_str());
- ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ false, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
const std::string path1 = BuildPath({kResDir, "drawable", "image.png"});
- const std::string path1_out = BuildPath({kResDir, "drawable_image.png.flat"});
+ const std::string path1_out = BuildPath({kOutDir, "drawable_image.png.flat"});
::android::base::utf8::unlink(path1_out.c_str());
- ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ false, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
const std::string path2 = BuildPath({kResDir, "drawable", "image.9.png"});
- const std::string path2_out = BuildPath({kResDir, "drawable_image.9.png.flat"});
+ const std::string path2_out = BuildPath({kOutDir, "drawable_image.9.png.flat"});
::android::base::utf8::unlink(path2_out.c_str());
- ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ false, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
// Resource files with periods in the file name should fail on non-legacy compilations
const std::string path3 = BuildPath({kResDir, "values", "values.all.xml"});
- const std::string path3_out = BuildPath({kResDir, "values_values.all.arsc.flat"});
+ const std::string path3_out = BuildPath({kOutDir, "values_values.all.arsc.flat"});
::android::base::utf8::unlink(path3_out.c_str());
- ASSERT_NE(TestCompile(path3, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_NE(TestCompile(path3, kOutDir, /** legacy */ false, diag), 0);
ASSERT_NE(::android::base::utf8::unlink(path3_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path3, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path3, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path3_out.c_str()), 0);
const std::string path4 = BuildPath({kResDir, "drawable", "image.small.png"});
- const std::string path4_out = BuildPath({kResDir, "drawable_image.small.png.flat"});
+ const std::string path4_out = BuildPath({kOutDir, "drawable_image.small.png.flat"});
::android::base::utf8::unlink(path4_out.c_str());
- ASSERT_NE(TestCompile(path4, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_NE(TestCompile(path4, kOutDir, /** legacy */ false, diag), 0);
ASSERT_NE(::android::base::utf8::unlink(path4_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path4, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path4, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path4_out.c_str()), 0);
const std::string path5 = BuildPath({kResDir, "drawable", "image.small.9.png"});
- const std::string path5_out = BuildPath({kResDir, "drawable_image.small.9.png.flat"});
+ const std::string path5_out = BuildPath({kOutDir, "drawable_image.small.9.png.flat"});
::android::base::utf8::unlink(path5_out.c_str());
- ASSERT_NE(TestCompile(path5, kResDir, /** legacy */ false, diag), 0);
+ ASSERT_NE(TestCompile(path5, kOutDir, /** legacy */ false, diag), 0);
ASSERT_NE(::android::base::utf8::unlink(path5_out.c_str()), 0);
- ASSERT_EQ(TestCompile(path5, kResDir, /** legacy */ true, diag), 0);
+ ASSERT_EQ(TestCompile(path5, kOutDir, /** legacy */ true, diag), 0);
ASSERT_EQ(::android::base::utf8::unlink(path5_out.c_str()), 0);
}
@@ -116,9 +116,7 @@
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
"integration-tests", "CompileTest", "DirInput", "res"});
- const std::string kOutputFlata =
- BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
- "CompileTest", "DirInput", "compiled.flata"});
+ const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"});
::android::base::utf8::unlink(kOutputFlata.c_str());
std::vector<android::StringPiece> args;
@@ -147,9 +145,7 @@
const std::string kResZip =
BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
"CompileTest", "ZipInput", "res.zip"});
- const std::string kOutputFlata =
- BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
- "CompileTest", "ZipInput", "compiled.flata"});
+ const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"});
::android::base::utf8::unlink(kOutputFlata.c_str());
@@ -257,9 +253,9 @@
TEST_F(CompilerTest, RelativePathTest) {
StdErrDiagnostics diag;
- const std::string res_path = BuildPath(
- {android::base::Dirname(android::base::GetExecutablePath()),
- "integration-tests", "CompileTest", "res"});
+ const std::string res_path =
+ BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+ "CompileTest", "res"});
const std::string path_values_colors = GetTestPath("values/colors.xml");
WriteFile(path_values_colors, "<resources>"
@@ -272,9 +268,8 @@
"<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>"
"</LinearLayout>");
- const std::string compiled_files_dir = BuildPath(
- {android::base::Dirname(android::base::GetExecutablePath()),
- "integration-tests", "CompileTest", "compiled"});
+ const std::string compiled_files_dir =
+ BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "compiled"});
CHECK(file::mkdirs(compiled_files_dir.data()));
const std::string path_values_colors_out =
@@ -283,9 +278,8 @@
BuildPath({compiled_files_dir, "layout_layout_one.flat"});
::android::base::utf8::unlink(path_values_colors_out.c_str());
::android::base::utf8::unlink(path_layout_layout_one_out.c_str());
- const std::string apk_path = BuildPath(
- {android::base::Dirname(android::base::GetExecutablePath()),
- "integration-tests", "CompileTest", "out.apk"});
+ const std::string apk_path =
+ BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "out.apk"});
const std::string source_set_res = BuildPath({"main", "res"});
const std::string relative_path_values_colors =
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index 285e5a1..5a1b184 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -67,8 +67,7 @@
}
void TestDirectoryFixture::SetUp() {
- temp_dir_ = file::BuildPath({android::base::GetExecutableDirectory(),
- "_temp",
+ temp_dir_ = file::BuildPath({testing::TempDir(), "_temp",
testing::UnitTest::GetInstance()->current_test_case()->name(),
testing::UnitTest::GetInstance()->current_test_info()->name()});
ASSERT_TRUE(file::mkdirs(temp_dir_));
@@ -236,4 +235,4 @@
return args_;
}
-} // namespace aapt
\ No newline at end of file
+} // namespace aapt
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 36bea57..95b43cd 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -1,7 +1,7 @@
#!/bin/bash
LOCAL_DIR="$( dirname "${BASH_SOURCE}" )"
-if git branch -vv | grep -q -E "^\*[^\[]+\[aosp/"; then
+if git log -n 1 --format='%D' HEAD@{upstream} | grep -q aosp/; then
# Change appears to be in AOSP
exit 0
elif git log -n 1 --format='%B' $1 | grep -q -E "^Ignore-AOSP-First: .+" ; then
diff --git a/tools/sdkparcelables/Android.bp b/tools/sdkparcelables/Android.bp
index 9d773e4..ec2bffd 100644
--- a/tools/sdkparcelables/Android.bp
+++ b/tools/sdkparcelables/Android.bp
@@ -14,7 +14,7 @@
"src/**/*.kt",
],
static_libs: [
- "asm-6.0",
+ "asm-7.0",
],
}
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
index 22e8d78..0fb062f 100644
--- a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
@@ -39,7 +39,7 @@
kotlin.system.exitProcess(2)
}
- val ancestorCollector = AncestorCollector(Opcodes.ASM6, null)
+ val ancestorCollector = AncestorCollector(Opcodes.ASM7, null)
for (entry in zipFile.entries()) {
if (entry.name.endsWith(".class")) {