diff options
132 files changed, 6131 insertions, 1797 deletions
diff --git a/Android.bp b/Android.bp index 36ed8656cfbc..d1b4f6606b7e 100644 --- a/Android.bp +++ b/Android.bp @@ -50,12 +50,8 @@ license { "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 @@ filegroup { ":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 @@ filegroup { } 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 @@ filegroup { 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 8af2e02f8d08..4b5f4739bbb9 100644 --- a/ApiDocs.bp +++ b/ApiDocs.bp @@ -60,9 +60,9 @@ stubs_defaults { 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 @@ stubs_defaults { 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 @@ stubs_defaults { 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 { 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 @@ droidstubs { }, } -// 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 @@ -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 31fecd1c45c3..14da6fbcd69c 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -177,6 +177,7 @@ droidstubs { 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 a1a46afcffe6..161a317b2a3c 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -235,7 +235,6 @@ public class PowerExemptionManager { 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 bed38954a70c..2c5965c300e3 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 c6ea175954c9..d5d21052c3fe 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -24,6 +24,20 @@ package { 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 @@ genrule { 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", + ], + system_server_classpath: [ + "service-media-s", + "service-permission", ], - 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", - }, - ], - visibility: ["//visibility:public"], } genrule { @@ -145,108 +146,6 @@ genrule { } 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 { } 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 { } 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 @@ genrule { 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 000000000000..4b6ebc1947e9 --- /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 92339416ebbb..d696bc797825 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -17980,10 +17980,12 @@ package android.hardware.biometrics { 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 @@ package android.net { 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.sip { 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 @@ package android.net.vcn { 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 @@ package android.net.vcn { 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 @@ package android.net.vcn { 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 @@ package android.security.identity { 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 @@ package android.security.identity { } 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 @@ package android.security.identity { 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 @@ package android.security.identity { 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 @@ package android.telephony { 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 d13170bf4a35..b380e6d1393d 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -71,6 +71,14 @@ package android.content { 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 @@ package android.media { 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 41ce6f689165..a8ef47e29d67 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -138,6 +138,7 @@ package android { 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 @@ package android { 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 { 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 @@ package android.media.tv { 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 @@ package android.net { 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 @@ package android.net { 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 @@ package android.os { 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 9b37457c9907..f94ee8579eab 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -28,8 +28,7 @@ import android.annotation.RequiresPermission; 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 @@ public final class BluetoothAdapter { "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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { } } }; + */ /** @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 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - state = mBluetoothGetStateCache.query(null); - } - } catch (RuntimeException e) { - if (e.getCause() instanceof RemoteException) { - Log.e(TAG, "", e.getCause()); - } else { - throw e; + //state = mBluetoothGetStateCache.query(null); + state = mService.getState(); } + } catch (RemoteException e) { + Log.e(TAG, "", e); + e.rethrowFromSystemServer(); } finally { mServiceLock.readLock().unlock(); } @@ -2100,6 +2104,7 @@ public final class BluetoothAdapter { } } + /* 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 @@ public final class BluetoothAdapter { } }; + */ /** @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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { } } }; + */ /** @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 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mBluetoothGetAdapterConnectionStateCache.query(null); - } - } catch (RuntimeException e) { - if (e.getCause() instanceof RemoteException) { - Log.e(TAG, "getConnectionState:", e.getCause()); - } else { - throw e; + return mService.getAdapterConnectionState(); } + //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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { (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 93f026860856..1edf5cc96b0d 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -23,8 +23,7 @@ import android.annotation.RequiresPermission; 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 @@ public final class BluetoothDevice implements Parcelable, Attributable { 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 @@ public final class BluetoothDevice implements Parcelable, Attributable { } } }; + */ /** @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 @@ public final class BluetoothDevice implements Parcelable, Attributable { 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 000000000000..fb7789db25c7 --- /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 000000000000..fb080c9ec3e3 --- /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 e047e5d81a9d..d0f74e985729 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -240,12 +240,19 @@ public interface BluetoothProfile { 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 fbee57773173..8e9d7b74bf09 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 7129d762cd95..c52a6ee33989 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 @@ public final class AdvertiseSettings implements Parcelable { */ 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 @@ public final class AdvertiseSettings implements Parcelable { mAdvertiseTxPowerLevel = in.readInt(); mAdvertiseConnectable = in.readInt() != 0; mAdvertiseTimeoutMillis = in.readInt(); + mOwnAddressType = in.readInt(); } /** @@ -118,12 +126,23 @@ public final class AdvertiseSettings implements Parcelable { 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 @@ public final class AdvertiseSettings implements Parcelable { 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 @@ public final class AdvertiseSettings implements Parcelable { 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 @@ public final class AdvertiseSettings implements Parcelable { } /** + * 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 e39b198ae384..5c8fae65193d 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 @@ public final class AdvertisingSetParameters implements Parcelable { */ 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 @@ public final class AdvertisingSetParameters implements Parcelable { 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 @@ public final class AdvertisingSetParameters implements Parcelable { mSecondaryPhy = secondaryPhy; mInterval = interval; mTxPowerLevel = txPowerLevel; + mOwnAddressType = ownAddressType; } private AdvertisingSetParameters(Parcel in) { @@ -132,6 +173,7 @@ public final class AdvertisingSetParameters implements Parcelable { mSecondaryPhy = in.readInt(); mInterval = in.readInt(); mTxPowerLevel = in.readInt(); + mOwnAddressType = in.readInt(); } /** @@ -197,6 +239,16 @@ public final class AdvertisingSetParameters implements Parcelable { 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 @@ public final class AdvertisingSetParameters implements Parcelable { + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy + ", interval=" + mInterval - + ", txPowerLevel=" + mTxPowerLevel + "]"; + + ", txPowerLevel=" + mTxPowerLevel + + ", ownAddressType=" + mOwnAddressType + "]"; } @Override @@ -225,6 +278,7 @@ public final class AdvertisingSetParameters implements Parcelable { 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 @@ public final class AdvertisingSetParameters implements Parcelable { 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 @@ public final class AdvertisingSetParameters implements Parcelable { } /** + * 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 @@ public final class AdvertisingSetParameters implements Parcelable { } 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 b9f8a57a75ea..879dceedaaec 100644 --- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -138,6 +138,7 @@ public final class BluetoothLeAdvertiser { 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 9659df6bf04a..8a5e097c2a73 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4206,9 +4206,9 @@ public class Intent implements Parcelable, Cloneable { * 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 @@ public class Intent implements Parcelable, Cloneable { * @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 2ed00b5d2982..7cd7e7acab12 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2485,6 +2485,8 @@ public abstract class PackageManager { * 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 c8c122da4ab8..9fb70d6a07f5 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -38,6 +38,7 @@ import android.os.Parcel; 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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan /** * 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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan 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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan /** * 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 7648cf241298..d41570682fe1 100644 --- a/core/java/android/hardware/biometrics/CryptoObject.java +++ b/core/java/android/hardware/biometrics/CryptoObject.java @@ -18,6 +18,7 @@ package android.hardware.biometrics; 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 @@ import javax.crypto.Mac; /** * 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 @@ public class CryptoObject { 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 @@ public class CryptoObject { /** * 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 @@ public class CryptoObject { 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 a3d595c23095..480923e2b01d 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -58,6 +58,7 @@ import android.os.PowerManager; 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 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * 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 f50aa991a67c..147138e6712a 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -69,6 +69,8 @@ interface INetworkPolicyManager { 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 fab692cba2f6..55541377a0bf 100644 --- a/core/java/android/net/Ikev2VpnProfile.java +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -35,6 +35,7 @@ import android.annotation.Nullable; 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 @@ import java.util.Objects; * 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 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { // 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 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { 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 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { } /** - * Sets whether the local traffic is exempted from the VPN. + * Sets whether the local traffic is exempted from the VPN. + * + * 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 + * @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 6f093835fb08..f42c4b7c420d 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 @@ import java.util.Objects; public final class InternalNetworkUpdateRequest implements Parcelable { @NonNull private final StaticIpConfiguration mIpConfig; - @Nullable + @NonNull private final NetworkCapabilities mNetworkCapabilities; @NonNull @@ -37,20 +36,16 @@ public final class InternalNetworkUpdateRequest implements Parcelable { @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 7ebb646ba3eb..426fc617d5fb 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -535,6 +535,46 @@ public class NetworkPolicyManager { } /** + * 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 cfd458c65362..ac97b3616c3b 100644 --- a/core/java/android/net/PrivateDnsConnectivityChecker.java +++ b/core/java/android/net/PrivateDnsConnectivityChecker.java @@ -44,7 +44,7 @@ public class PrivateDnsConnectivityChecker { */ 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 5c2855307509..319382691925 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -96,6 +96,141 @@ public class VpnManager { */ 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 2ced05693755..1ae1b050d32f 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -41,6 +41,7 @@ import android.os.RemoteException; 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.Inet6Address; 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 @@ public class VpnService extends Service { } } + 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 @@ public class VpnService extends Service { 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 @@ public class VpnService extends Service { throw new IllegalArgumentException("Bad address"); } mAddresses.add(new LinkAddress(address, prefixLength)); - mConfig.updateAllowedFamilies(address); return this; } @@ -579,28 +587,68 @@ public class VpnService extends Service { * 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 @@ public class VpnService extends Service { * 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 @@ public class VpnService extends Service { } /** + * 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 @@ public class VpnService extends Service { 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 febd9b406111..3f7521a12ae9 100644 --- a/core/java/android/net/annotations/PolicyDirection.java +++ b/core/java/android/net/annotations/PolicyDirection.java @@ -24,10 +24,6 @@ import java.lang.annotation.RetentionPolicy; /** * 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 1ac3f0a6d7d1..69e63133eb03 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 static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZ 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.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 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 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork 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 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork @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 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork 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 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork 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 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork super.hashCode(), mAllowedNetworkPlmnIds, mAllowedSpecificCarrierIds, - mAllowRoaming, - mRequireOpportunistic); + mRoamingMatchCriteria, + mOpportunisticMatchCriteria); } @Override @@ -197,8 +246,8 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork 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 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork 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. + * + * <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 allowRoaming the flag to indicate if roaming is allowed. Defaults to {@code - * false}. + * @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 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork @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 d07c24a6529c..a6830b708c31 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.lang.annotation.RetentionPolicy; 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 @@ public final class VcnGatewayConnectionConfig { /** @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 @@ public final class VcnGatewayConnectionConfig { /** @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 @@ public final class VcnGatewayConnectionConfig { @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 @@ public final class VcnGatewayConnectionConfig { 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 @@ public final class VcnGatewayConnectionConfig { 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 @@ public final class VcnGatewayConnectionConfig { checkValidCapability(cap); } - Objects.requireNonNull(mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null"); + validateNetworkTemplateList(mUnderlyingNetworkTemplates); Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null"); validateRetryInterval(mRetryIntervalsMs); @@ -314,6 +302,19 @@ public final class VcnGatewayConnectionConfig { } } + 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 @@ public final class VcnGatewayConnectionConfig { } /** - * 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 @@ public final class VcnGatewayConnectionConfig { 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 @@ public final class VcnGatewayConnectionConfig { mGatewayConnectionName, mTunnelConnectionParams, mExposedCapabilities, - mUnderlyingNetworkPriorities, + mUnderlyingNetworkTemplates, Arrays.hashCode(mRetryIntervalsMs), mMaxMtu); } @@ -451,7 +450,7 @@ public final class VcnGatewayConnectionConfig { 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 @@ public final class VcnGatewayConnectionConfig { @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 @@ public final class VcnGatewayConnectionConfig { } /** - * Set the VcnUnderlyingNetworkTemplate list. + * Set the list of templates to match underlying networks against, in high-to-low priority + * order. + * + * <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. * - * @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>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 @@ public final class VcnGatewayConnectionConfig { 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 d306d5cb6826..3a9ca3edded7 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.Retention; 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 */ + public static final int DEFAULT_MIN_BANDWIDTH_KBPS = 0; + + /** @hide */ + 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 */ - protected static final String ALLOW_METERED_KEY = "mAllowMetered"; - private final boolean mAllowMetered; + static final String MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY = + "mMinEntryDownstreamBandwidthKbps"; + + private final int mMinEntryDownstreamBandwidthKbps; /** @hide */ - protected VcnUnderlyingNetworkTemplate( - int networkPriorityType, int networkQuality, boolean allowMetered) { + 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 @@ public abstract class VcnUnderlyingNetworkTemplate { 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 @@ public abstract class VcnUnderlyingNetworkTemplate { 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 @@ public abstract class VcnUnderlyingNetworkTemplate { 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 the matching criteria for metered networks. + * + * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMetered(int) + * @see VcnCellUnderlyingNetworkTemplate.Builder#setMetered(int) + */ + public int getMetered() { + return mMeteredMatchCriteria; } - /** Return if a metered network is allowed. */ - public boolean allowMetered() { - return mAllowMetered; + /** + * Returns the minimum entry upstream bandwidth allowed by this template. + * + * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int) + * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int) + */ + public int getMinEntryUpstreamBandwidthKbps() { + return mMinEntryUpstreamBandwidthKbps; } /** - * This class is used to incrementally build VcnUnderlyingNetworkTemplate objects. + * Returns the minimum exit 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; - - /** @hide */ - protected Builder() {} - - /** - * 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); - - mNetworkQuality = networkQuality; - return self(); - } + public int getMinExitUpstreamBandwidthKbps() { + return mMinExitUpstreamBandwidthKbps; + } - /** - * 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(); - } + /** + * 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; + } - /** @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 6bbb2bfecda4..23a07abdf0cb 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 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork @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 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork } 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. + * + * <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 ssid the required SSID, or {@code null} if any SSID is acceptable. + * @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 setSsid(@Nullable String ssid) { - mSsid = ssid; + public Builder setMetered(@MatchCriteria int matchCriteria) { + validateMatchCriteria(matchCriteria, "setMetered"); + + mMeteredMatchCriteria = matchCriteria; return this; } - /** Build the VcnWifiUnderlyingNetworkTemplate. */ + /** + * 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 VcnWifiUnderlyingNetworkTemplate build() { - return new VcnWifiUnderlyingNetworkTemplate(mNetworkQuality, mAllowMetered, mSsid); + public Builder setSsids(@NonNull Set<String> ssids) { + validateSsids(ssids); + + mSsids.clear(); + mSsids.addAll(ssids); + return this; } - /** @hide */ - @Override - Builder self() { + /** + * 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( + mMeteredMatchCriteria, + mMinEntryUpstreamBandwidthKbps, + mMinExitUpstreamBandwidthKbps, + mMinEntryDownstreamBandwidthKbps, + mMinExitDownstreamBandwidthKbps, + mSsids); + } } } diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 9eaaa91532d0..542de3fad8b0 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -131,6 +131,7 @@ public class TelephonyRegistryManager { mContext.getAttributionTag(), callback); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -152,6 +153,7 @@ public class TelephonyRegistryManager { mSubscriptionChangedListenerMap.remove(listener); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -194,6 +196,7 @@ public class TelephonyRegistryManager { mContext.getAttributionTag(), callback); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -216,6 +219,7 @@ public class TelephonyRegistryManager { mOpportunisticSubscriptionChangedListenerMap.remove(listener); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -304,6 +308,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCarrierNetworkChange(active); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -329,6 +334,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCarrierNetworkChangeWithSubId(subscriptionId, active); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -347,6 +353,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -364,6 +371,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCallStateForAllSubs(state, incomingNumber); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -376,6 +384,7 @@ public class TelephonyRegistryManager { sRegistry.notifySubscriptionInfoChanged(); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -388,6 +397,7 @@ public class TelephonyRegistryManager { sRegistry.notifyOpportunisticSubscriptionInfoChanged(); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -404,6 +414,7 @@ public class TelephonyRegistryManager { sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -421,6 +432,7 @@ public class TelephonyRegistryManager { sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -439,6 +451,7 @@ public class TelephonyRegistryManager { sRegistry.notifyMessageWaitingChangedForPhoneId(slotIndex, subId, msgWaitingInd); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -454,6 +467,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCallForwardingChangedForSubscriber(subId, callForwardInd); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -469,6 +483,7 @@ public class TelephonyRegistryManager { sRegistry.notifyDataActivityForSubscriber(subId, dataActivityType); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -490,6 +505,7 @@ public class TelephonyRegistryManager { slotIndex, subId, preciseState); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -508,6 +524,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -523,6 +540,7 @@ public class TelephonyRegistryManager { sRegistry.notifyEmergencyNumberList(slotIndex, subId); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -538,6 +556,7 @@ public class TelephonyRegistryManager { sRegistry.notifyOutgoingEmergencyCall(phoneId, subId, emergencyNumber); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -553,6 +572,7 @@ public class TelephonyRegistryManager { sRegistry.notifyOutgoingEmergencySms(phoneId, subId, emergencyNumber); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -570,6 +590,7 @@ public class TelephonyRegistryManager { sRegistry.notifyRadioPowerStateChanged(slotIndex, subId, radioPowerState); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -583,6 +604,7 @@ public class TelephonyRegistryManager { sRegistry.notifyPhoneCapabilityChanged(phoneCapability); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -615,6 +637,7 @@ public class TelephonyRegistryManager { SIM_ACTIVATION_TYPE_DATA, activationState); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -634,6 +657,7 @@ public class TelephonyRegistryManager { SIM_ACTIVATION_TYPE_VOICE, activationState); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -651,6 +675,7 @@ public class TelephonyRegistryManager { sRegistry.notifyUserMobileDataStateChangedForPhoneId(slotIndex, subId, state); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -669,6 +694,7 @@ public class TelephonyRegistryManager { sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, telephonyDisplayInfo); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -683,6 +709,7 @@ public class TelephonyRegistryManager { sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -698,6 +725,7 @@ public class TelephonyRegistryManager { sRegistry.notifySrvccStateChanged(subId, state); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -721,6 +749,7 @@ public class TelephonyRegistryManager { foregroundCallPreciseState, backgroundCallPreciseState); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -741,6 +770,7 @@ public class TelephonyRegistryManager { sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -755,6 +785,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCellLocationForSubscriber(subId, cellLocation); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -769,7 +800,7 @@ public class TelephonyRegistryManager { try { sRegistry.notifyCellInfoForSubscriber(subId, cellInfo); } catch (RemoteException ex) { - + throw ex.rethrowFromSystemServer(); } } @@ -781,7 +812,7 @@ public class TelephonyRegistryManager { try { sRegistry.notifyActiveDataSubIdChanged(activeDataSubId); } catch (RemoteException ex) { - + throw ex.rethrowFromSystemServer(); } } @@ -814,6 +845,7 @@ public class TelephonyRegistryManager { sRegistry.notifyRegistrationFailed(slotIndex, subId, cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode); } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -830,6 +862,7 @@ public class TelephonyRegistryManager { sRegistry.notifyBarringInfoChanged(slotIndex, subId, barringInfo); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -846,6 +879,7 @@ public class TelephonyRegistryManager { sRegistry.notifyPhysicalChannelConfigForSubscriber(slotIndex, subId, configs); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -862,6 +896,7 @@ public class TelephonyRegistryManager { sRegistry.notifyDataEnabled(slotIndex, subId, enabled, reason); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -880,6 +915,7 @@ public class TelephonyRegistryManager { allowedNetworkType); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -895,6 +931,7 @@ public class TelephonyRegistryManager { 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 2e7629a76dee..2ae56f808972 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.Parcel; 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 class VpnConfig implements Parcelable { 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 class VpnConfig implements Parcelable { 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 @@ public class VpnConfig implements Parcelable { //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 @@ public class VpnConfig implements Parcelable { //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 0f1c6f3a150a..270b5d6903b5 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.IntDef; 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.content.IntentFilter; 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.Iterator; 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 @@ public class BatteryStatsImpl extends BatteryStats { 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 @@ public class BatteryStatsImpl extends BatteryStats { * @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 @@ public class BatteryStatsImpl extends BatteryStats { // 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 @@ public class BatteryStatsImpl extends BatteryStats { * 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 @@ public class BatteryStatsImpl extends BatteryStats { // 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 2cfec4b17f63..07b16ed42624 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -242,6 +242,7 @@ cc_library_shared { "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 afad08a87d37..1be18733e97d 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 @@ static jint QTagUid_tagSocketFd(JNIEnv* env, jclass, 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 @@ static jint QTagUid_untagSocketFd(JNIEnv* env, jclass, 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 @@ static jint QTagUid_setCounterSet(JNIEnv* env, jclass, 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 jint QTagUid_deleteTagData(JNIEnv* env, jclass, } 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 60630626fb68..c42517d8a873 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -37,7 +37,6 @@ license { 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 6ecfcec0e804..6bcaace96cbe 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 48a1da15d574..1cc1894a916a 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 com.google.common.truth.Truth.assertThat; 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 androidx.test.runner.AndroidJUnit4; 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 @@ public class MobileRadioPowerCalculatorTest { 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 @@ public class MobileRadioPowerCalculatorTest { 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 cee1a0352a7e..db0c934bd0d4 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 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { } @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 fc44ddc216b4..e7ce9a03f18a 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 android.os.BatteryStats.POWER_DATA_UNAVAILABLE; 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 androidx.test.runner.AndroidJUnit4; 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 @@ public class WifiPowerCalculatorTest { 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 @@ public class WifiPowerCalculatorTest { 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 @@ public class WifiPowerCalculatorTest { 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 @@ public class WifiPowerCalculatorTest { // 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 @@ public class WifiPowerCalculatorTest { // 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 5858e3988486..a8a66403cd79 100644 --- a/graphics/java/android/graphics/Outline.java +++ b/graphics/java/android/graphics/Outline.java @@ -169,8 +169,7 @@ public final class Outline { } /** - * 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 @@ public final class Outline { } /** - * 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 000000000000..2a47a02405e0 --- /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 000000000000..beb03af46303 --- /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 000000000000..7afe3d448bf9 --- /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 6398cee74cba..8e011053d2a7 100644 --- a/identity/java/android/security/identity/CredstoreIdentityCredential.java +++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java @@ -58,14 +58,17 @@ class CredstoreIdentityCredential extends IdentityCredential { 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 @@ class CredstoreIdentityCredential extends IdentityCredential { private boolean mAllowUsingExhaustedKeys = true; private boolean mAllowUsingExpiredKeys = false; + private boolean mIncrementKeyUsageCount = true; @Override public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) { @@ -250,6 +254,11 @@ class CredstoreIdentityCredential extends IdentityCredential { mAllowUsingExpiredKeys = allowUsingExpiredKeys; } + @Override + public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) { + mIncrementKeyUsageCount = incrementKeyUsageCount; + } + private boolean mOperationHandleSet = false; private long mOperationHandle = 0; @@ -264,7 +273,8 @@ class CredstoreIdentityCredential extends IdentityCredential { 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 @@ class CredstoreIdentityCredential extends IdentityCredential { 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 d8d47424e2e8..fb0880ce3521 100644 --- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java +++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java @@ -126,7 +126,8 @@ class CredstoreIdentityCredentialStore extends IdentityCredentialStore { 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 @@ class CredstoreIdentityCredentialStore extends IdentityCredentialStore { + 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 000000000000..e3c6689a8914 --- /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 1e685856d011..cdf746fc9900 100644 --- a/identity/java/android/security/identity/IdentityCredential.java +++ b/identity/java/android/security/identity/IdentityCredential.java @@ -48,7 +48,9 @@ public abstract class IdentityCredential { * 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 @@ public abstract class IdentityCredential { * @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 @@ public abstract class IdentityCredential { * * @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 @@ public abstract class IdentityCredential { * @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 @@ public abstract class IdentityCredential { * * @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 @@ public abstract class IdentityCredential { * * @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 @@ public abstract class IdentityCredential { * 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 @@ public abstract class IdentityCredential { * @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 6ccd0e892141..dbb8aaa7796e 100644 --- a/identity/java/android/security/identity/IdentityCredentialStore.java +++ b/identity/java/android/security/identity/IdentityCredentialStore.java @@ -209,6 +209,25 @@ public abstract class IdentityCredentialStore { @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 000000000000..afaafce32798 --- /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 71860d261285..d46f9854b278 100644 --- a/identity/java/android/security/identity/ResultData.java +++ b/identity/java/android/security/identity/ResultData.java @@ -28,7 +28,10 @@ import java.util.Collection; /** * 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 919a93b8f107..05fb4c3cf76f 100644 --- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java +++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; 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 @@ public class AndroidKeyStoreMaintenance { * 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 @@ public class AndroidKeyStoreMaintenance { 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 6942017d5f27..3ca457f1a6a7 100644 --- a/libs/hwui/jni/NinePatch.cpp +++ b/libs/hwui/jni/NinePatch.cpp @@ -67,7 +67,7 @@ public: 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 72995efb1c21..8cbb70ed2c86 100644 --- a/libs/hwui/jni/android_util_PathParser.cpp +++ b/libs/hwui/jni/android_util_PathParser.cpp @@ -61,7 +61,7 @@ static jlong createPathDataFromStringPath(JNIEnv* env, jobject, jstring inputStr } 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 312f4ba6eed3..75e017c16674 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 0aff43e00671..5f501372666b 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -3,7 +3,6 @@ elaurent@google.com 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 eeaf6e9015ac..c7c1d54cd3c6 100644 --- a/media/java/Android.bp +++ b/media/java/Android.bp @@ -8,7 +8,7 @@ package { } 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 19ea2de6a434..d1bb41e70b54 100644 --- a/media/java/android/media/BtProfileConnectionInfo.java +++ b/media/java/android/media/BtProfileConnectionInfo.java @@ -34,7 +34,7 @@ public final class BtProfileConnectionInfo implements Parcelable { /** @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 @@ public final class BtProfileConnectionInfo implements Parcelable { } /** + * 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 33acd0d5e0e2..fa0429350a25 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 efd9d1005b05..420f4ef95cd5 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -18,6 +18,7 @@ package android.media.tv; 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.annotation.TestApi; 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.Iterator; 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 @@ public final class TvInputManager { 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 @@ public final class TvInputManager { 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 76b84d98046a..0eb1c311695c 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 773cdc982af7..50bb79ccaa0b 100644 --- a/media/jni/soundpool/Stream.cpp +++ b/media/jni/soundpool/Stream.cpp @@ -275,118 +275,104 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID, 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); - - // 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; - } + 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; + + 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 } - -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 + 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); + + // 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 000000000000..fd66d3b9904e --- /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 73ea663aa37e..75548894c9f0 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 a6c1b5098066..32fd734d61a0 100644 --- a/native/android/libandroid_net.map.txt +++ b/native/android/libandroid_net.map.txt @@ -18,6 +18,10 @@ LIBANDROID_NET { 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 e2f36a77b7c6..d7c22e1a5741 100644 --- a/native/android/net.c +++ b/native/android/net.c @@ -161,3 +161,15 @@ int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen, 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 f684a4d9d89a..d33666d744d1 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 @@ public final class NetworkStats implements AutoCloseable { } /** + * 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 a316b8a617b7..f74edb1a01e5 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 com.android.net.module.util.NetworkIdentityUtils; 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 @@ public class NetworkStatsManager { 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 @@ public class NetworkStatsManager { * @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 @@ public class NetworkStatsManager { } /** + * 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 @@ public class NetworkStatsManager { 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 @@ public class NetworkStatsManager { 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 12937b5cb2c7..a4babb543dbd 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 @@ interface INetworkStatsService { /** 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 dfedf6633dcd..babe0bfb9760 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 @@ interface INetworkStatsSession { */ @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 0d15dffae92d..49aa99b0975b 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 @@ package android.net; 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.SystemService; 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 dalvik.system.CloseGuard; 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 @@ public final class IpSecManager { @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 b00fea4de269..9d532e7929a6 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 @@ public final class NetworkStats implements Parcelable { 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 @@ public final class NetworkStats implements Parcelable { * @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 7935d28f305d..9f9d73f88851 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.Range; 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.BufferedInputStream; 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 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W /** 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 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W @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 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W @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 d8feb88f0fe4..1af32bf5524c 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.compat.annotation.UnsupportedAppUsage; 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 dalvik.system.SocketTagger; 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 @@ import java.net.SocketException; * 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 @@ public class TrafficStats { @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 @@ public class TrafficStats { } /** + * 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 e35f6a648b77..1eb52fb44629 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 @@ import java.net.SocketException; /** * 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 0e9a9da6804b..000000000000 --- 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 97281ed42452..e15acf3ef616 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.buildTemplateMobileWildcard; 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.MINUTE_IN_MILLIS; 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.content.pm.PackageManager; 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.NetworkStatsCollection; 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.Handler; 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 android.util.proto.ProtoOutputStream; 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 class NetworkStatsService extends INetworkStatsService.Stub { } } - 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } /** - * 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStats getDetailedUidStats(String[] requiredIfaces) { + enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); try { final String[] ifacesToQuery = mStatsFactory.augmentWithStackedInterfaces(requiredIfaces); @@ -965,7 +1001,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public void forceUpdate() { - mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); + PermissionUtils.enforceNetworkStackPermission(mContext); final long token = Binder.clearCallingIdentity(); try { @@ -1011,7 +1048,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { }; /** - * 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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); - } - } - - @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)); + /** + * 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 + ")"); } + invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface, + warning, limit)); } @Override @@ -1956,13 +1942,29 @@ public class NetworkStatsService extends INetworkStatsService.Stub { */ // 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mSemaphore = semaphore; mAlertObserver = alertObserver; mStatsProviderCbList = cbList; + mNetworkPolicyManager = networkPolicyManager; } @NonNull @@ -2106,15 +2113,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // 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 011ca0b38e5d..cff45c6be0e0 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.NetworkStats.Bucket; 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 @@ public class DataUsageController { 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 class DataUsageController { } 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 3f95a07cc750..afd44d5bbc90 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 android.util.Log; import com.android.internal.util.ArrayUtils; import java.util.List; +import java.util.Set; /** * Utils class for data usage @@ -73,10 +75,15 @@ public class DataUsageUtils { 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 7e389a19e403..919f602862b4 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 static junit.framework.Assert.assertEquals; 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.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Set; + @RunWith(AndroidJUnit4.class) @SmallTest public class NetworkPolicyEditorTest { @@ -44,7 +47,9 @@ public class NetworkPolicyEditorTest { @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 9be783d61ea8..f0456b3d51ba 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 @@ package com.android.settingslib.net; 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.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.shadows.ShadowSubscriptionManager; +import java.util.Set; + @RunWith(RobolectricTestRunner.class) public class DataUsageControllerTest { @@ -54,8 +51,6 @@ public class DataUsageControllerTest { 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 @@ public class DataUsageControllerTest { private NetworkTemplate mWifiNetworkTemplate; private DataUsageController mController; - private NetworkStatsHistory mNetworkStatsHistory; private final int mDefaultSubscriptionId = 1234; @Before @@ -80,17 +74,16 @@ public class DataUsageControllerTest { .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 e8d584486746..5b0f659e9c7a 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.app.usage.NetworkStatsManager; 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.Mock; 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 @@ public class NetworkCycleDataForUidLoaderTest { 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 618a37e2e40f..5b9b74a96e1c 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 450e9881bef2..c8b4f1109b5e 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.IBluetoothManager; 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { + 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 a2c2dbd407a5..39516802e93b 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.STATS_PER_UID; 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub { 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 000000000000..2dff72fdc344 --- /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 2d6170b4cbea..53c8635c4e0a 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.Configuration; 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.storage.StorageSessionController.ExternalStorageServic 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 @@ class StorageManagerService extends IStorageManager.Stub @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 @@ class StorageManagerService extends IStorageManager.Stub } 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 @@ class StorageManagerService extends IStorageManager.Stub 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 @@ class StorageManagerService extends IStorageManager.Stub // 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 @@ class StorageManagerService extends IStorageManager.Stub 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 7ba032f683b8..c6bca19cd9f6 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 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { 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 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { 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 9a349ef499fc..d75ddba8f041 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 static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub 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 4fc661472b5a..2eb59b28c3e9 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 @@ import java.util.concurrent.atomic.AtomicBoolean; 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 0a114b924063..fba8c05065f2 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 @@ public class AudioDeviceInventory { 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 8c9389101141..6654c0c2304d 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 @@ class ProgramInfoCache { 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 @@ class ProgramInfoCache { } // 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 093ecd57124f..3d5abfe83394 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 @@ public class ClipboardService extends SystemService { 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 @@ public class ClipboardService extends SystemService { @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 cc9efbc64c02..fce673765020 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 @@ public class MultipathPolicyTracker { 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 @@ public class MultipathPolicyTracker { 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 @@ public class MultipathPolicyTracker { 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 2c666b5bb7c7..9a9c3ea05d19 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 @@ public class Vpn { 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 2e2d812c058e..8097f4e9b329 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 03a63b9058a8..8ef42ff97aad 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 class NetworkPolicyManagerInternal { */ 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 class NetworkPolicyManagerInternal { */ 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 2ca057d02278..e1cbdb7d22fe 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.writeIntArrayXml; 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.IUidObserver; 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.NetworkRequest; 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); mAppStandby = LocalServices.getService(AppStandbyInternal.class); - mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class); synchronized (mUidRulesFirstLock) { synchronized (mNetworkPoliciesSecondLock) { @@ -1167,21 +1221,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { }; /** - * 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 ef0079e0c01f..ca675973b2fd 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.ProtoInputStream; 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 @@ public final class PowerStatsLogger extends Handler { 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 bb52c1dc1a29..9953ca8f9b65 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.Binder; 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 @@ public class PowerStatsService extends SystemService { private StatsPullAtomCallbackImpl mPullAtomCallback; @Nullable private PowerStatsInternal mPowerStatsInternal; + @Nullable + @GuardedBy("this") + private Looper mLooper; @VisibleForTesting static class Injector { @@ -127,12 +131,12 @@ public class PowerStatsService extends SystemService { } } - 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 @@ public class PowerStatsService extends SystemService { 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 @@ public class PowerStatsService extends SystemService { 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 @@ public class PowerStatsService extends SystemService { 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 0f37450c24c9..e7d05b623481 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 @@ public final class TextClassificationManagerService extends ITextClassifierServi 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 @@ public final class TextClassificationManagerService extends ITextClassifierServi } } + 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 bd8d13b87125..c96c1ee01a6d 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.NET_CAPABILITY_NOT_ROAMING; 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.internal.annotations.VisibleForTesting.Visibility; 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 @@ class NetworkPriorityClassifier { public static int calculatePriorityClass( VcnContext vcnContext, UnderlyingNetworkRecord networkRecord, - LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities, + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, @@ -94,7 +95,7 @@ class NetworkPriorityClassifier { } int priorityIndex = 0; - for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkPriorities) { + for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkTemplates) { if (checkMatchesPriorityRule( vcnContext, nwPriority, @@ -119,10 +120,32 @@ class NetworkPriorityClassifier { 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 @@ class NetworkPriorityClassifier { } // 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 @@ class NetworkPriorityClassifier { 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 @@ class NetworkPriorityClassifier { .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 @@ class NetworkPriorityClassifier { .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 df2f0d58565e..c0488b18cb65 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.TelephonySubscriptionTracker.TelephonySubscription 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 @@ public class UnderlyingNetworkRecord { static Comparator<UnderlyingNetworkRecord> getComparator( VcnContext vcnContext, - LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities, + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, @@ -87,7 +87,7 @@ public class UnderlyingNetworkRecord { NetworkPriorityClassifier.calculatePriorityClass( vcnContext, left, - underlyingNetworkPriorities, + underlyingNetworkTemplates, subscriptionGroup, snapshot, currentlySelected, @@ -96,7 +96,7 @@ public class UnderlyingNetworkRecord { NetworkPriorityClassifier.calculatePriorityClass( vcnContext, right, - underlyingNetworkPriorities, + underlyingNetworkTemplates, subscriptionGroup, snapshot, currentlySelected, @@ -133,7 +133,7 @@ public class UnderlyingNetworkRecord { void dump( VcnContext vcnContext, IndentingPrintWriter pw, - LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities, + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, @@ -145,7 +145,7 @@ public class UnderlyingNetworkRecord { 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 0792d9bde4b4..df98390ddf05 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.media.IAudioService; 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } 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 2aee778c0933..230349532df3 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1837,7 +1837,7 @@ public final class SystemServer implements Dumpable { 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 000000000000..f4d51f91b51b --- /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 bcb2cf8fa0c3..187b0123c879 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 3ac30d0258a5..a63aa6a918d7 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.PRIVATE_DNS_SET_NO_ERROR; 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.StringParceledListSlice; 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 b811e28a3f71..9a6f61e7c6cf 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.blockedReasonsToString; 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_LIMIT_SNOO 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.IActivityManager; 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.Network; 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.PowerManagerInternal; 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 @@ public class NetworkPolicyManagerServiceTest { 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 @@ public class NetworkPolicyManagerServiceTest { .setBatterySaverEnabled(false).build(); final PowerManagerInternal pmInternal = addLocalServiceMock(PowerManagerInternal.class); when(pmInternal.getLowPowerState(anyInt())).thenReturn(state); + } + + 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); + } - mStatsService = addLocalServiceMock(NetworkStatsManagerInternal.class); + 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 @@ public class NetworkPolicyManagerServiceTest { return mConnManager; case Context.USER_SERVICE: return mUserManager; + case Context.NETWORK_STATS_SERVICE: + return mStatsManager; default: return super.getSystemService(name); } @@ -400,8 +438,9 @@ public class NetworkPolicyManagerServiceTest { }).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 @@ public class NetworkPolicyManagerServiceTest { 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 @@ public class NetworkPolicyManagerServiceTest { LocalServices.removeServiceForTest(DeviceIdleInternal.class); LocalServices.removeServiceForTest(AppStandbyInternal.class); LocalServices.removeServiceForTest(UsageStatsManagerInternal.class); - LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class); } @After @@ -1108,10 +1149,7 @@ public class NetworkPolicyManagerServiceTest { 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 @@ public class NetworkPolicyManagerServiceTest { @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 @@ public class NetworkPolicyManagerServiceTest { // 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 @@ public class NetworkPolicyManagerServiceTest { // 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 @@ public class NetworkPolicyManagerServiceTest { // 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 @@ public class NetworkPolicyManagerServiceTest { // 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 @@ public class NetworkPolicyManagerServiceTest { @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 @@ public class NetworkPolicyManagerServiceTest { // 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 @@ public class NetworkPolicyManagerServiceTest { // 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 @@ public class NetworkPolicyManagerServiceTest { // 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 @@ public class NetworkPolicyManagerServiceTest { // 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 @@ public class NetworkPolicyManagerServiceTest { 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 @@ public class NetworkPolicyManagerServiceTest { 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 @@ public class NetworkPolicyManagerServiceTest { 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 class NetworkPolicyManagerServiceTest { 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 @@ public class NetworkPolicyManagerServiceTest { 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 @@ public class NetworkPolicyManagerServiceTest { } 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 26b34fdd4e04..304fe5a1c9c3 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.PowerEntity; 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 @@ public class PowerStatsServiceTest { } @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 000000000000..361760d7f945 --- /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 b109c4664a7d..e59c2e41a6c0 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2459,6 +2459,10 @@ public class CarrierConfigManager { * * 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 @@ public class CarrierConfigManager { /** * 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 @@ public class CarrierConfigManager { 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 class CarrierConfigManager { 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 @@ public class CarrierConfigManager { /* 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 ba9584112b75..39ab7eb0973c 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -592,6 +592,7 @@ public interface RILConstants { 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 97ebba689d8b..8be732452228 100644 --- a/test-base/Android.bp +++ b/test-base/Android.bp @@ -26,7 +26,19 @@ package { // 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 0f56bb3819f1..2a19af9f8cd2 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 d03aee282ee1..2fbcf9d87bd4 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 1f2905da08f4..2aef9ae7ca32 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 androidx.test.runner.AndroidJUnit4; 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 class VcnGatewayConnectionConfigTest { }; 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 class VcnGatewayConnectionConfigTest { // 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 @@ public class VcnGatewayConnectionConfigTest { } @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 @@ public class VcnGatewayConnectionConfigTest { 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 @@ public class VcnGatewayConnectionConfigTest { } @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 @@ public class VcnGatewayConnectionConfigTest { 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 000000000000..399e13600442 --- /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 652057fd48c7..4063178e005d 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 f23d5bf67ebf..6c849b5af888 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 @@ public class NetworkPriorityClassifierTest { 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 @@ public class NetworkPriorityClassifierTest { .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 @@ public class NetworkPriorityClassifierTest { .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 class NetworkPriorityClassifierTest { 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 @@ public class NetworkPriorityClassifierTest { 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 @@ public class NetworkPriorityClassifierTest { 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 @@ public class NetworkPriorityClassifierTest { 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 @@ public class NetworkPriorityClassifierTest { @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 @@ public class NetworkPriorityClassifierTest { 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 @@ public class NetworkPriorityClassifierTest { 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 @@ public class NetworkPriorityClassifierTest { @Test public void testMatchWifiFailWithoutNotRoamingBit() { final VcnCellUnderlyingNetworkTemplate networkPriority = - getCellNetworkPriorityBuilder().setAllowRoaming(false /* allowRoaming */).build(); + getCellNetworkPriorityBuilder().setRoaming(MATCH_FORBIDDEN).build(); assertFalse( checkMatchesCellPriorityRule( @@ -353,7 +493,7 @@ public class NetworkPriorityClassifierTest { 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 89757134f3a7..fbfbf68b30bb 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 @@ TEST_F(CompilerTest, MultiplePeriods) { 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 @@ TEST_F(CompilerTest, DirInput) { 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 @@ TEST_F(CompilerTest, ZipInput) { 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, DoNotTranslateTest) { 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 @@ TEST_F(CompilerTest, RelativePathTest) { "<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 @@ TEST_F(CompilerTest, RelativePathTest) { 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 285e5a11b4c0..5a1b184febe8 100644 --- a/tools/aapt2/test/Fixture.cpp +++ b/tools/aapt2/test/Fixture.cpp @@ -67,8 +67,7 @@ void ClearDirectory(const android::StringPiece& path) { } 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 @@ std::vector<std::string> LinkCommandBuilder::Build(const std::string& out_apk) { 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 36bea57b710f..95b43cdf253d 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 9d773e44faea..ec2bffdfaf57 100644 --- a/tools/sdkparcelables/Android.bp +++ b/tools/sdkparcelables/Android.bp @@ -14,7 +14,7 @@ java_binary_host { "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 22e8d781335b..0fb062f280e3 100644 --- a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt +++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt @@ -39,7 +39,7 @@ fun main(args: Array<String>) { 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")) { |