diff options
331 files changed, 8199 insertions, 52322 deletions
diff --git a/Android.bp b/Android.bp index 36ed8656cfbc..9e65646154b8 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: [ @@ -69,6 +65,7 @@ filegroup { // Java/AIDL sources under frameworks/base ":framework-annotations", ":framework-blobstore-sources", + ":framework-bluetooth-sources", // TODO(b/214988855) : Remove once framework-bluetooth jar is ready ":framework-connectivity-tiramisu-sources", ":framework-core-sources", ":framework-drm-sources", @@ -81,7 +78,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 +138,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: [ @@ -169,6 +145,7 @@ java_library { "framework-minus-apex", "framework-appsearch.impl", "framework-connectivity.impl", + "framework-connectivity-tiramisu.impl", "framework-graphics.impl", "framework-mediaprovider.impl", "framework-permission.impl", @@ -419,7 +396,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", @@ -527,7 +503,9 @@ filegroup { visibility: ["//visibility:private"], } -// These defaults are used for both the jar stubs and the doc stubs. +// Defaults for all stubs that include the non-updatable framework. These defaults do not include +// module symbols, so will not compile correctly on their own. Users must add module APIs to the +// classpath (or sources) somehow. stubs_defaults { name: "android-non-updatable-stubs-defaults", srcs: [":android-non-updatable-stub-sources"], @@ -535,17 +513,14 @@ stubs_defaults { system_modules: "none", java_version: "1.8", arg_files: ["core/res/AndroidManifest.xml"], - // TODO(b/147699819): remove below aidl includes. aidl: { local_include_dirs: [ - "apex/media/aidl/stable", "media/aidl", "telephony/java", ], include_dirs: [ "frameworks/av/aidl", "frameworks/native/libs/permission/aidl", - "packages/modules/Connectivity/framework/aidl-export", ], }, // These are libs from framework-internal-utils that are required (i.e. being referenced) @@ -567,6 +542,30 @@ stubs_defaults { "android.hardware.usb.gadget-V1.0-java", "android.hardware.vibrator-V1.3-java", "framework-protos", + ], + filter_packages: packages_to_document, + high_mem: true, // Lots of sources => high memory use, see b/170701554 + installable: false, + annotations_enabled: true, + previous_api: ":android.api.public.latest", + merge_annotations_dirs: ["metalava-manual"], + defaults_visibility: ["//visibility:private"], + visibility: ["//frameworks/base/api"], +} + +// Defaults with module APIs in the classpath (mostly from prebuilts). +// Suitable for compiling android-non-updatable. +stubs_defaults { + name: "module-classpath-stubs-defaults", + aidl: { + local_include_dirs: [ + "apex/media/aidl/stable", + ], + include_dirs: [ + "packages/modules/Connectivity/framework/aidl-export", + ], + }, + libs: [ "art.module.public.api", "sdk_module-lib_current_framework-tethering", // There are a few classes from modules used by the core that @@ -577,14 +576,7 @@ stubs_defaults { // NOTE: The below can be removed once the prebuilt stub contains IKE. "sdk_system_current_android.net.ipsec.ike", ], - filter_packages: packages_to_document, - high_mem: true, // Lots of sources => high memory use, see b/170701554 - installable: false, - annotations_enabled: true, - previous_api: ":android.api.public.latest", - merge_annotations_dirs: ["metalava-manual"], defaults_visibility: ["//visibility:private"], - visibility: ["//frameworks/base/api"], } build = [ diff --git a/ApiDocs.bp b/ApiDocs.bp index 8af2e02f8d08..f7bd34ea2d66 100644 --- a/ApiDocs.bp +++ b/ApiDocs.bp @@ -57,12 +57,15 @@ framework_docs_only_libs = [ stubs_defaults { name: "android-non-updatable-doc-stubs-defaults", - defaults: ["android-non-updatable-stubs-defaults"], + defaults: [ + "android-non-updatable-stubs-defaults", + "module-classpath-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, @@ -71,44 +74,21 @@ stubs_defaults { stubs_defaults { name: "framework-doc-stubs-default", + defaults: ["android-non-updatable-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", ":android-test-runner-sources", ], - arg_files: [ - "core/res/AndroidManifest.xml", - ], libs: framework_docs_only_libs, create_doc_stubs: true, - annotations_enabled: true, - filter_packages: packages_to_document, api_levels_annotations_enabled: true, api_levels_annotations_dirs: [ "sdk-dir", "api-versions-jars-dir", ], - previous_api: ":android.api.public.latest", - merge_annotations_dirs: [ - "metalava-manual", - ], write_sdk_values: true, - // TODO(b/169090544): remove below aidl includes. - aidl: { - local_include_dirs: ["media/aidl"], - include_dirs: [ - "frameworks/av/aidl", - "frameworks/native/libs/permission/aidl", - ], - }, } // Defaults module for doc-stubs targets that use module source code as input. @@ -116,8 +96,13 @@ 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-connectivity-tiramisu-updatable-sources", ":framework-graphics-srcs", ":framework-mediaprovider-sources", ":framework-permission-sources", @@ -156,22 +141,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 +153,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..9e8b7071d0c7 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -24,23 +24,15 @@ // with the latest frozen API signature. ///////////////////////////////////////////////////////////////////// -// Common metalava configs -///////////////////////////////////////////////////////////////////// - -stubs_defaults { - name: "metalava-non-updatable-api-stubs-default", - defaults: ["android-non-updatable-stubs-defaults"], - api_levels_annotations_enabled: false, - defaults_visibility: ["//visibility:private"], -} - -///////////////////////////////////////////////////////////////////// // These modules provide source files for the stub libraries ///////////////////////////////////////////////////////////////////// droidstubs { name: "api-stubs-docs-non-updatable", - defaults: ["metalava-non-updatable-api-stubs-default"], + defaults: [ + "android-non-updatable-stubs-defaults", + "module-classpath-stubs-defaults", + ], args: metalava_framework_docs_args, check_api: { current: { @@ -89,7 +81,10 @@ module_libs = " --show-annotation android.annotation.SystemApi\\(" + droidstubs { name: "system-api-stubs-docs-non-updatable", - defaults: ["metalava-non-updatable-api-stubs-default"], + defaults: [ + "android-non-updatable-stubs-defaults", + "module-classpath-stubs-defaults", + ], args: metalava_framework_docs_args + priv_apps, check_api: { current: { @@ -125,7 +120,10 @@ droidstubs { droidstubs { name: "test-api-stubs-docs-non-updatable", - defaults: ["metalava-non-updatable-api-stubs-default"], + defaults: [ + "android-non-updatable-stubs-defaults", + "module-classpath-stubs-defaults", + ], args: metalava_framework_docs_args + test + priv_apps_in_stubs, check_api: { current: { @@ -167,7 +165,10 @@ droidstubs { droidstubs { name: "module-lib-api-stubs-docs-non-updatable", - defaults: ["metalava-non-updatable-api-stubs-default"], + defaults: [ + "android-non-updatable-stubs-defaults", + "module-classpath-stubs-defaults", + ], args: metalava_framework_docs_args + priv_apps_in_stubs + module_libs, check_api: { current: { @@ -177,6 +178,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, @@ -211,6 +213,7 @@ modules_public_stubs = [ "conscrypt.module.public.api.stubs", "framework-appsearch.stubs", "framework-connectivity.stubs", + "framework-connectivity-tiramisu.stubs", "framework-graphics.stubs", "framework-media.stubs", "framework-mediaprovider.stubs", @@ -230,6 +233,7 @@ modules_system_stubs = [ "conscrypt.module.public.api.stubs", // Only has public stubs "framework-appsearch.stubs.system", "framework-connectivity.stubs.system", + "framework-connectivity-tiramisu.stubs.system", "framework-graphics.stubs.system", "framework-media.stubs.system", "framework-mediaprovider.stubs.system", 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 f89c55705ada..0c3a3e60bd9a 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -32,6 +32,7 @@ bootstrap_go_package { "soong", "soong-android", "soong-genrule", + "soong-java", ], srcs: ["api.go"], pluginFor: ["soong_build"], @@ -102,43 +103,31 @@ 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", - ], - 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", - }, +combined_apis { + name: "frameworks-base-api", + bootclasspath: [ + "android.net.ipsec.ike", + "art.module.public.api", + "conscrypt.module.public.api", + "framework-appsearch", + "framework-connectivity", + "framework-connectivity-tiramisu", + "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", ], - visibility: ["//visibility:public"], } genrule { @@ -158,108 +147,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", @@ -278,79 +165,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", @@ -372,42 +186,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", @@ -423,86 +201,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 index 976b140f407f..4b6ebc1947e9 100644 --- a/api/api.go +++ b/api/api.go @@ -15,12 +15,19 @@ 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 @@ -30,22 +37,12 @@ import ( // The properties of the combined_apis module type. type CombinedApisProperties struct { - // Module libraries that have public APIs - Public []string - // Module libraries that have system APIs - System []string - // Module libraries that have module_library APIs - Module_lib []string - // Module libraries that have system_server APIs - System_server []string - // ART module library. The only API library not removed from the filtered api database, 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. - Art_module string + // 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 { @@ -77,6 +74,19 @@ type genruleProps struct { 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" @@ -105,9 +115,9 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { props := genruleProps{} props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename) props.Tools = []string{"metalava"} - props.Out = []string{txt.TxtFilename} + props.Out = []string{filename} props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)") - props.Srcs = createSrcs(txt.BaseTxt, txt.Modules, txt.ModuleTag) + props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...) props.Dists = []android.Dist{ { Targets: []string{"droidcore"}, @@ -130,12 +140,36 @@ func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) { props.Tools = []string{"merge_zips"} props.Out = []string{"current.srcjar"} props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)") - props.Srcs = createSrcs(":api-stubs-docs-non-updatable", modules, "{.public.stubs.source}") + 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"} @@ -144,59 +178,62 @@ func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) { // 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 = createSrcs(":framework-doc-stubs{.api_versions.xml}", modules, ".stubs{.jar}") + 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 createSrcs(base string, modules []string, tag string) []string { - a := make([]string, 0, len(modules)+1) - a = append(a, base) - for _, module := range modules { - a = append(a, ":"+module+tag) - } - return a +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 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 +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, props CombinedApisProperties) { +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: props.Public, + Modules: bootclasspath, ModuleTag: "{.public" + tagSuffix[i], Scope: "public", }) textFiles = append(textFiles, MergedTxtDefinition{ TxtFilename: f, BaseTxt: ":non-updatable-system-" + f, - Modules: props.System, + Modules: bcpWithSystemApi, ModuleTag: "{.system" + tagSuffix[i], Scope: "system", }) textFiles = append(textFiles, MergedTxtDefinition{ TxtFilename: f, BaseTxt: ":non-updatable-module-lib-" + f, - Modules: props.Module_lib, + Modules: bcpWithSystemApi, ModuleTag: "{.module-lib" + tagSuffix[i], Scope: "module-lib", }) textFiles = append(textFiles, MergedTxtDefinition{ TxtFilename: f, BaseTxt: ":non-updatable-system-server-" + f, - Modules: props.System_server, + Modules: system_server_classpath, ModuleTag: "{.system-server" + tagSuffix[i], Scope: "system-server", }) @@ -207,12 +244,22 @@ func createMergedTxts(ctx android.LoadHookContext, props CombinedApisProperties) } func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) { - createMergedTxts(ctx, a.properties) + 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) - createMergedStubsSrcjar(ctx, a.properties.Public) + createMergedModuleLibStubs(ctx, bootclasspath) - // For the filtered api versions, we prune all APIs except art module's APIs. - createFilteredApiVersions(ctx, remove(a.properties.Public, a.properties.Art_module)) + createMergedAnnotations(ctx, bootclasspath) + + createFilteredApiVersions(ctx, bootclasspath) + + createPublicStubsSourceFilegroup(ctx, bootclasspath) } func combinedApisModuleFactory() android.Module { @@ -222,3 +269,36 @@ func combinedApisModuleFactory() android.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/boot/hiddenapi/hiddenapi-max-target-o.txt b/boot/hiddenapi/hiddenapi-max-target-o.txt index 0ec918b11723..9153426b29ab 100644 --- a/boot/hiddenapi/hiddenapi-max-target-o.txt +++ b/boot/hiddenapi/hiddenapi-max-target-o.txt @@ -36441,93 +36441,6 @@ Landroid/net/MobileLinkQualityInfo;->mLteRssnr:I Landroid/net/MobileLinkQualityInfo;->mLteSignalStrength:I Landroid/net/MobileLinkQualityInfo;->mMobileNetworkType:I Landroid/net/MobileLinkQualityInfo;->mRssi:I -Landroid/net/nsd/DnsSdTxtRecord;-><init>()V -Landroid/net/nsd/DnsSdTxtRecord;-><init>(Landroid/net/nsd/DnsSdTxtRecord;)V -Landroid/net/nsd/DnsSdTxtRecord;-><init>([B)V -Landroid/net/nsd/DnsSdTxtRecord;->contains(Ljava/lang/String;)Z -Landroid/net/nsd/DnsSdTxtRecord;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/nsd/DnsSdTxtRecord;->get(Ljava/lang/String;)Ljava/lang/String; -Landroid/net/nsd/DnsSdTxtRecord;->getKey(I)Ljava/lang/String; -Landroid/net/nsd/DnsSdTxtRecord;->getRawData()[B -Landroid/net/nsd/DnsSdTxtRecord;->getValue(I)[B -Landroid/net/nsd/DnsSdTxtRecord;->getValue(Ljava/lang/String;)[B -Landroid/net/nsd/DnsSdTxtRecord;->getValueAsString(I)Ljava/lang/String; -Landroid/net/nsd/DnsSdTxtRecord;->insert([B[BI)V -Landroid/net/nsd/DnsSdTxtRecord;->keyCount()I -Landroid/net/nsd/DnsSdTxtRecord;->mData:[B -Landroid/net/nsd/DnsSdTxtRecord;->mSeperator:B -Landroid/net/nsd/DnsSdTxtRecord;->remove(Ljava/lang/String;)I -Landroid/net/nsd/DnsSdTxtRecord;->set(Ljava/lang/String;Ljava/lang/String;)V -Landroid/net/nsd/DnsSdTxtRecord;->size()I -Landroid/net/nsd/INsdManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V -Landroid/net/nsd/INsdManager$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String; -Landroid/net/nsd/INsdManager$Stub$Proxy;->getMessenger()Landroid/os/Messenger; -Landroid/net/nsd/INsdManager$Stub$Proxy;->mRemote:Landroid/os/IBinder; -Landroid/net/nsd/INsdManager$Stub$Proxy;->setEnabled(Z)V -Landroid/net/nsd/INsdManager$Stub;-><init>()V -Landroid/net/nsd/INsdManager$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/net/nsd/INsdManager$Stub;->TRANSACTION_getMessenger:I -Landroid/net/nsd/INsdManager$Stub;->TRANSACTION_setEnabled:I -Landroid/net/nsd/INsdManager;->setEnabled(Z)V -Landroid/net/nsd/NsdManager;-><init>(Landroid/content/Context;Landroid/net/nsd/INsdManager;)V -Landroid/net/nsd/NsdManager;->BASE:I -Landroid/net/nsd/NsdManager;->checkListener(Ljava/lang/Object;)V -Landroid/net/nsd/NsdManager;->checkProtocol(I)V -Landroid/net/nsd/NsdManager;->checkServiceInfo(Landroid/net/nsd/NsdServiceInfo;)V -Landroid/net/nsd/NsdManager;->DBG:Z -Landroid/net/nsd/NsdManager;->DISABLE:I -Landroid/net/nsd/NsdManager;->disconnect()V -Landroid/net/nsd/NsdManager;->DISCOVER_SERVICES:I -Landroid/net/nsd/NsdManager;->DISCOVER_SERVICES_FAILED:I -Landroid/net/nsd/NsdManager;->DISCOVER_SERVICES_STARTED:I -Landroid/net/nsd/NsdManager;->ENABLE:I -Landroid/net/nsd/NsdManager;->EVENT_NAMES:Landroid/util/SparseArray; -Landroid/net/nsd/NsdManager;->fatal(Ljava/lang/String;)V -Landroid/net/nsd/NsdManager;->FIRST_LISTENER_KEY:I -Landroid/net/nsd/NsdManager;->getListenerKey(Ljava/lang/Object;)I -Landroid/net/nsd/NsdManager;->getMessenger()Landroid/os/Messenger; -Landroid/net/nsd/NsdManager;->getNsdServiceInfoType(Landroid/net/nsd/NsdServiceInfo;)Ljava/lang/String; -Landroid/net/nsd/NsdManager;->init()V -Landroid/net/nsd/NsdManager;->mAsyncChannel:Lcom/android/internal/util/AsyncChannel; -Landroid/net/nsd/NsdManager;->mConnected:Ljava/util/concurrent/CountDownLatch; -Landroid/net/nsd/NsdManager;->mContext:Landroid/content/Context; -Landroid/net/nsd/NsdManager;->mHandler:Landroid/net/nsd/NsdManager$ServiceHandler; -Landroid/net/nsd/NsdManager;->mListenerKey:I -Landroid/net/nsd/NsdManager;->mListenerMap:Landroid/util/SparseArray; -Landroid/net/nsd/NsdManager;->mMapLock:Ljava/lang/Object; -Landroid/net/nsd/NsdManager;->mService:Landroid/net/nsd/INsdManager; -Landroid/net/nsd/NsdManager;->mServiceMap:Landroid/util/SparseArray; -Landroid/net/nsd/NsdManager;->nameOf(I)Ljava/lang/String; -Landroid/net/nsd/NsdManager;->NATIVE_DAEMON_EVENT:I -Landroid/net/nsd/NsdManager;->nextListenerKey()I -Landroid/net/nsd/NsdManager;->putListener(Ljava/lang/Object;Landroid/net/nsd/NsdServiceInfo;)I -Landroid/net/nsd/NsdManager;->REGISTER_SERVICE:I -Landroid/net/nsd/NsdManager;->REGISTER_SERVICE_FAILED:I -Landroid/net/nsd/NsdManager;->REGISTER_SERVICE_SUCCEEDED:I -Landroid/net/nsd/NsdManager;->removeListener(I)V -Landroid/net/nsd/NsdManager;->RESOLVE_SERVICE:I -Landroid/net/nsd/NsdManager;->RESOLVE_SERVICE_FAILED:I -Landroid/net/nsd/NsdManager;->RESOLVE_SERVICE_SUCCEEDED:I -Landroid/net/nsd/NsdManager;->SERVICE_FOUND:I -Landroid/net/nsd/NsdManager;->SERVICE_LOST:I -Landroid/net/nsd/NsdManager;->setEnabled(Z)V -Landroid/net/nsd/NsdManager;->STOP_DISCOVERY:I -Landroid/net/nsd/NsdManager;->STOP_DISCOVERY_FAILED:I -Landroid/net/nsd/NsdManager;->STOP_DISCOVERY_SUCCEEDED:I -Landroid/net/nsd/NsdManager;->TAG:Ljava/lang/String; -Landroid/net/nsd/NsdManager;->UNREGISTER_SERVICE:I -Landroid/net/nsd/NsdManager;->UNREGISTER_SERVICE_FAILED:I -Landroid/net/nsd/NsdManager;->UNREGISTER_SERVICE_SUCCEEDED:I -Landroid/net/nsd/NsdServiceInfo;-><init>(Ljava/lang/String;Ljava/lang/String;)V -Landroid/net/nsd/NsdServiceInfo;->getTxtRecord()[B -Landroid/net/nsd/NsdServiceInfo;->getTxtRecordSize()I -Landroid/net/nsd/NsdServiceInfo;->mHost:Ljava/net/InetAddress; -Landroid/net/nsd/NsdServiceInfo;->mPort:I -Landroid/net/nsd/NsdServiceInfo;->mServiceName:Ljava/lang/String; -Landroid/net/nsd/NsdServiceInfo;->mServiceType:Ljava/lang/String; -Landroid/net/nsd/NsdServiceInfo;->mTxtRecord:Landroid/util/ArrayMap; -Landroid/net/nsd/NsdServiceInfo;->setTxtRecords(Ljava/lang/String;)V -Landroid/net/nsd/NsdServiceInfo;->TAG:Ljava/lang/String; Landroid/net/PacProxySelector;-><init>()V Landroid/net/PacProxySelector;->mDefaultList:Ljava/util/List; Landroid/net/PacProxySelector;->mProxyService:Lcom/android/net/IProxyService; diff --git a/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt index 79d2521b892c..20d7cc01fe1d 100644 --- a/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt +++ b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt @@ -21,7 +21,6 @@ Landroid/Manifest$permission;->CAPTURE_VIDEO_OUTPUT:Ljava/lang/String; Landroid/Manifest$permission;->READ_FRAME_BUFFER:Ljava/lang/String; Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController; Landroid/net/INetworkPolicyListener$Stub;-><init>()V -Landroid/net/nsd/INsdManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/nsd/INsdManager; Landroid/net/sip/ISipSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/sip/ISipSession; Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_enable:I Landroid/os/IPowerManager$Stub;->TRANSACTION_acquireWakeLock:I diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp index a1575173ded6..6a685a79cc33 100644 --- a/cmds/app_process/Android.bp +++ b/cmds/app_process/Android.bp @@ -64,6 +64,8 @@ cc_binary { "libwilhelm", ], + header_libs: ["bionic_libc_platform_headers"], + compile_multilib: "both", cflags: [ diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 12083b6fe20b..815f9455471c 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -15,6 +15,7 @@ #include <android-base/macros.h> #include <binder/IPCThreadState.h> +#include <bionic/pac.h> #include <hwbinder/IPCThreadState.h> #include <utils/Log.h> #include <cutils/memory.h> @@ -182,6 +183,10 @@ int main(int argc, char* const argv[]) ALOGV("app_process main with argv: %s", argv_String.string()); } + // Because of applications that are using PAC instructions incorrectly, PAC + // is disabled in application processes for now. + ScopedDisablePAC x; + AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); // Process command line arguments // ignore argv[0] diff --git a/core/api/current.txt b/core/api/current.txt index 92339416ebbb..add4158230db 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -7185,6 +7185,7 @@ package android.app.admin { method @Nullable public java.util.List<java.lang.String> getPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName); method @Nullable public java.util.List<java.lang.String> getPermittedInputMethods(@NonNull android.content.ComponentName); method public int getPersonalAppsSuspendedReasons(@NonNull android.content.ComponentName); + method @NonNull public android.app.admin.PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig(); method public int getRequiredPasswordComplexity(); method public long getRequiredStrongAuthTimeout(@Nullable android.content.ComponentName); method public boolean getScreenCaptureDisabled(@Nullable android.content.ComponentName); @@ -7327,6 +7328,7 @@ package android.app.admin { method public boolean setPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName, @Nullable java.util.List<java.lang.String>); method public boolean setPermittedInputMethods(@NonNull android.content.ComponentName, java.util.List<java.lang.String>); method public void setPersonalAppsSuspended(@NonNull android.content.ComponentName, boolean); + method public void setPreferentialNetworkServiceConfig(@NonNull android.app.admin.PreferentialNetworkServiceConfig); method public void setPreferentialNetworkServiceEnabled(boolean); method public void setProfileEnabled(@NonNull android.content.ComponentName); method public void setProfileName(@NonNull android.content.ComponentName, String); @@ -7572,6 +7574,32 @@ package android.app.admin { field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.NetworkEvent> CREATOR; } + public final class PreferentialNetworkServiceConfig implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public int[] getExcludedUids(); + method @NonNull public int[] getIncludedUids(); + method public int getNetworkId(); + method public boolean isEnabled(); + method public boolean isFallbackToDefaultConnectionAllowed(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.PreferentialNetworkServiceConfig> CREATOR; + field public static final int PREFERENTIAL_NETWORK_ID_1 = 1; // 0x1 + field public static final int PREFERENTIAL_NETWORK_ID_2 = 2; // 0x2 + field public static final int PREFERENTIAL_NETWORK_ID_3 = 3; // 0x3 + field public static final int PREFERENTIAL_NETWORK_ID_4 = 4; // 0x4 + field public static final int PREFERENTIAL_NETWORK_ID_5 = 5; // 0x5 + } + + public static final class PreferentialNetworkServiceConfig.Builder { + ctor public PreferentialNetworkServiceConfig.Builder(); + method @NonNull public android.app.admin.PreferentialNetworkServiceConfig build(); + method @NonNull public android.app.admin.PreferentialNetworkServiceConfig.Builder setEnabled(boolean); + method @NonNull public android.app.admin.PreferentialNetworkServiceConfig.Builder setExcludedUids(@NonNull int[]); + method @NonNull public android.app.admin.PreferentialNetworkServiceConfig.Builder setFallbackToDefaultConnectionAllowed(boolean); + method @NonNull public android.app.admin.PreferentialNetworkServiceConfig.Builder setIncludedUids(@NonNull int[]); + method @NonNull public android.app.admin.PreferentialNetworkServiceConfig.Builder setNetworkId(int); + } + public class SecurityLog { ctor public SecurityLog(); field public static final int LEVEL_ERROR = 3; // 0x3 @@ -8642,6 +8670,7 @@ package android.bluetooth { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices(); method @Deprecated public static android.bluetooth.BluetoothAdapter getDefaultAdapter(); method public int getLeMaximumAdvertisingDataLength(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getMaxConnectedAudioDevices(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getProfileConnectionState(int); method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int); @@ -8652,11 +8681,12 @@ package android.bluetooth { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean isDiscovering(); method public boolean isEnabled(); method public boolean isLe2MPhySupported(); + method public int isLeAudioBroadcastAssistantSupported(); + method public int isLeAudioBroadcastSourceSupported(); method public int isLeAudioSupported(); method public boolean isLeCodedPhySupported(); method public boolean isLeExtendedAdvertisingSupported(); method public boolean isLePeriodicAdvertisingSupported(); - method public int isLePeriodicAdvertisingSyncTransferSenderSupported(); method public boolean isMultipleAdvertisementSupported(); method public boolean isOffloadedFilteringSupported(); method public boolean isOffloadedScanBatchingSupported(); @@ -9061,6 +9091,7 @@ package android.bluetooth { field public static final int SOURCE_CODEC_TYPE_APTX = 2; // 0x2 field public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; // 0x3 field public static final int SOURCE_CODEC_TYPE_INVALID = 1000000; // 0xf4240 + field public static final int SOURCE_CODEC_TYPE_LC3 = 5; // 0x5 field public static final int SOURCE_CODEC_TYPE_LDAC = 4; // 0x4 field public static final int SOURCE_CODEC_TYPE_SBC = 0; // 0x0 } @@ -9153,6 +9184,7 @@ package android.bluetooth { field public static final String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT"; field public static final String EXTRA_PREVIOUS_BOND_STATE = "android.bluetooth.device.extra.PREVIOUS_BOND_STATE"; field public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"; + field public static final String EXTRA_TRANSPORT = "android.bluetooth.device.extra.TRANSPORT"; field public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; field public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; // 0x2 field public static final int PAIRING_VARIANT_PIN = 0; // 0x0 @@ -9515,24 +9547,61 @@ package android.bluetooth { method public void close(); method protected void finalize(); method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothDevice getConnectedGroupLeadDevice(int); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getGroupId(@NonNull android.bluetooth.BluetoothDevice); field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED"; } - public final class BluetoothLeAudioCodecConfig { + public final class BluetoothLeAudioCodecConfig implements android.os.Parcelable { + method public int describeContents(); + method public int getBitsPerSample(); + method public int getChannelMode(); method @NonNull public String getCodecName(); + method public int getCodecPriority(); method public int getCodecType(); + method public int getFrameDuration(); method public static int getMaxCodecType(); + method public int getOctetsPerFrame(); + method public int getSampleRate(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int BITS_PER_SAMPLE_16 = 1; // 0x1 + field public static final int BITS_PER_SAMPLE_24 = 2; // 0x2 + field public static final int BITS_PER_SAMPLE_32 = 3; // 0x3 + field public static final int BITS_PER_SAMPLE_NONE = 0; // 0x0 + field public static final int CHANNEL_MODE_MONO = 1; // 0x1 + field public static final int CHANNEL_MODE_NONE = 0; // 0x0 + field public static final int CHANNEL_MODE_STEREO = 2; // 0x2 + field public static final int CODEC_PRIORITY_DEFAULT = 0; // 0x0 + field public static final int CODEC_PRIORITY_DISABLED = -1; // 0xffffffff + field public static final int CODEC_PRIORITY_HIGHEST = 1000000; // 0xf4240 + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothLeAudioCodecConfig> CREATOR; + field public static final int FRAME_DURATION_10000 = 2; // 0x2 + field public static final int FRAME_DURATION_7500 = 1; // 0x1 + field public static final int FRAME_DURATION_NONE = 0; // 0x0 + field public static final int SAMPLE_RATE_16000 = 2; // 0x2 + field public static final int SAMPLE_RATE_24000 = 3; // 0x3 + field public static final int SAMPLE_RATE_32000 = 4; // 0x4 + field public static final int SAMPLE_RATE_44100 = 5; // 0x5 + field public static final int SAMPLE_RATE_48000 = 6; // 0x6 + field public static final int SAMPLE_RATE_8000 = 1; // 0x1 + field public static final int SAMPLE_RATE_NONE = 0; // 0x0 field public static final int SOURCE_CODEC_TYPE_INVALID = 1000000; // 0xf4240 field public static final int SOURCE_CODEC_TYPE_LC3 = 0; // 0x0 } public static final class BluetoothLeAudioCodecConfig.Builder { ctor public BluetoothLeAudioCodecConfig.Builder(); + ctor public BluetoothLeAudioCodecConfig.Builder(@NonNull android.bluetooth.BluetoothLeAudioCodecConfig); method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig build(); + method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setBitsPerSample(int); + method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setChannelMode(int); + method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setCodecPriority(int); method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setCodecType(int); + method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setFrameDuration(int); + method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setOctetsPerFrame(int); + method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setSampleRate(int); } public final class BluetoothManager { @@ -9553,6 +9622,7 @@ package android.bluetooth { field public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE"; field public static final int GATT = 7; // 0x7 field public static final int GATT_SERVER = 8; // 0x8 + field public static final int HAP_CLIENT = 28; // 0x1c field public static final int HEADSET = 1; // 0x1 field @Deprecated public static final int HEALTH = 3; // 0x3 field public static final int HEARING_AID = 21; // 0x15 @@ -17980,10 +18050,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(); } @@ -21879,9 +21951,11 @@ package android.media { field public static final int COLOR_Format24bitBGR888 = 12; // 0xc field @Deprecated public static final int COLOR_Format24bitRGB888 = 11; // 0xb field @Deprecated public static final int COLOR_Format25bitARGB1888 = 14; // 0xe + field public static final int COLOR_Format32bitABGR2101010 = 2130750114; // 0x7f00aaa2 field public static final int COLOR_Format32bitABGR8888 = 2130747392; // 0x7f00a000 field @Deprecated public static final int COLOR_Format32bitARGB8888 = 16; // 0x10 field @Deprecated public static final int COLOR_Format32bitBGRA8888 = 15; // 0xf + field public static final int COLOR_Format64bitABGRFloat = 2130710294; // 0x7f000f16 field @Deprecated public static final int COLOR_Format8bitRGB332 = 2; // 0x2 field @Deprecated public static final int COLOR_FormatCbYCrY = 27; // 0x1b field @Deprecated public static final int COLOR_FormatCrYCbY = 28; // 0x1c @@ -21914,10 +21988,12 @@ package android.media { field @Deprecated public static final int COLOR_FormatYUV422SemiPlanar = 24; // 0x18 field public static final int COLOR_FormatYUV444Flexible = 2135181448; // 0x7f444888 field @Deprecated public static final int COLOR_FormatYUV444Interleaved = 29; // 0x1d + field public static final int COLOR_FormatYUVP010 = 54; // 0x36 field @Deprecated public static final int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00 field @Deprecated public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100 field public static final String FEATURE_AdaptivePlayback = "adaptive-playback"; field public static final String FEATURE_DynamicTimestamp = "dynamic-timestamp"; + field public static final String FEATURE_EncodingStatistics = "encoding-statistics"; field public static final String FEATURE_FrameParsing = "frame-parsing"; field public static final String FEATURE_IntraRefresh = "intra-refresh"; field public static final String FEATURE_LowLatency = "low-latency"; @@ -22691,6 +22767,7 @@ package android.media { field public static final String KEY_OPERATING_RATE = "operating-rate"; field public static final String KEY_OUTPUT_REORDER_DEPTH = "output-reorder-depth"; field public static final String KEY_PCM_ENCODING = "pcm-encoding"; + field public static final String KEY_PICTURE_TYPE = "picture-type"; field public static final String KEY_PIXEL_ASPECT_RATIO_HEIGHT = "sar-height"; field public static final String KEY_PIXEL_ASPECT_RATIO_WIDTH = "sar-width"; field public static final String KEY_PREPEND_HEADER_TO_SYNC_FRAMES = "prepend-sps-pps-to-idr-frames"; @@ -22708,6 +22785,8 @@ package android.media { field public static final String KEY_TILE_HEIGHT = "tile-height"; field public static final String KEY_TILE_WIDTH = "tile-width"; field public static final String KEY_TRACK_ID = "track-id"; + field public static final String KEY_VIDEO_ENCODING_STATISTICS_LEVEL = "video-encoding-statistics-level"; + field public static final String KEY_VIDEO_QP_AVERAGE = "video-qp-average"; field public static final String KEY_VIDEO_QP_B_MAX = "video-qp-b-max"; field public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min"; field public static final String KEY_VIDEO_QP_I_MAX = "video-qp-i-max"; @@ -22752,12 +22831,18 @@ package android.media { field public static final String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled"; field public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8"; field public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9"; + field public static final int PICTURE_TYPE_B = 3; // 0x3 + field public static final int PICTURE_TYPE_I = 1; // 0x1 + field public static final int PICTURE_TYPE_P = 2; // 0x2 + field public static final int PICTURE_TYPE_UNKNOWN = 0; // 0x0 field public static final int TYPE_BYTE_BUFFER = 5; // 0x5 field public static final int TYPE_FLOAT = 3; // 0x3 field public static final int TYPE_INTEGER = 1; // 0x1 field public static final int TYPE_LONG = 2; // 0x2 field public static final int TYPE_NULL = 0; // 0x0 field public static final int TYPE_STRING = 4; // 0x4 + field public static final int VIDEO_ENCODING_STATISTICS_LEVEL_1 = 1; // 0x1 + field public static final int VIDEO_ENCODING_STATISTICS_LEVEL_NONE = 0; // 0x0 } public final class MediaMetadata implements android.os.Parcelable { @@ -26896,11 +26981,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); @@ -26980,65 +27067,6 @@ package android.net.http { } -package android.net.nsd { - - public final class NsdManager { - method public void discoverServices(String, int, android.net.nsd.NsdManager.DiscoveryListener); - method public void registerService(android.net.nsd.NsdServiceInfo, int, android.net.nsd.NsdManager.RegistrationListener); - method public void resolveService(android.net.nsd.NsdServiceInfo, android.net.nsd.NsdManager.ResolveListener); - method public void stopServiceDiscovery(android.net.nsd.NsdManager.DiscoveryListener); - method public void unregisterService(android.net.nsd.NsdManager.RegistrationListener); - field public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED"; - field public static final String EXTRA_NSD_STATE = "nsd_state"; - field public static final int FAILURE_ALREADY_ACTIVE = 3; // 0x3 - field public static final int FAILURE_INTERNAL_ERROR = 0; // 0x0 - field public static final int FAILURE_MAX_LIMIT = 4; // 0x4 - field public static final int NSD_STATE_DISABLED = 1; // 0x1 - field public static final int NSD_STATE_ENABLED = 2; // 0x2 - field public static final int PROTOCOL_DNS_SD = 1; // 0x1 - } - - public static interface NsdManager.DiscoveryListener { - method public void onDiscoveryStarted(String); - method public void onDiscoveryStopped(String); - method public void onServiceFound(android.net.nsd.NsdServiceInfo); - method public void onServiceLost(android.net.nsd.NsdServiceInfo); - method public void onStartDiscoveryFailed(String, int); - method public void onStopDiscoveryFailed(String, int); - } - - public static interface NsdManager.RegistrationListener { - method public void onRegistrationFailed(android.net.nsd.NsdServiceInfo, int); - method public void onServiceRegistered(android.net.nsd.NsdServiceInfo); - method public void onServiceUnregistered(android.net.nsd.NsdServiceInfo); - method public void onUnregistrationFailed(android.net.nsd.NsdServiceInfo, int); - } - - public static interface NsdManager.ResolveListener { - method public void onResolveFailed(android.net.nsd.NsdServiceInfo, int); - method public void onServiceResolved(android.net.nsd.NsdServiceInfo); - } - - public final class NsdServiceInfo implements android.os.Parcelable { - ctor public NsdServiceInfo(); - method public int describeContents(); - method public java.util.Map<java.lang.String,byte[]> getAttributes(); - method public java.net.InetAddress getHost(); - method public int getPort(); - method public String getServiceName(); - method public String getServiceType(); - method public void removeAttribute(String); - method public void setAttribute(String, String); - method public void setHost(java.net.InetAddress); - method public void setPort(int); - method public void setServiceName(String); - method public void setServiceType(String); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.nsd.NsdServiceInfo> CREATOR; - } - -} - package android.net.rtp { @Deprecated public class AudioCodec { @@ -27277,6 +27305,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 +27342,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 +27352,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 +27376,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 { @@ -31598,7 +31671,7 @@ package android.os { method @Deprecated public void readMap(@NonNull java.util.Map, @Nullable ClassLoader); method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>); method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader); - method @Nullable public <T> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>); + method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<? super T>); method @Deprecated @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader); method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>); method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader); @@ -31608,7 +31681,7 @@ package android.os { method @Nullable public android.os.PersistableBundle readPersistableBundle(); method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader); method @Deprecated @Nullable public java.io.Serializable readSerializable(); - method @Nullable public <T> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>); + method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<? super T>); method @NonNull public android.util.Size readSize(); method @NonNull public android.util.SizeF readSizeF(); method @Deprecated @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader); @@ -37503,6 +37576,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 +37632,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 +37657,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 +37696,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 +41341,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 @@ -41246,6 +41373,7 @@ package android.telephony { field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array"; field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array"; field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array"; + field public static final String KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL = "iwlan.supports_eap_aka_fast_reauth_bool"; } public abstract class CellIdentity implements android.os.Parcelable { diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index d13170bf4a35..74ec189514c5 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -54,7 +54,30 @@ package android.app { package android.app.usage { public class NetworkStatsManager { + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void forceUpdate(); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyNetworkStatus(@NonNull java.util.List<android.net.Network>, @NonNull java.util.List<android.net.NetworkStateSnapshot>, @Nullable String, @NonNull java.util.List<android.net.UnderlyingNetworkInfo>); + method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForDevice(@NonNull android.net.NetworkTemplate, long, long); + method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(@NonNull android.net.NetworkTemplate, long, long, int, int, int) throws java.lang.SecurityException; + method @NonNull @WorkerThread public android.app.usage.NetworkStats querySummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException; + method @NonNull @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(@NonNull android.net.NetworkTemplate, long, long); + method @NonNull @WorkerThread public android.app.usage.NetworkStats queryTaggedSummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException; + method public void registerUsageCallback(@NonNull android.net.NetworkTemplate, long, @NonNull java.util.concurrent.Executor, @NonNull android.app.usage.NetworkStatsManager.UsageCallback); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setDefaultGlobalAlert(long); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setPollOnOpen(boolean); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setStatsProviderWarningAndLimitAsync(@NonNull String, long, long); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setUidForeground(int, boolean); + } + + public abstract static class NetworkStatsManager.UsageCallback { + method public void onThresholdReached(@NonNull android.net.NetworkTemplate); + } + +} + +package android.bluetooth { + + public final class BluetoothPan implements android.bluetooth.BluetoothProfile { + method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.TETHER_PRIVILEGED}) public android.net.TetheringManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheredInterfaceCallback); } } @@ -71,6 +94,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 +158,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(); @@ -192,10 +224,6 @@ package android.media.session { package android.net { - public final class ConnectivityFrameworkInitializerTiramisu { - method public static void registerServiceWrappers(); - } - public final class EthernetNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { ctor public EthernetNetworkSpecifier(@NonNull String); method public int describeContents(); @@ -212,11 +240,43 @@ package android.net { method public int getResourceId(); } + public class LocalSocket implements java.io.Closeable { + ctor public LocalSocket(@NonNull java.io.FileDescriptor); + } + + public class NetworkIdentity { + method public int getOemManaged(); + method public int getRatType(); + method @Nullable public String getSubscriberId(); + method public int getType(); + method @Nullable public String getWifiNetworkKey(); + method public boolean isDefaultNetwork(); + method public boolean isMetered(); + method public boolean isRoaming(); + } + + public static final class NetworkIdentity.Builder { + ctor public NetworkIdentity.Builder(); + method @NonNull public android.net.NetworkIdentity build(); + method @NonNull public android.net.NetworkIdentity.Builder clearRatType(); + method @NonNull public android.net.NetworkIdentity.Builder setDefaultNetwork(boolean); + method @NonNull public android.net.NetworkIdentity.Builder setMetered(boolean); + method @NonNull public android.net.NetworkIdentity.Builder setNetworkStateSnapshot(@NonNull android.net.NetworkStateSnapshot); + method @NonNull public android.net.NetworkIdentity.Builder setOemManaged(int); + method @NonNull public android.net.NetworkIdentity.Builder setRatType(int); + method @NonNull public android.net.NetworkIdentity.Builder setRoaming(boolean); + method @NonNull public android.net.NetworkIdentity.Builder setSubscriberId(@Nullable String); + method @NonNull public android.net.NetworkIdentity.Builder setType(int); + method @NonNull public android.net.NetworkIdentity.Builder setWifiNetworkKey(@Nullable String); + } + public class NetworkPolicyManager { method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getMultipathPreference(@NonNull android.net.Network); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getRestrictBackgroundStatus(int); + method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public android.telephony.SubscriptionPlan getSubscriptionPlan(@NonNull android.net.NetworkTemplate); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyStatsProviderWarningOrLimitReached(); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); } @@ -237,6 +297,44 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStateSnapshot> CREATOR; } + public class NetworkStatsCollection { + method @NonNull public java.util.Map<android.net.NetworkStatsCollection.Key,android.net.NetworkStatsHistory> getEntries(); + } + + public static final class NetworkStatsCollection.Builder { + ctor public NetworkStatsCollection.Builder(long); + method @NonNull public android.net.NetworkStatsCollection.Builder addEntry(@NonNull android.net.NetworkStatsCollection.Key, @NonNull android.net.NetworkStatsHistory); + method @NonNull public android.net.NetworkStatsCollection build(); + } + + public static class NetworkStatsCollection.Key { + ctor public NetworkStatsCollection.Key(@NonNull java.util.Set<android.net.NetworkIdentity>, int, int, int); + } + + public final class NetworkStatsHistory implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<android.net.NetworkStatsHistory.Entry> getEntries(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStatsHistory> CREATOR; + } + + public static final class NetworkStatsHistory.Builder { + ctor public NetworkStatsHistory.Builder(long, int); + method @NonNull public android.net.NetworkStatsHistory.Builder addEntry(@NonNull android.net.NetworkStatsHistory.Entry); + method @NonNull public android.net.NetworkStatsHistory build(); + } + + public static final class NetworkStatsHistory.Entry { + ctor public NetworkStatsHistory.Entry(long, long, long, long, long, long, long); + method public long getActiveTime(); + method public long getBucketStart(); + method public long getOperations(); + method public long getRxBytes(); + method public long getRxPackets(); + method public long getTxBytes(); + method public long getTxPackets(); + } + public final class NetworkTemplate implements android.os.Parcelable { method public int describeContents(); method public int getDefaultNetworkStatus(); @@ -247,6 +345,7 @@ package android.net { method public int getRoaming(); method @NonNull public java.util.Set<java.lang.String> getSubscriberIds(); method @NonNull public java.util.Set<java.lang.String> getWifiNetworkKeys(); + method public boolean matches(@NonNull android.net.NetworkIdentity); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkTemplate> CREATOR; field public static final int MATCH_BLUETOOTH = 8; // 0x8 @@ -292,6 +391,11 @@ package android.net { method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo); } + public class TrafficStats { + method public static void attachSocketTagger(); + method public static void init(@NonNull android.content.Context); + } + public final class UnderlyingNetworkInfo implements android.os.Parcelable { ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>); method public int describeContents(); @@ -335,6 +439,7 @@ package android.os { } public class Process { + field public static final int NFC_UID = 1027; // 0x403 field public static final int VPN_UID = 1016; // 0x3f8 } @@ -366,6 +471,16 @@ package android.os { method @NonNull public java.util.List<android.content.ComponentName> getEnabledComponentOverrides(@NonNull String); } + public final class Trace { + method public static void asyncTraceBegin(long, @NonNull String, int); + method public static void asyncTraceEnd(long, @NonNull String, int); + method public static boolean isTagEnabled(long); + method public static void traceBegin(long, @NonNull String); + method public static void traceCounter(long, @NonNull String, int); + method public static void traceEnd(long); + field public static final long TRACE_TAG_NETWORK = 2097152L; // 0x200000L + } + } package android.os.storage { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index b4a82ffc5707..4a173fd18c8e 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"; @@ -314,6 +316,7 @@ package android { public static final class R.array { field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005 + field public static final int config_optionalIpSecAlgorithms; } public static final class R.attr { @@ -1848,6 +1851,8 @@ package android.app.usage { } public class NetworkStatsManager { + method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getMobileUidStats(); + method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getWifiUidStats(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider); } @@ -2013,6 +2018,7 @@ package android.bluetooth { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isEncrypted(); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isInSilenceMode(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond(); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setLowLatencyAudioAllowed(boolean); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setMessageAccessPermission(int); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setMetadata(int, @NonNull byte[]); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setPhonebookAccessPermission(int); @@ -2022,9 +2028,11 @@ package android.bluetooth { field public static final int ACCESS_REJECTED = 2; // 0x2 field public static final int ACCESS_UNKNOWN = 0; // 0x0 field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED"; + field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_SWITCH_BUFFER_SIZE = "android.bluetooth.device.action.SWITCH_BUFFER_SIZE"; field public static final String DEVICE_TYPE_DEFAULT = "Default"; field public static final String DEVICE_TYPE_UNTETHERED_HEADSET = "Untethered Headset"; field public static final String DEVICE_TYPE_WATCH = "Watch"; + field public static final String EXTRA_LOW_LATENCY_BUFFER_SIZE = "android.bluetooth.device.extra.LOW_LATENCY_BUFFER_SIZE"; field public static final int METADATA_COMPANION_APP = 4; // 0x4 field public static final int METADATA_DEVICE_TYPE = 17; // 0x11 field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10 @@ -2062,6 +2070,15 @@ package android.bluetooth { method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean stopScoUsingVirtualVoiceCall(); } + public final class BluetoothHeadsetClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { + method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); + method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); + field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.headsetprofile.action.CONNECTION_STATE_CHANGED"; + } + public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile { method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public long getHiSyncId(@NonNull android.bluetooth.BluetoothDevice); @@ -2080,6 +2097,10 @@ package android.bluetooth { field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; } + public final class BluetoothLeAudio implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getAudioLocation(@NonNull android.bluetooth.BluetoothDevice); + } + public final class BluetoothMap implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { method public void close(); method protected void finalize(); @@ -2089,15 +2110,21 @@ package android.bluetooth { field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; } - public final class BluetoothMapClient implements android.bluetooth.BluetoothProfile { + public final class BluetoothMapClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { + method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); + method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.SEND_SMS}) public boolean sendMessage(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.net.Uri>, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); + field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED"; } public final class BluetoothPan implements android.bluetooth.BluetoothProfile { method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isTetheringOn(); - method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.TETHER_PRIVILEGED}) public void setBluetoothTethering(boolean); + method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.TETHER_PRIVILEGED}) public void setBluetoothTethering(boolean); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED"; field public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED"; @@ -2118,6 +2145,15 @@ package android.bluetooth { field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; } + public final class BluetoothPbapClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { + method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); + method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); + field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED"; + } + public interface BluetoothProfile { field public static final int A2DP_SINK = 11; // 0xb field public static final int AVRCP_CONTROLLER = 12; // 0xc @@ -2151,6 +2187,7 @@ package android.bluetooth { field @NonNull public static final android.os.ParcelUuid COORDINATED_SET; field @NonNull public static final android.os.ParcelUuid DIP; field @NonNull public static final android.os.ParcelUuid GENERIC_MEDIA_CONTROL; + field @NonNull public static final android.os.ParcelUuid HAS; field @NonNull public static final android.os.ParcelUuid HEARING_AID; field @NonNull public static final android.os.ParcelUuid HFP; field @NonNull public static final android.os.ParcelUuid HFP_AG; @@ -5880,6 +5917,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); } @@ -7520,11 +7558,12 @@ package android.net { field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK"; } - public final class NetworkStats implements android.os.Parcelable { + public final class NetworkStats implements java.lang.Iterable<android.net.NetworkStats.Entry> android.os.Parcelable { ctor public NetworkStats(long, int); method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats); method @NonNull public android.net.NetworkStats addEntry(@NonNull android.net.NetworkStats.Entry); method public int describeContents(); + method @NonNull public java.util.Iterator<android.net.NetworkStats.Entry> iterator(); method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR; @@ -7548,6 +7587,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 { @@ -7584,6 +7634,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 @@ -8506,6 +8557,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 @@ -13675,6 +13727,7 @@ package android.telephony.ims { field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6 field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; // 0x4 field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5; // 0x5 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_INTERNET_PDN = 12; // 0xc field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9; // 0x9 field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2; // 0x2 field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3; // 0x3 diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 3b0a5f3e9bdd..6d7835f84dc7 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -97,6 +97,7 @@ import android.media.MediaFrameworkPlatformInitializer; import android.media.MediaServiceManager; import android.net.ConnectivityManager; import android.net.Proxy; +import android.net.TrafficStats; import android.net.Uri; import android.os.AsyncTask; import android.os.Binder; @@ -6663,6 +6664,13 @@ public final class ActivityThread extends ClientTransactionHandler NetworkSecurityConfigProvider.install(appContext); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + // For backward compatibility, TrafficStats needs static access to the application context. + // But for isolated apps which cannot access network related services, service discovery + // is restricted. Hence, calling this would result in NPE. + if (!Process.isIsolated()) { + TrafficStats.init(appContext); + } + // Continue loading instrumentation. if (ii != null) { initInstrumentation(ii, data, appContext); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 0fe80c45ad2a..3840f760eda8 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -10177,6 +10177,9 @@ public class DevicePolicyManager { * On fully-managed devices this method is unsupported because all traffic is considered * work traffic. * + * <p> This method enables preferential network service with a default configuration. + * To fine-tune the configuration, use {@link #setPreferentialNetworkServiceConfig) instead. + * * <p>This method can only be called by the profile owner of a managed profile. * @param enabled whether preferential network service should be enabled. * @throws SecurityException if the caller is not the profile owner. @@ -10215,6 +10218,56 @@ public class DevicePolicyManager { } /** + * Sets preferential network configuration on the work profile. + * {@see PreferentialNetworkServiceConfig} + * + * An example of a supported preferential network service is the Enterprise + * slice on 5G networks. + * + * By default, preferential network service is disabled on the work profile on supported + * carriers and devices. Admins can explicitly enable it with this API. + * On fully-managed devices this method is unsupported because all traffic is considered + * work traffic. + * + * <p>This method can only be called by the profile owner of a managed profile. + * @param preferentialNetworkServiceConfig preferential network configuration. + * @throws SecurityException if the caller is not the profile owner. + **/ + public void setPreferentialNetworkServiceConfig( + @NonNull PreferentialNetworkServiceConfig preferentialNetworkServiceConfig) { + throwIfParentInstance("setPreferentialNetworkServiceConfig"); + if (mService == null) { + return; + } + try { + mService.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfig); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get preferential network configuration + * {@see PreferentialNetworkServiceConfig} + * + * <p>This method can be called by the profile owner of a managed profile. + * + * @return preferential network configuration. + * @throws SecurityException if the caller is not the profile owner. + */ + public @NonNull PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig() { + throwIfParentInstance("getPreferentialNetworkServiceConfig"); + if (mService == null) { + return PreferentialNetworkServiceConfig.DEFAULT; + } + try { + return mService.getPreferentialNetworkServiceConfig(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * This method is mostly deprecated. * Most of the settings that still have an effect have dedicated setter methods or user * restrictions. See individual settings for details. diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index d287437b143b..c78a5a00f645 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -21,6 +21,7 @@ import android.app.admin.NetworkEvent; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.app.admin.ParcelableGranteeMap; +import android.app.admin.PreferentialNetworkServiceConfig; import android.app.admin.StartInstallingUpdateCallback; import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; @@ -278,6 +279,10 @@ interface IDevicePolicyManager { void setPreferentialNetworkServiceEnabled(in boolean enabled); boolean isPreferentialNetworkServiceEnabled(int userHandle); + void setPreferentialNetworkServiceConfig( + in PreferentialNetworkServiceConfig preferentialNetworkServiceConfig); + PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig(); + void setLockTaskPackages(in ComponentName who, in String[] packages); String[] getLockTaskPackages(in ComponentName who); boolean isLockTaskPermitted(in String pkg); diff --git a/core/java/android/app/admin/PreferentialNetworkServiceConfig.aidl b/core/java/android/app/admin/PreferentialNetworkServiceConfig.aidl new file mode 100644 index 000000000000..6b6ee7d8d538 --- /dev/null +++ b/core/java/android/app/admin/PreferentialNetworkServiceConfig.aidl @@ -0,0 +1,20 @@ +/* +** +** Copyright 2022, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.app.admin; + +parcelable PreferentialNetworkServiceConfig;
\ No newline at end of file diff --git a/core/java/android/app/admin/PreferentialNetworkServiceConfig.java b/core/java/android/app/admin/PreferentialNetworkServiceConfig.java new file mode 100644 index 000000000000..2849139c606b --- /dev/null +++ b/core/java/android/app/admin/PreferentialNetworkServiceConfig.java @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.admin; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Network configuration to be set for the user profile + * {@see DevicePolicyManager#setPreferentialNetworkServiceConfig}. + */ +public final class PreferentialNetworkServiceConfig implements Parcelable { + final boolean mIsEnabled; + final int mNetworkId; + final boolean mAllowFallbackToDefaultConnection; + final int[] mIncludedUids; + final int[] mExcludedUids; + + /** @hide */ + public static final PreferentialNetworkServiceConfig DEFAULT = + (new PreferentialNetworkServiceConfig.Builder()).build(); + + /** + * Preferential network identifier 1. + */ + public static final int PREFERENTIAL_NETWORK_ID_1 = 1; + + /** + * Preferential network identifier 2. + */ + public static final int PREFERENTIAL_NETWORK_ID_2 = 2; + + /** + * Preferential network identifier 3. + */ + public static final int PREFERENTIAL_NETWORK_ID_3 = 3; + + /** + * Preferential network identifier 4. + */ + public static final int PREFERENTIAL_NETWORK_ID_4 = 4; + + /** + * Preferential network identifier 5. + */ + public static final int PREFERENTIAL_NETWORK_ID_5 = 5; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "PREFERENTIAL_NETWORK_ID_" }, value = { + PREFERENTIAL_NETWORK_ID_1, + PREFERENTIAL_NETWORK_ID_2, + PREFERENTIAL_NETWORK_ID_3, + PREFERENTIAL_NETWORK_ID_4, + PREFERENTIAL_NETWORK_ID_5, + }) + + public @interface PreferentialNetworkPreferenceId { + } + + private PreferentialNetworkServiceConfig(boolean isEnabled, + boolean allowFallbackToDefaultConnection, int[] includedUids, + int[] excludedUids, @PreferentialNetworkPreferenceId int networkId) { + mIsEnabled = isEnabled; + mAllowFallbackToDefaultConnection = allowFallbackToDefaultConnection; + mIncludedUids = includedUids; + mExcludedUids = excludedUids; + mNetworkId = networkId; + } + + private PreferentialNetworkServiceConfig(Parcel in) { + mIsEnabled = in.readBoolean(); + mAllowFallbackToDefaultConnection = in.readBoolean(); + mNetworkId = in.readInt(); + mIncludedUids = in.createIntArray(); + mExcludedUids = in.createIntArray(); + } + + /** + * Is the preferential network enabled. + * @return true if enabled else false + */ + public boolean isEnabled() { + return mIsEnabled; + } + + /** + * is fallback to default network allowed. This boolean configures whether default connection + * (default internet or wifi) should be used or not if a preferential network service + * connection is not available. + * @return true if fallback is allowed, else false. + */ + public boolean isFallbackToDefaultConnectionAllowed() { + return mAllowFallbackToDefaultConnection; + } + + /** + * Get the array of uids that are applicable for the profile preference. + * + * {@see #getExcludedUids()} + * Included UIDs and Excluded UIDs can't both be non-empty. + * if both are empty, it means this request applies to all uids in the user profile. + * if included is not empty, then only included UIDs are applied. + * if excluded is not empty, then it is all uids in the user profile except these UIDs. + * @return Array of uids applicable for the profile preference. + * Empty array would mean that this request applies to all uids in the profile. + */ + public @NonNull int[] getIncludedUids() { + return mIncludedUids; + } + + /** + * Get the array of uids that are excluded for the profile preference. + * + * {@see #getIncludedUids()} + * Included UIDs and Excluded UIDs can't both be non-empty. + * if both are empty, it means this request applies to all uids in the user profile. + * if included is not empty, then only included UIDs are applied. + * if excluded is not empty, then it is all uids in the user profile except these UIDs. + * @return Array of uids that are excluded for the profile preference. + * Empty array would mean that this request applies to all uids in the profile. + */ + public @NonNull int[] getExcludedUids() { + return mExcludedUids; + } + + /** + * @return preference enterprise identifier. + * valid values starts from + * {@link #PREFERENTIAL_NETWORK_ID_1} to {@link #PREFERENTIAL_NETWORK_ID_5}. + * preference identifier is applicable only if preference network service is enabled + * + */ + public @PreferentialNetworkPreferenceId int getNetworkId() { + return mNetworkId; + } + + @Override + public String toString() { + return "PreferentialNetworkServiceConfig{" + + "mIsEnabled=" + isEnabled() + + "mAllowFallbackToDefaultConnection=" + isFallbackToDefaultConnectionAllowed() + + "mIncludedUids=" + mIncludedUids.toString() + + "mExcludedUids=" + mExcludedUids.toString() + + "mNetworkId=" + mNetworkId + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final PreferentialNetworkServiceConfig that = (PreferentialNetworkServiceConfig) o; + return mIsEnabled == that.mIsEnabled + && mAllowFallbackToDefaultConnection == that.mAllowFallbackToDefaultConnection + && mNetworkId == that.mNetworkId + && Objects.equals(mIncludedUids, that.mIncludedUids) + && Objects.equals(mExcludedUids, that.mExcludedUids); + } + + @Override + public int hashCode() { + return ((Objects.hashCode(mIsEnabled) * 17) + + (Objects.hashCode(mAllowFallbackToDefaultConnection) * 19) + + (Objects.hashCode(mIncludedUids) * 23) + + (Objects.hashCode(mExcludedUids) * 29) + + mNetworkId * 31); + } + + /** + * Builder used to create {@link PreferentialNetworkServiceConfig} objects. + * Specify the preferred Network preference + */ + public static final class Builder { + boolean mIsEnabled = false; + int mNetworkId = 0; + boolean mAllowFallbackToDefaultConnection = true; + int[] mIncludedUids = new int[0]; + int[] mExcludedUids = new int[0]; + + /** + * Constructs an empty Builder with preferential network disabled by default. + */ + public Builder() {} + + /** + * Set the preferential network service enabled state. + * Default value is false. + * @param isEnabled the desired network preference to use, true to enable else false + * @return The builder to facilitate chaining. + */ + @NonNull + public PreferentialNetworkServiceConfig.Builder setEnabled(boolean isEnabled) { + mIsEnabled = isEnabled; + return this; + } + + /** + * Set whether the default connection should be used as fallback. + * This boolean configures whether the default connection (default internet or wifi) + * should be used if a preferential network service connection is not available. + * Default value is true + * @param allowFallbackToDefaultConnection true if fallback is allowed else false + * @return The builder to facilitate chaining. + */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public PreferentialNetworkServiceConfig.Builder setFallbackToDefaultConnectionAllowed( + boolean allowFallbackToDefaultConnection) { + mAllowFallbackToDefaultConnection = allowFallbackToDefaultConnection; + return this; + } + + /** + * Set the array of uids whose network access will go through this preferential + * network service. + * {@see #setExcludedUids(int[])} + * Included UIDs and Excluded UIDs can't both be non-empty. + * if both are empty, it means this request applies to all uids in the user profile. + * if included is not empty, then only included UIDs are applied. + * if excluded is not empty, then it is all uids in the user profile except these UIDs. + * @param uids array of included uids + * @return The builder to facilitate chaining. + */ + @NonNull + public PreferentialNetworkServiceConfig.Builder setIncludedUids( + @NonNull int[] uids) { + Objects.requireNonNull(uids); + mIncludedUids = uids; + return this; + } + + /** + * Set the array of uids who are not allowed through this preferential + * network service. + * {@see #setIncludedUids(int[])} + * Included UIDs and Excluded UIDs can't both be non-empty. + * if both are empty, it means this request applies to all uids in the user profile. + * if included is not empty, then only included UIDs are applied. + * if excluded is not empty, then it is all uids in the user profile except these UIDs. + * @param uids array of excluded uids + * @return The builder to facilitate chaining. + */ + @NonNull + public PreferentialNetworkServiceConfig.Builder setExcludedUids( + @NonNull int[] uids) { + Objects.requireNonNull(uids); + mExcludedUids = uids; + return this; + } + + /** + * Returns an instance of {@link PreferentialNetworkServiceConfig} created from the + * fields set on this builder. + */ + @NonNull + public PreferentialNetworkServiceConfig build() { + if (mIncludedUids.length > 0 && mExcludedUids.length > 0) { + throw new IllegalStateException("Both includedUids and excludedUids " + + "cannot be nonempty"); + } + return new PreferentialNetworkServiceConfig(mIsEnabled, + mAllowFallbackToDefaultConnection, mIncludedUids, mExcludedUids, mNetworkId); + } + + /** + * Set the preferential network identifier. + * Valid values starts from {@link #PREFERENTIAL_NETWORK_ID_1} to + * {@link #PREFERENTIAL_NETWORK_ID_5}. + * preference identifier is applicable only if preferential network service is enabled. + * @param preferenceId preference Id + * @return The builder to facilitate chaining. + */ + @NonNull + public PreferentialNetworkServiceConfig.Builder setNetworkId( + @PreferentialNetworkPreferenceId int preferenceId) { + if ((preferenceId < PREFERENTIAL_NETWORK_ID_1) + || (preferenceId > PREFERENTIAL_NETWORK_ID_5)) { + throw new IllegalArgumentException("Invalid preference identifier"); + } + mNetworkId = preferenceId; + return this; + } + } + + @Override + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + dest.writeBoolean(mIsEnabled); + dest.writeBoolean(mAllowFallbackToDefaultConnection); + dest.writeInt(mNetworkId); + dest.writeIntArray(mIncludedUids); + dest.writeIntArray(mExcludedUids); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator<PreferentialNetworkServiceConfig> CREATOR = + new Creator<PreferentialNetworkServiceConfig>() { + @Override + public PreferentialNetworkServiceConfig[] newArray(int size) { + return new PreferentialNetworkServiceConfig[size]; + } + + @Override + public PreferentialNetworkServiceConfig createFromParcel( + @NonNull android.os.Parcel in) { + return new PreferentialNetworkServiceConfig(in); + } + }; +} diff --git a/core/java/android/app/trust/OWNERS b/core/java/android/app/trust/OWNERS new file mode 100644 index 000000000000..e2c6ce15b51e --- /dev/null +++ b/core/java/android/app/trust/OWNERS @@ -0,0 +1 @@ +include /core/java/android/service/trust/OWNERS diff --git a/core/java/android/app/wallpapereffectsgeneration/OWNERS b/core/java/android/app/wallpapereffectsgeneration/OWNERS new file mode 100644 index 000000000000..2bc01541a939 --- /dev/null +++ b/core/java/android/app/wallpapereffectsgeneration/OWNERS @@ -0,0 +1,5 @@ +susharon@google.com +shanh@google.com +huiwu@google.com +srazdan@google.com + diff --git a/core/java/android/bluetooth/Attributable.java b/core/java/android/bluetooth/Attributable.java deleted file mode 100644 index d9acbe3eefb9..000000000000 --- a/core/java/android/bluetooth/Attributable.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.bluetooth; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.AttributionSource; - -import java.util.List; - -/** - * Marker interface for a class which can have an {@link AttributionSource} - * assigned to it; these are typically {@link android.os.Parcelable} classes - * which need to be updated after crossing Binder transaction boundaries. - * - * @hide - */ -public interface Attributable { - void setAttributionSource(@NonNull AttributionSource attributionSource); - - static @Nullable <T extends Attributable> T setAttributionSource( - @Nullable T attributable, - @NonNull AttributionSource attributionSource) { - if (attributable != null) { - attributable.setAttributionSource(attributionSource); - } - return attributable; - } - - static @Nullable <T extends Attributable> List<T> setAttributionSource( - @Nullable List<T> attributableList, - @NonNull AttributionSource attributionSource) { - if (attributableList != null) { - final int size = attributableList.size(); - for (int i = 0; i < size; i++) { - setAttributionSource(attributableList.get(i), attributionSource); - } - } - return attributableList; - } -} diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java deleted file mode 100644 index 8b9cec17a196..000000000000 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ /dev/null @@ -1,1149 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresNoPermission; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SystemApi; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.Context; -import android.os.Build; -import android.os.IBinder; -import android.os.ParcelUuid; -import android.os.RemoteException; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - - -/** - * This class provides the public APIs to control the Bluetooth A2DP - * profile. - * - * <p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothA2dp proxy object. - * - * <p> Android only supports one connected Bluetooth A2dp device at a time. - * Each method is protected with its appropriate permission. - */ -public final class BluetoothA2dp implements BluetoothProfile { - private static final String TAG = "BluetoothA2dp"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** - * Intent used to broadcast the change in connection state of the A2DP - * profile. - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED"; - - /** - * Intent used to broadcast the change in the Playing state of the A2DP - * profile. - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING}, - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PLAYING_STATE_CHANGED = - "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED"; - - /** @hide */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED = - "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED"; - - /** - * Intent used to broadcast the selection of a connected device as active. - * - * <p>This intent will have one extra: - * <ul> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can - * be null if no device is active. </li> - * </ul> - * - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(trackingBug = 171933273) - public static final String ACTION_ACTIVE_DEVICE_CHANGED = - "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; - - /** - * Intent used to broadcast the change in the Audio Codec state of the - * A2DP Source profile. - * - * <p>This intent will have 2 extras: - * <ul> - * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently - * connected, otherwise it is not included.</li> - * </ul> - * - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(trackingBug = 181103983) - public static final String ACTION_CODEC_CONFIG_CHANGED = - "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"; - - /** - * A2DP sink device is streaming music. This state can be one of - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of - * {@link #ACTION_PLAYING_STATE_CHANGED} intent. - */ - public static final int STATE_PLAYING = 10; - - /** - * A2DP sink device is NOT streaming music. This state can be one of - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of - * {@link #ACTION_PLAYING_STATE_CHANGED} intent. - */ - public static final int STATE_NOT_PLAYING = 11; - - /** @hide */ - @IntDef(prefix = "OPTIONAL_CODECS_", value = { - OPTIONAL_CODECS_SUPPORT_UNKNOWN, - OPTIONAL_CODECS_NOT_SUPPORTED, - OPTIONAL_CODECS_SUPPORTED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface OptionalCodecsSupportStatus {} - - /** - * We don't have a stored preference for whether or not the given A2DP sink device supports - * optional codecs. - * - * @hide - */ - @SystemApi - public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1; - - /** - * The given A2DP sink device does not support optional codecs. - * - * @hide - */ - @SystemApi - public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; - - /** - * The given A2DP sink device does support optional codecs. - * - * @hide - */ - @SystemApi - public static final int OPTIONAL_CODECS_SUPPORTED = 1; - - /** @hide */ - @IntDef(prefix = "OPTIONAL_CODECS_PREF_", value = { - OPTIONAL_CODECS_PREF_UNKNOWN, - OPTIONAL_CODECS_PREF_DISABLED, - OPTIONAL_CODECS_PREF_ENABLED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface OptionalCodecsPreferenceStatus {} - - /** - * We don't have a stored preference for whether optional codecs should be enabled or - * disabled for the given A2DP device. - * - * @hide - */ - @SystemApi - public static final int OPTIONAL_CODECS_PREF_UNKNOWN = -1; - - /** - * Optional codecs should be disabled for the given A2DP device. - * - * @hide - */ - @SystemApi - public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; - - /** - * Optional codecs should be enabled for the given A2DP device. - * - * @hide - */ - @SystemApi - public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; - - /** @hide */ - @IntDef(prefix = "DYNAMIC_BUFFER_SUPPORT_", value = { - DYNAMIC_BUFFER_SUPPORT_NONE, - DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD, - DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Type {} - - /** - * Indicates the supported type of Dynamic Audio Buffer is not supported. - * - * @hide - */ - @SystemApi - public static final int DYNAMIC_BUFFER_SUPPORT_NONE = 0; - - /** - * Indicates the supported type of Dynamic Audio Buffer is A2DP offload. - * - * @hide - */ - @SystemApi - public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; - - /** - * Indicates the supported type of Dynamic Audio Buffer is A2DP software encoding. - * - * @hide - */ - @SystemApi - public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp", - IBluetoothA2dp.class.getName()) { - @Override - public IBluetoothA2dp getServiceInterface(IBinder service) { - return IBluetoothA2dp.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothA2dp proxy object for interacting with the local - * Bluetooth A2DP service. - */ - /* package */ BluetoothA2dp(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - } - - @UnsupportedAppUsage - /*package*/ void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothA2dp getService() { - return mProfileConnector.getService(); - } - - @Override - public void finalize() { - // The empty finalize needs to be kept or the - // cts signature tests would fail. - } - - /** - * Initiate connection to a profile of the remote Bluetooth device. - * - * <p> This API returns false in scenarios like the profile on the - * device is already connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that - * connection state intent for the profile will be broadcasted with - * the state. Users can get the connection state of the profile - * from this intent. - * - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @UnsupportedAppUsage - public boolean connect(BluetoothDevice device) { - if (DBG) log("connect(" + device + ")"); - final IBluetoothA2dp service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connectWithAttribution(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate disconnection from a profile - * - * <p> This API will return false in scenarios like the profile on the - * Bluetooth device is not in connected state etc. When this API returns, - * true, it is guaranteed that the connection state change - * intent will be broadcasted with the state. Users can get the - * disconnection state of the profile from this intent. - * - * <p> If the disconnection is initiated by a remote device, the state - * will transition from {@link #STATE_CONNECTED} to - * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the - * host (local) device the state will transition from - * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to - * state {@link #STATE_DISCONNECTED}. The transition to - * {@link #STATE_DISCONNECTING} can be used to distinguish between the - * two scenarios. - * - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @UnsupportedAppUsage - public boolean disconnect(BluetoothDevice device) { - if (DBG) log("disconnect(" + device + ")"); - final IBluetoothA2dp service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnectWithAttribution(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getConnectedDevices() { - if (VDBG) log("getConnectedDevices()"); - final IBluetoothA2dp service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevicesWithAttribution(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothA2dp service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStatesWithAttribution(states, - mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @BtProfileState int getConnectionState(BluetoothDevice device) { - if (VDBG) log("getState(" + device + ")"); - final IBluetoothA2dp service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionStateWithAttribution(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Select a connected device as active. - * - * The active device selection is per profile. An active device's - * purpose is profile-specific. For example, A2DP audio streaming - * is to the active A2DP Sink device. If a remote device is not - * connected, it cannot be selected as active. - * - * <p> This API returns false in scenarios like the profile on the - * device is not connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that the - * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted - * with the active device. - * - * @param device the remote Bluetooth device. Could be null to clear - * the active device and stop streaming audio to a Bluetooth device. - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @UnsupportedAppUsage(trackingBug = 171933273) - public boolean setActiveDevice(@Nullable BluetoothDevice device) { - if (DBG) log("setActiveDevice(" + device + ")"); - final IBluetoothA2dp service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && ((device == null) || isValidDevice(device))) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setActiveDevice(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the connected device that is active. - * - * @return the connected device that is active or null if no device - * is active - * @hide - */ - @UnsupportedAppUsage(trackingBug = 171933273) - @Nullable - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothDevice getActiveDevice() { - if (VDBG) log("getActiveDevice()"); - final IBluetoothA2dp service = getService(); - final BluetoothDevice defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<BluetoothDevice> recv = - new SynchronousResultReceiver(); - service.getActiveDevice(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF} - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothA2dp service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the priority of the profile. - * - * <p> The priority can be any of: - * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - final IBluetoothA2dp service = getService(); - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Checks if Avrcp device supports the absolute volume feature. - * - * @return true if device supports absolute volume - * @hide - */ - @RequiresNoPermission - public boolean isAvrcpAbsoluteVolumeSupported() { - if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported"); - final IBluetoothA2dp service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isAvrcpAbsoluteVolumeSupported(recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Tells remote device to set an absolute volume. Only if absolute volume is supported - * - * @param volume Absolute volume to be set on AVRCP side - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void setAvrcpAbsoluteVolume(int volume) { - if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume"); - final IBluetoothA2dp service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - service.setAvrcpAbsoluteVolume(volume, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Check if A2DP profile is streaming music. - * - * @param device BluetoothDevice device - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isA2dpPlaying(BluetoothDevice device) { - if (DBG) log("isA2dpPlaying(" + device + ")"); - final IBluetoothA2dp service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isA2dpPlaying(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * This function checks if the remote device is an AVCRP - * target and thus whether we should send volume keys - * changes or not. - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean shouldSendVolumeKeys(BluetoothDevice device) { - if (isEnabled() && isValidDevice(device)) { - ParcelUuid[] uuids = device.getUuids(); - if (uuids == null) return false; - - for (ParcelUuid uuid : uuids) { - if (uuid.equals(BluetoothUuid.AVRCP_TARGET)) { - return true; - } - } - } - return false; - } - - /** - * Gets the current codec status (configuration and capability). - * - * @param device the remote Bluetooth device. - * @return the current codec status - * @hide - */ - @UnsupportedAppUsage(trackingBug = 181103983) - @Nullable - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { - if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); - verifyDeviceNotNull(device, "getCodecStatus"); - final IBluetoothA2dp service = getService(); - final BluetoothCodecStatus defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<BluetoothCodecStatus> recv = - new SynchronousResultReceiver(); - service.getCodecStatus(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Sets the codec configuration preference. - * - * @param device the remote Bluetooth device. - * @param codecConfig the codec configuration preference - * @hide - */ - @UnsupportedAppUsage(trackingBug = 181103983) - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void setCodecConfigPreference(@NonNull BluetoothDevice device, - @NonNull BluetoothCodecConfig codecConfig) { - if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); - verifyDeviceNotNull(device, "setCodecConfigPreference"); - if (codecConfig == null) { - Log.e(TAG, "setCodecConfigPreference: Codec config can't be null"); - throw new IllegalArgumentException("codecConfig cannot be null"); - } - final IBluetoothA2dp service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - service.setCodecConfigPreference(device, codecConfig, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Enables the optional codecs. - * - * @param device the remote Bluetooth device. - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void enableOptionalCodecs(@NonNull BluetoothDevice device) { - if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")"); - verifyDeviceNotNull(device, "enableOptionalCodecs"); - enableDisableOptionalCodecs(device, true); - } - - /** - * Disables the optional codecs. - * - * @param device the remote Bluetooth device. - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void disableOptionalCodecs(@NonNull BluetoothDevice device) { - if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")"); - verifyDeviceNotNull(device, "disableOptionalCodecs"); - enableDisableOptionalCodecs(device, false); - } - - /** - * Enables or disables the optional codecs. - * - * @param device the remote Bluetooth device. - * @param enable if true, enable the optional codecs, other disable them - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) { - final IBluetoothA2dp service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - if (enable) { - service.enableOptionalCodecs(device, mAttributionSource); - } else { - service.disableOptionalCodecs(device, mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Returns whether this device supports optional codecs. - * - * @param device The device to check - * @return one of OPTIONAL_CODECS_SUPPORT_UNKNOWN, OPTIONAL_CODECS_NOT_SUPPORTED, or - * OPTIONAL_CODECS_SUPPORTED. - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @OptionalCodecsSupportStatus - public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) { - if (DBG) log("isOptionalCodecsSupported(" + device + ")"); - verifyDeviceNotNull(device, "isOptionalCodecsSupported"); - final IBluetoothA2dp service = getService(); - final int defaultValue = OPTIONAL_CODECS_SUPPORT_UNKNOWN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.supportsOptionalCodecs(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Returns whether this device should have optional codecs enabled. - * - * @param device The device in question. - * @return one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or - * OPTIONAL_CODECS_PREF_DISABLED. - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @OptionalCodecsPreferenceStatus - public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) { - if (DBG) log("isOptionalCodecsEnabled(" + device + ")"); - verifyDeviceNotNull(device, "isOptionalCodecsEnabled"); - final IBluetoothA2dp service = getService(); - final int defaultValue = OPTIONAL_CODECS_PREF_UNKNOWN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getOptionalCodecsEnabled(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Sets a persistent preference for whether a given device should have optional codecs enabled. - * - * @param device The device to set this preference for. - * @param value Whether the optional codecs should be enabled for this device. This should be - * one of OPTIONAL_CODECS_PREF_UNKNOWN, OPTIONAL_CODECS_PREF_ENABLED, or - * OPTIONAL_CODECS_PREF_DISABLED. - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device, - @OptionalCodecsPreferenceStatus int value) { - if (DBG) log("setOptionalCodecsEnabled(" + device + ")"); - verifyDeviceNotNull(device, "setOptionalCodecsEnabled"); - if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN - && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED - && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { - Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value); - return; - } - final IBluetoothA2dp service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - service.setOptionalCodecsEnabled(device, value, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Get the supported type of the Dynamic Audio Buffer. - * <p>Possible return values are - * {@link #DYNAMIC_BUFFER_SUPPORT_NONE}, - * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD}, - * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING}. - * - * @return supported type of Dynamic Audio Buffer feature - * - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @Type int getDynamicBufferSupport() { - if (VDBG) log("getDynamicBufferSupport()"); - final IBluetoothA2dp service = getService(); - final int defaultValue = DYNAMIC_BUFFER_SUPPORT_NONE; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getDynamicBufferSupport(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Return the record of {@link BufferConstraints} object that - * has the default/maximum/minimum audio buffer. This can be used to inform what the controller - * has support for the audio buffer. - * - * @return a record with {@link BufferConstraints} or null if report is unavailable - * or unsupported - * - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @Nullable BufferConstraints getBufferConstraints() { - if (VDBG) log("getBufferConstraints()"); - final IBluetoothA2dp service = getService(); - final BufferConstraints defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<BufferConstraints> recv = - new SynchronousResultReceiver(); - service.getBufferConstraints(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set Dynamic Audio Buffer Size. - * - * @param codec audio codec - * @param value buffer millis - * @return true to indicate success, or false on immediate error - * - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec, - int value) { - if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")"); - if (value < 0) { - Log.e(TAG, "Trying to set audio buffer length to a negative value: " + value); - return false; - } - final IBluetoothA2dp service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setBufferLengthMillis(codec, value, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Helper for converting a state to a string. - * - * For debug use only - strings are not internationalized. - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public static String stateToString(int state) { - switch (state) { - case STATE_DISCONNECTED: - return "disconnected"; - case STATE_CONNECTING: - return "connecting"; - case STATE_CONNECTED: - return "connected"; - case STATE_DISCONNECTING: - return "disconnecting"; - case STATE_PLAYING: - return "playing"; - case STATE_NOT_PLAYING: - return "not playing"; - default: - return "<unknown state " + state + ">"; - } - } - - private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; - } - - private void verifyDeviceNotNull(BluetoothDevice device, String methodName) { - if (device == null) { - Log.e(TAG, methodName + ": device param is null"); - throw new IllegalArgumentException("Device cannot be null"); - } - } - - private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; - - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java deleted file mode 100755 index 59416818ceb3..000000000000 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.Context; -import android.os.Build; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This class provides the public APIs to control the Bluetooth A2DP Sink - * profile. - * - * <p>BluetoothA2dpSink is a proxy object for controlling the Bluetooth A2DP Sink - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothA2dpSink proxy object. - * - * @hide - */ -@SystemApi -public final class BluetoothA2dpSink implements BluetoothProfile { - private static final String TAG = "BluetoothA2dpSink"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** - * Intent used to broadcast the change in connection state of the A2DP Sink - * profile. - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - * @hide - */ - @SystemApi - @SuppressLint("ActionValue") - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothA2dpSink> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK, - "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) { - @Override - public IBluetoothA2dpSink getServiceInterface(IBinder service) { - return IBluetoothA2dpSink.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothA2dp proxy object for interacting with the local - * Bluetooth A2DP service. - */ - /* package */ BluetoothA2dpSink(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - } - - /*package*/ void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothA2dpSink getService() { - return mProfileConnector.getService(); - } - - @Override - public void finalize() { - close(); - } - - /** - * Initiate connection to a profile of the remote bluetooth device. - * - * <p> Currently, the system supports only 1 connection to the - * A2DP profile. The API will automatically disconnect connected - * devices before connecting. - * - * <p> This API returns false in scenarios like the profile on the - * device is already connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that - * connection state intent for the profile will be broadcasted with - * the state. Users can get the connection state of the profile - * from this intent. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean connect(BluetoothDevice device) { - if (DBG) log("connect(" + device + ")"); - final IBluetoothA2dpSink service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate disconnection from a profile - * - * <p> This API will return false in scenarios like the profile on the - * Bluetooth device is not in connected state etc. When this API returns, - * true, it is guaranteed that the connection state change - * intent will be broadcasted with the state. Users can get the - * disconnection state of the profile from this intent. - * - * <p> If the disconnection is initiated by a remote device, the state - * will transition from {@link #STATE_CONNECTED} to - * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the - * host (local) device the state will transition from - * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to - * state {@link #STATE_DISCONNECTED}. The transition to - * {@link #STATE_DISCONNECTING} can be used to distinguish between the - * two scenarios. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnect(BluetoothDevice device) { - if (DBG) log("disconnect(" + device + ")"); - final IBluetoothA2dpSink service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - * - * @hide - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getConnectedDevices() { - if (VDBG) log("getConnectedDevices()"); - final IBluetoothA2dpSink service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - * - * @hide - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothA2dpSink service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - * - * @hide - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(BluetoothDevice device) { - if (VDBG) log("getConnectionState(" + device + ")"); - final IBluetoothA2dpSink service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the current audio configuration for the A2DP source device, - * or null if the device has no audio configuration - * - * @param device Remote bluetooth device. - * @return audio configuration for the device, or null - * - * {@see BluetoothAudioConfig} - * - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { - if (VDBG) log("getAudioConfig(" + device + ")"); - final IBluetoothA2dpSink service = getService(); - final BluetoothAudioConfig defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<BluetoothAudioConfig> recv = - new SynchronousResultReceiver(); - service.getAudioConfig(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF} - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothA2dpSink service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the priority of the profile. - * - * <p> The priority can be any of: - * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - final IBluetoothA2dpSink service = getService(); - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Check if audio is playing on the bluetooth device (A2DP profile is streaming music). - * - * @param device BluetoothDevice device - * @return true if audio is playing (A2dp is streaming music), false otherwise - * - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean isAudioPlaying(@NonNull BluetoothDevice device) { - if (VDBG) log("isAudioPlaying(" + device + ")"); - final IBluetoothA2dpSink service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isA2dpPlaying(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Helper for converting a state to a string. - * - * For debug use only - strings are not internationalized. - * - * @hide - */ - public static String stateToString(int state) { - switch (state) { - case STATE_DISCONNECTED: - return "disconnected"; - case STATE_CONNECTING: - return "connecting"; - case STATE_CONNECTED: - return "connected"; - case STATE_DISCONNECTING: - return "disconnecting"; - case BluetoothA2dp.STATE_PLAYING: - return "playing"; - case BluetoothA2dp.STATE_NOT_PLAYING: - return "not playing"; - default: - return "<unknown state " + state + ">"; - } - } - - private boolean isEnabled() { - return mAdapter.getState() == BluetoothAdapter.STATE_ON; - } - - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java deleted file mode 100644 index c17a7b4b3dfd..000000000000 --- a/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.ElapsedRealtimeLong; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Collections; -import java.util.List; - -/** - * Record of energy and activity information from controller and - * underlying bt stack state.Timestamp the record with system - * time. - * - * @hide - */ -@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) -public final class BluetoothActivityEnergyInfo implements Parcelable { - private final long mTimestamp; - private int mBluetoothStackState; - private long mControllerTxTimeMs; - private long mControllerRxTimeMs; - private long mControllerIdleTimeMs; - private long mControllerEnergyUsed; - private List<UidTraffic> mUidTraffic; - - /** @hide */ - @IntDef(prefix = { "BT_STACK_STATE_" }, value = { - BT_STACK_STATE_INVALID, - BT_STACK_STATE_STATE_ACTIVE, - BT_STACK_STATE_STATE_SCANNING, - BT_STACK_STATE_STATE_IDLE - }) - @Retention(RetentionPolicy.SOURCE) - public @interface BluetoothStackState {} - - public static final int BT_STACK_STATE_INVALID = 0; - public static final int BT_STACK_STATE_STATE_ACTIVE = 1; - public static final int BT_STACK_STATE_STATE_SCANNING = 2; - public static final int BT_STACK_STATE_STATE_IDLE = 3; - - /** @hide */ - public BluetoothActivityEnergyInfo(long timestamp, int stackState, - long txTime, long rxTime, long idleTime, long energyUsed) { - mTimestamp = timestamp; - mBluetoothStackState = stackState; - mControllerTxTimeMs = txTime; - mControllerRxTimeMs = rxTime; - mControllerIdleTimeMs = idleTime; - mControllerEnergyUsed = energyUsed; - } - - /** @hide */ - private BluetoothActivityEnergyInfo(Parcel in) { - mTimestamp = in.readLong(); - mBluetoothStackState = in.readInt(); - mControllerTxTimeMs = in.readLong(); - mControllerRxTimeMs = in.readLong(); - mControllerIdleTimeMs = in.readLong(); - mControllerEnergyUsed = in.readLong(); - mUidTraffic = in.createTypedArrayList(UidTraffic.CREATOR); - } - - /** @hide */ - @Override - public String toString() { - return "BluetoothActivityEnergyInfo{" - + " mTimestamp=" + mTimestamp - + " mBluetoothStackState=" + mBluetoothStackState - + " mControllerTxTimeMs=" + mControllerTxTimeMs - + " mControllerRxTimeMs=" + mControllerRxTimeMs - + " mControllerIdleTimeMs=" + mControllerIdleTimeMs - + " mControllerEnergyUsed=" + mControllerEnergyUsed - + " mUidTraffic=" + mUidTraffic - + " }"; - } - - public static final @NonNull Parcelable.Creator<BluetoothActivityEnergyInfo> CREATOR = - new Parcelable.Creator<BluetoothActivityEnergyInfo>() { - public BluetoothActivityEnergyInfo createFromParcel(Parcel in) { - return new BluetoothActivityEnergyInfo(in); - } - - public BluetoothActivityEnergyInfo[] newArray(int size) { - return new BluetoothActivityEnergyInfo[size]; - } - }; - - /** @hide */ - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeLong(mTimestamp); - out.writeInt(mBluetoothStackState); - out.writeLong(mControllerTxTimeMs); - out.writeLong(mControllerRxTimeMs); - out.writeLong(mControllerIdleTimeMs); - out.writeLong(mControllerEnergyUsed); - out.writeTypedList(mUidTraffic); - } - - /** @hide */ - @Override - public int describeContents() { - return 0; - } - - /** - * Get the Bluetooth stack state associated with the energy info. - * - * @return one of {@link #BluetoothStackState} states - */ - @BluetoothStackState - public int getBluetoothStackState() { - return mBluetoothStackState; - } - - /** - * @return tx time in ms - */ - public long getControllerTxTimeMillis() { - return mControllerTxTimeMs; - } - - /** - * @return rx time in ms - */ - public long getControllerRxTimeMillis() { - return mControllerRxTimeMs; - } - - /** - * @return idle time in ms - */ - public long getControllerIdleTimeMillis() { - return mControllerIdleTimeMs; - } - - /** - * Get the product of current (mA), voltage (V), and time (ms). - * - * @return energy used - */ - public long getControllerEnergyUsed() { - return mControllerEnergyUsed; - } - - /** - * @return timestamp (real time elapsed in milliseconds since boot) of record creation - */ - public @ElapsedRealtimeLong long getTimestampMillis() { - return mTimestamp; - } - - /** - * Get the {@link List} of each application {@link android.bluetooth.UidTraffic}. - * - * @return current {@link List} of {@link android.bluetooth.UidTraffic} - */ - public @NonNull List<UidTraffic> getUidTraffic() { - if (mUidTraffic == null) { - return Collections.emptyList(); - } - return mUidTraffic; - } - - /** @hide */ - public void setUidTraffic(List<UidTraffic> traffic) { - mUidTraffic = traffic; - } - - /** - * @return true if the record Tx time, Rx time, and Idle time are more than 0. - */ - public boolean isValid() { - return ((mControllerTxTimeMs >= 0) && (mControllerRxTimeMs >= 0) - && (mControllerIdleTimeMs >= 0)); - } -} diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java deleted file mode 100644 index 9b37457c9907..000000000000 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ /dev/null @@ -1,4389 +0,0 @@ -/* - * Copyright 2009-2016 The Android Open Source Project - * Copyright 2015 Samsung LSI - * - * 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 static java.util.Objects.requireNonNull; - -import android.annotation.CallbackExecutor; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresNoPermission; -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.bluetooth.BluetoothDevice.Transport; -import android.bluetooth.BluetoothProfile.ConnectionPolicy; -import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresBluetoothLocationPermission; -import android.bluetooth.annotations.RequiresBluetoothScanPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.bluetooth.le.BluetoothLeAdvertiser; -import android.bluetooth.le.BluetoothLeScanner; -import android.bluetooth.le.PeriodicAdvertisingManager; -import android.bluetooth.le.ScanCallback; -import android.bluetooth.le.ScanFilter; -import android.bluetooth.le.ScanRecord; -import android.bluetooth.le.ScanResult; -import android.bluetooth.le.ScanSettings; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.Context; -import android.os.Binder; -import android.os.Build; -import android.os.IBinder; -import android.os.ParcelUuid; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.os.ServiceManager; -import android.sysprop.BluetoothProperties; -import android.util.Log; -import android.util.Pair; - -import com.android.internal.annotations.GuardedBy; - -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; -import java.util.WeakHashMap; -import java.util.concurrent.Executor; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -/** - * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter} - * lets you perform fundamental Bluetooth tasks, such as initiate - * device discovery, query a list of bonded (paired) devices, - * instantiate a {@link BluetoothDevice} using a known MAC address, and create - * a {@link BluetoothServerSocket} to listen for connection requests from other - * devices, and start a scan for Bluetooth LE devices. - * - * <p>To get a {@link BluetoothAdapter} representing the local Bluetooth - * adapter, call the {@link BluetoothManager#getAdapter} function on {@link BluetoothManager}. - * On JELLY_BEAN_MR1 and below you will need to use the static {@link #getDefaultAdapter} - * method instead. - * </p><p> - * Fundamentally, this is your starting point for all - * Bluetooth actions. Once you have the local adapter, you can get a set of - * {@link BluetoothDevice} objects representing all paired devices with - * {@link #getBondedDevices()}; start device discovery with - * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to - * listen for incoming RFComm connection requests with {@link - * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented - * Channels (CoC) connection requests with {@link #listenUsingL2capChannel()}; or start a scan for - * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. - * </p> - * <p>This class is thread safe.</p> - * <div class="special reference"> - * <h3>Developer Guides</h3> - * <p> - * For more information about using Bluetooth, read the <a href= - * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer - * guide. - * </p> - * </div> - * - * {@see BluetoothDevice} - * {@see BluetoothServerSocket} - */ -public final class BluetoothAdapter { - private static final String TAG = "BluetoothAdapter"; - private static final String DESCRIPTOR = "android.bluetooth.BluetoothAdapter"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** - * Default MAC address reported to a client that does not have the - * android.permission.LOCAL_MAC_ADDRESS permission. - * - * @hide - */ - public static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00"; - - /** - * Sentinel error value for this class. Guaranteed to not equal any other - * integer constant in this class. Provided as a convenience for functions - * that require a sentinel error value, for example: - * <p><code>Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, - * BluetoothAdapter.ERROR)</code> - */ - public static final int ERROR = Integer.MIN_VALUE; - - /** - * Broadcast Action: The state of the local Bluetooth adapter has been - * changed. - * <p>For example, Bluetooth has been turned on or off. - * <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link - * #EXTRA_PREVIOUS_STATE} containing the new and old states - * respectively. - */ - @RequiresLegacyBluetoothPermission - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String - ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED"; - - /** - * Used as an int extra field in {@link #ACTION_STATE_CHANGED} - * intents to request the current power state. Possible values are: - * {@link #STATE_OFF}, - * {@link #STATE_TURNING_ON}, - * {@link #STATE_ON}, - * {@link #STATE_TURNING_OFF}, - */ - public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE"; - /** - * Used as an int extra field in {@link #ACTION_STATE_CHANGED} - * intents to request the previous power state. Possible values are: - * {@link #STATE_OFF}, - * {@link #STATE_TURNING_ON}, - * {@link #STATE_ON}, - * {@link #STATE_TURNING_OFF} - */ - public static final String EXTRA_PREVIOUS_STATE = - "android.bluetooth.adapter.extra.PREVIOUS_STATE"; - - /** @hide */ - @IntDef(prefix = { "STATE_" }, value = { - STATE_OFF, - STATE_TURNING_ON, - STATE_ON, - STATE_TURNING_OFF, - STATE_BLE_TURNING_ON, - STATE_BLE_ON, - STATE_BLE_TURNING_OFF - }) - @Retention(RetentionPolicy.SOURCE) - public @interface AdapterState {} - - /** - * Indicates the local Bluetooth adapter is off. - */ - public static final int STATE_OFF = 10; - /** - * Indicates the local Bluetooth adapter is turning on. However local - * clients should wait for {@link #STATE_ON} before attempting to - * use the adapter. - */ - public static final int STATE_TURNING_ON = 11; - /** - * Indicates the local Bluetooth adapter is on, and ready for use. - */ - public static final int STATE_ON = 12; - /** - * Indicates the local Bluetooth adapter is turning off. Local clients - * should immediately attempt graceful disconnection of any remote links. - */ - public static final int STATE_TURNING_OFF = 13; - - /** - * Indicates the local Bluetooth adapter is turning Bluetooth LE mode on. - * - * @hide - */ - public static final int STATE_BLE_TURNING_ON = 14; - - /** - * Indicates the local Bluetooth adapter is in LE only mode. - * - * @hide - */ - public static final int STATE_BLE_ON = 15; - - /** - * Indicates the local Bluetooth adapter is turning off LE only mode. - * - * @hide - */ - public static final int STATE_BLE_TURNING_OFF = 16; - - /** - * UUID of the GATT Read Characteristics for LE_PSM value. - * - * @hide - */ - public static final UUID LE_PSM_CHARACTERISTIC_UUID = - UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a"); - - /** - * Human-readable string helper for AdapterState - * - * @hide - */ - public static String nameForState(@AdapterState int state) { - switch (state) { - case STATE_OFF: - return "OFF"; - case STATE_TURNING_ON: - return "TURNING_ON"; - case STATE_ON: - return "ON"; - case STATE_TURNING_OFF: - return "TURNING_OFF"; - case STATE_BLE_TURNING_ON: - return "BLE_TURNING_ON"; - case STATE_BLE_ON: - return "BLE_ON"; - case STATE_BLE_TURNING_OFF: - return "BLE_TURNING_OFF"; - default: - return "?!?!? (" + state + ")"; - } - } - - /** - * Activity Action: Show a system activity that requests discoverable mode. - * This activity will also request the user to turn on Bluetooth if it - * is not currently enabled. - * <p>Discoverable mode is equivalent to {@link - * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see - * this Bluetooth adapter when they perform a discovery. - * <p>For privacy, Android is not discoverable by default. - * <p>The sender of this Intent can optionally use extra field {@link - * #EXTRA_DISCOVERABLE_DURATION} to request the duration of - * discoverability. Currently the default duration is 120 seconds, and - * maximum duration is capped at 300 seconds for each request. - * <p>Notification of the result of this activity is posted using the - * {@link android.app.Activity#onActivityResult} callback. The - * <code>resultCode</code> - * will be the duration (in seconds) of discoverability or - * {@link android.app.Activity#RESULT_CANCELED} if the user rejected - * discoverability or an error has occurred. - * <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED} - * for global notification whenever the scan mode changes. For example, an - * application can be notified when the device has ended discoverability. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String - ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE"; - - /** - * Used as an optional int extra field in {@link - * #ACTION_REQUEST_DISCOVERABLE} intents to request a specific duration - * for discoverability in seconds. The current default is 120 seconds, and - * requests over 300 seconds will be capped. These values could change. - */ - public static final String EXTRA_DISCOVERABLE_DURATION = - "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION"; - - /** - * Activity Action: Show a system activity that allows the user to turn on - * Bluetooth. - * <p>This system activity will return once Bluetooth has completed turning - * on, or the user has decided not to turn Bluetooth on. - * <p>Notification of the result of this activity is posted using the - * {@link android.app.Activity#onActivityResult} callback. The - * <code>resultCode</code> - * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been - * turned on or {@link android.app.Activity#RESULT_CANCELED} if the user - * has rejected the request or an error has occurred. - * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED} - * for global notification whenever Bluetooth is turned on or off. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String - ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE"; - - /** - * Activity Action: Show a system activity that allows the user to turn off - * Bluetooth. This is used only if permission review is enabled which is for - * apps targeting API less than 23 require a permission review before any of - * the app's components can run. - * <p>This system activity will return once Bluetooth has completed turning - * off, or the user has decided not to turn Bluetooth off. - * <p>Notification of the result of this activity is posted using the - * {@link android.app.Activity#onActivityResult} callback. The - * <code>resultCode</code> - * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been - * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user - * has rejected the request or an error has occurred. - * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED} - * for global notification whenever Bluetooth is turned on or off. - * - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String - ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE"; - - /** - * Activity Action: Show a system activity that allows user to enable BLE scans even when - * Bluetooth is turned off.<p> - * - * Notification of result of this activity is posted using - * {@link android.app.Activity#onActivityResult}. The <code>resultCode</code> will be - * {@link android.app.Activity#RESULT_OK} if BLE scan always available setting is turned on or - * {@link android.app.Activity#RESULT_CANCELED} if the user has rejected the request or an - * error occurred. - * - * @hide - */ - @SystemApi - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = - "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE"; - - /** - * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter - * has changed. - * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link - * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes - * respectively. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String - ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED"; - - /** - * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} - * intents to request the current scan mode. Possible values are: - * {@link #SCAN_MODE_NONE}, - * {@link #SCAN_MODE_CONNECTABLE}, - * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}, - */ - public static final String EXTRA_SCAN_MODE = "android.bluetooth.adapter.extra.SCAN_MODE"; - /** - * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED} - * intents to request the previous scan mode. Possible values are: - * {@link #SCAN_MODE_NONE}, - * {@link #SCAN_MODE_CONNECTABLE}, - * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}, - */ - public static final String EXTRA_PREVIOUS_SCAN_MODE = - "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE"; - - /** @hide */ - @IntDef(prefix = { "SCAN_" }, value = { - SCAN_MODE_NONE, - SCAN_MODE_CONNECTABLE, - SCAN_MODE_CONNECTABLE_DISCOVERABLE - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ScanMode {} - - /** - * Indicates that both inquiry scan and page scan are disabled on the local - * Bluetooth adapter. Therefore this device is neither discoverable - * nor connectable from remote Bluetooth devices. - */ - public static final int SCAN_MODE_NONE = 20; - /** - * Indicates that inquiry scan is disabled, but page scan is enabled on the - * local Bluetooth adapter. Therefore this device is not discoverable from - * remote Bluetooth devices, but is connectable from remote devices that - * have previously discovered this device. - */ - public static final int SCAN_MODE_CONNECTABLE = 21; - /** - * Indicates that both inquiry scan and page scan are enabled on the local - * Bluetooth adapter. Therefore this device is both discoverable and - * connectable from remote Bluetooth devices. - */ - public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23; - - /** - * Device only has a display. - * - * @hide - */ - public static final int IO_CAPABILITY_OUT = 0; - - /** - * Device has a display and the ability to input Yes/No. - * - * @hide - */ - public static final int IO_CAPABILITY_IO = 1; - - /** - * Device only has a keyboard for entry but no display. - * - * @hide - */ - public static final int IO_CAPABILITY_IN = 2; - - /** - * Device has no Input or Output capability. - * - * @hide - */ - public static final int IO_CAPABILITY_NONE = 3; - - /** - * Device has a display and a full keyboard. - * - * @hide - */ - public static final int IO_CAPABILITY_KBDISP = 4; - - /** - * Maximum range value for Input/Output capabilities. - * - * <p>This should be updated when adding a new Input/Output capability. Other code - * like validation depends on this being accurate. - * - * @hide - */ - public static final int IO_CAPABILITY_MAX = 5; - - /** - * The Input/Output capability of the device is unknown. - * - * @hide - */ - public static final int IO_CAPABILITY_UNKNOWN = 255; - - /** @hide */ - @IntDef({IO_CAPABILITY_OUT, IO_CAPABILITY_IO, IO_CAPABILITY_IN, IO_CAPABILITY_NONE, - IO_CAPABILITY_KBDISP}) - @Retention(RetentionPolicy.SOURCE) - public @interface IoCapability {} - - /** @hide */ - @IntDef(prefix = "ACTIVE_DEVICE_", value = {ACTIVE_DEVICE_AUDIO, - ACTIVE_DEVICE_PHONE_CALL, ACTIVE_DEVICE_ALL}) - @Retention(RetentionPolicy.SOURCE) - public @interface ActiveDeviceUse {} - - /** - * Use the specified device for audio (a2dp and hearing aid profile) - * - * @hide - */ - @SystemApi - public static final int ACTIVE_DEVICE_AUDIO = 0; - - /** - * Use the specified device for phone calls (headset profile and hearing - * aid profile) - * - * @hide - */ - @SystemApi - public static final int ACTIVE_DEVICE_PHONE_CALL = 1; - - /** - * Use the specified device for a2dp, hearing aid profile, and headset profile - * - * @hide - */ - @SystemApi - public static final int ACTIVE_DEVICE_ALL = 2; - - /** @hide */ - @IntDef({BluetoothProfile.HEADSET, BluetoothProfile.A2DP, - BluetoothProfile.HEARING_AID}) - @Retention(RetentionPolicy.SOURCE) - public @interface ActiveDeviceProfile {} - - /** - * Broadcast Action: The local Bluetooth adapter has started the remote - * device discovery process. - * <p>This usually involves an inquiry scan of about 12 seconds, followed - * by a page scan of each new device to retrieve its Bluetooth name. - * <p>Register for {@link BluetoothDevice#ACTION_FOUND} to be notified as - * remote Bluetooth devices are found. - * <p>Device discovery is a heavyweight procedure. New connections to - * remote Bluetooth devices should not be attempted while discovery is in - * progress, and existing connections will experience limited bandwidth - * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing - * discovery. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String - ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED"; - /** - * Broadcast Action: The local Bluetooth adapter has finished the device - * discovery process. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String - ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; - - /** - * Broadcast Action: The local Bluetooth adapter has changed its friendly - * Bluetooth name. - * <p>This name is visible to remote Bluetooth devices. - * <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing - * the name. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String - ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED"; - /** - * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED} - * intents to request the local Bluetooth name. - */ - public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME"; - - /** - * Intent used to broadcast the change in connection state of the local - * Bluetooth adapter to a profile of the remote device. When the adapter is - * not connected to any profiles of any remote devices and it attempts a - * connection to a profile this intent will be sent. Once connected, this intent - * will not be sent for any more connection attempts to any profiles of any - * remote device. When the adapter disconnects from the last profile its - * connected to of any remote device, this intent will be sent. - * - * <p> This intent is useful for applications that are only concerned about - * whether the local adapter is connected to any profile of any device and - * are not really concerned about which profile. For example, an application - * which displays an icon to display whether Bluetooth is connected or not - * can use this intent. - * - * <p>This intent will have 3 extras: - * {@link #EXTRA_CONNECTION_STATE} - The current connection state. - * {@link #EXTRA_PREVIOUS_CONNECTION_STATE}- The previous connection state. - * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. - * - * {@link #EXTRA_CONNECTION_STATE} or {@link #EXTRA_PREVIOUS_CONNECTION_STATE} - * can be any of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String - ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"; - - /** - * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED} - * - * This extra represents the current connection state. - */ - public static final String EXTRA_CONNECTION_STATE = - "android.bluetooth.adapter.extra.CONNECTION_STATE"; - - /** - * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED} - * - * This extra represents the previous connection state. - */ - public static final String EXTRA_PREVIOUS_CONNECTION_STATE = - "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE"; - - /** - * Broadcast Action: The Bluetooth adapter state has changed in LE only mode. - * - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @SystemApi public static final String ACTION_BLE_STATE_CHANGED = - "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; - - /** - * Intent used to broadcast the change in the Bluetooth address - * of the local Bluetooth adapter. - * <p>Always contains the extra field {@link - * #EXTRA_BLUETOOTH_ADDRESS} containing the Bluetooth address. - * - * Note: only system level processes are allowed to send this - * defined broadcast. - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED = - "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED"; - - /** - * Used as a String extra field in {@link - * #ACTION_BLUETOOTH_ADDRESS_CHANGED} intent to store the local - * Bluetooth address. - * - * @hide - */ - public static final String EXTRA_BLUETOOTH_ADDRESS = - "android.bluetooth.adapter.extra.BLUETOOTH_ADDRESS"; - - /** - * Broadcast Action: The notifys Bluetooth ACL connected event. This will be - * by BLE Always on enabled application to know the ACL_CONNECTED event - * when Bluetooth state in STATE_BLE_ON. This denotes GATT connection - * as Bluetooth LE is the only feature available in STATE_BLE_ON - * - * This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which - * works in Bluetooth state STATE_ON - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_BLE_ACL_CONNECTED = - "android.bluetooth.adapter.action.BLE_ACL_CONNECTED"; - - /** - * Broadcast Action: The notifys Bluetooth ACL connected event. This will be - * by BLE Always on enabled application to know the ACL_DISCONNECTED event - * when Bluetooth state in STATE_BLE_ON. This denotes GATT disconnection as Bluetooth - * LE is the only feature available in STATE_BLE_ON - * - * This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which - * works in Bluetooth state STATE_ON - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_BLE_ACL_DISCONNECTED = - "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; - - /** The profile is in disconnected state */ - public static final int STATE_DISCONNECTED = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED; - /** The profile is in connecting state */ - public static final int STATE_CONNECTING = BluetoothProtoEnums.CONNECTION_STATE_CONNECTING; - /** The profile is in connected state */ - public static final int STATE_CONNECTED = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED; - /** The profile is in disconnecting state */ - public static final int STATE_DISCONNECTING = - BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING; - - /** @hide */ - public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager"; - private final IBinder mToken; - - - /** - * When creating a ServerSocket using listenUsingRfcommOn() or - * listenUsingL2capOn() use SOCKET_CHANNEL_AUTO_STATIC to create - * a ServerSocket that auto assigns a channel number to the first - * bluetooth socket. - * The channel number assigned to this first Bluetooth Socket will - * be stored in the ServerSocket, and reused for subsequent Bluetooth - * sockets. - * - * @hide - */ - public static final int SOCKET_CHANNEL_AUTO_STATIC_NO_SDP = -2; - - - private static final int ADDRESS_LENGTH = 17; - - /** - * Lazily initialized singleton. Guaranteed final after first object - * constructed. - */ - private static BluetoothAdapter sAdapter; - - private BluetoothLeScanner mBluetoothLeScanner; - private BluetoothLeAdvertiser mBluetoothLeAdvertiser; - private PeriodicAdvertisingManager mPeriodicAdvertisingManager; - - private final IBluetoothManager mManagerService; - private final AttributionSource mAttributionSource; - - // Yeah, keeping both mService and sService isn't pretty, but it's too late - // in the current release for a major refactoring, so we leave them both - // intact until this can be cleaned up in a future release - - @UnsupportedAppUsage - @GuardedBy("mServiceLock") - private IBluetooth mService; - private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); - - @GuardedBy("sServiceLock") - private static boolean sServiceRegistered; - @GuardedBy("sServiceLock") - private static IBluetooth sService; - private static final Object sServiceLock = new Object(); - - private final Object mLock = new Object(); - private final Map<LeScanCallback, ScanCallback> mLeScanClients; - private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>> - mMetadataListeners = new HashMap<>(); - private final Map<BluetoothConnectionCallback, Executor> - mBluetoothConnectionCallbackExecutorMap = new HashMap<>(); - - /** - * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener - * implementation. - */ - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final IBluetoothMetadataListener mBluetoothMetadataListener = - new IBluetoothMetadataListener.Stub() { - @Override - public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) { - Attributable.setAttributionSource(device, mAttributionSource); - synchronized (mMetadataListeners) { - if (mMetadataListeners.containsKey(device)) { - List<Pair<OnMetadataChangedListener, Executor>> list = - mMetadataListeners.get(device); - for (Pair<OnMetadataChangedListener, Executor> pair : list) { - OnMetadataChangedListener listener = pair.first; - Executor executor = pair.second; - executor.execute(() -> { - listener.onMetadataChanged(device, key, value); - }); - } - } - } - return; - } - }; - - /** - * Get a handle to the default local Bluetooth adapter. - * <p> - * Currently Android only supports one Bluetooth adapter, but the API could - * be extended to support more. This will always return the default adapter. - * </p> - * - * @return the default local adapter, or null if Bluetooth is not supported - * on this hardware platform - * @deprecated this method will continue to work, but developers are - * strongly encouraged to migrate to using - * {@link BluetoothManager#getAdapter()}, since that approach - * enables support for {@link Context#createAttributionContext}. - */ - @Deprecated - @RequiresNoPermission - public static synchronized BluetoothAdapter getDefaultAdapter() { - if (sAdapter == null) { - sAdapter = createAdapter(AttributionSource.myAttributionSource()); - } - return sAdapter; - } - - /** {@hide} */ - public static BluetoothAdapter createAdapter(AttributionSource attributionSource) { - IBinder binder = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE); - if (binder != null) { - return new BluetoothAdapter(IBluetoothManager.Stub.asInterface(binder), - attributionSource); - } else { - Log.e(TAG, "Bluetooth binder is null"); - return null; - } - } - - /** - * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance. - */ - BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) { - mManagerService = Objects.requireNonNull(managerService); - mAttributionSource = Objects.requireNonNull(attributionSource); - synchronized (mServiceLock.writeLock()) { - mService = getBluetoothService(mManagerCallback); - } - mLeScanClients = new HashMap<LeScanCallback, ScanCallback>(); - mToken = new Binder(DESCRIPTOR); - } - - /** - * Get a {@link BluetoothDevice} object for the given Bluetooth hardware - * address. - * <p>Valid Bluetooth hardware addresses must be upper case, in a format - * such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is - * available to validate a Bluetooth address. - * <p>A {@link BluetoothDevice} will always be returned for a valid - * hardware address, even if this adapter has never seen that device. - * - * @param address valid Bluetooth MAC address - * @throws IllegalArgumentException if address is invalid - */ - @RequiresNoPermission - public BluetoothDevice getRemoteDevice(String address) { - final BluetoothDevice res = new BluetoothDevice(address); - res.setAttributionSource(mAttributionSource); - return res; - } - - /** - * Get a {@link BluetoothDevice} object for the given Bluetooth hardware - * address. - * <p>Valid Bluetooth hardware addresses must be 6 bytes. This method - * expects the address in network byte order (MSB first). - * <p>A {@link BluetoothDevice} will always be returned for a valid - * hardware address, even if this adapter has never seen that device. - * - * @param address Bluetooth MAC address (6 bytes) - * @throws IllegalArgumentException if address is invalid - */ - @RequiresNoPermission - public BluetoothDevice getRemoteDevice(byte[] address) { - if (address == null || address.length != 6) { - throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); - } - final BluetoothDevice res = new BluetoothDevice( - String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1], - address[2], address[3], address[4], address[5])); - res.setAttributionSource(mAttributionSource); - return res; - } - - /** - * Returns a {@link BluetoothLeAdvertiser} object for Bluetooth LE Advertising operations. - * Will return null if Bluetooth is turned off or if Bluetooth LE Advertising is not - * supported on this device. - * <p> - * Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported - * on this device before calling this method. - */ - @RequiresNoPermission - public BluetoothLeAdvertiser getBluetoothLeAdvertiser() { - if (!getLeAccess()) { - return null; - } - synchronized (mLock) { - if (mBluetoothLeAdvertiser == null) { - mBluetoothLeAdvertiser = new BluetoothLeAdvertiser(this); - } - return mBluetoothLeAdvertiser; - } - } - - /** - * Returns a {@link PeriodicAdvertisingManager} object for Bluetooth LE Periodic Advertising - * operations. Will return null if Bluetooth is turned off or if Bluetooth LE Periodic - * Advertising is not supported on this device. - * <p> - * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is - * supported on this device before calling this method. - * - * @hide - */ - @RequiresNoPermission - public PeriodicAdvertisingManager getPeriodicAdvertisingManager() { - if (!getLeAccess()) { - return null; - } - - if (!isLePeriodicAdvertisingSupported()) { - return null; - } - - synchronized (mLock) { - if (mPeriodicAdvertisingManager == null) { - mPeriodicAdvertisingManager = new PeriodicAdvertisingManager(this); - } - return mPeriodicAdvertisingManager; - } - } - - /** - * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations. - */ - @RequiresNoPermission - public BluetoothLeScanner getBluetoothLeScanner() { - if (!getLeAccess()) { - return null; - } - synchronized (mLock) { - if (mBluetoothLeScanner == null) { - mBluetoothLeScanner = new BluetoothLeScanner(this); - } - return mBluetoothLeScanner; - } - } - - /** - * Return true if Bluetooth is currently enabled and ready for use. - * <p>Equivalent to: - * <code>getBluetoothState() == STATE_ON</code> - * - * @return true if the local adapter is turned on - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public boolean isEnabled() { - return getState() == BluetoothAdapter.STATE_ON; - } - - /** - * Return true if Bluetooth LE(Always BLE On feature) is currently - * enabled and ready for use - * <p>This returns true if current state is either STATE_ON or STATE_BLE_ON - * - * @return true if the local Bluetooth LE adapter is turned on - * @hide - */ - @SystemApi - @RequiresNoPermission - public boolean isLeEnabled() { - final int state = getLeState(); - if (DBG) { - Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state)); - } - return (state == BluetoothAdapter.STATE_ON - || state == BluetoothAdapter.STATE_BLE_ON - || state == BluetoothAdapter.STATE_TURNING_ON - || state == BluetoothAdapter.STATE_TURNING_OFF); - } - - /** - * Turns off Bluetooth LE which was earlier turned on by calling enableBLE(). - * - * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition - * to STATE_OFF and completely shut-down Bluetooth - * - * <p> If the Adapter state is STATE_ON, This would unregister the existance of - * special Bluetooth LE application and hence the further turning off of Bluetooth - * from UI would ensure the complete turn-off of Bluetooth rather than staying back - * BLE only state - * - * <p>This is an asynchronous call: it will return immediately, and - * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} - * to be notified of subsequent adapter state changes If this call returns - * true, then the adapter state will immediately transition from {@link - * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time - * later transition to either {@link #STATE_BLE_ON} or {@link - * #STATE_OFF} based on the existance of the further Always BLE ON enabled applications - * If this call returns false then there was an - * immediate problem that will prevent the QAdapter from being turned off - - * such as the QAadapter already being turned off. - * - * @return true to indicate success, or false on immediate error - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disableBLE() { - if (!isBleScanAlwaysAvailable()) { - return false; - } - try { - return mManagerService.disableBle(mAttributionSource, mToken); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Applications who want to only use Bluetooth Low Energy (BLE) can call enableBLE. - * - * enableBLE registers the existence of an app using only LE functions. - * - * enableBLE may enable Bluetooth to an LE only mode so that an app can use - * LE related features (BluetoothGatt or BluetoothGattServer classes) - * - * If the user disables Bluetooth while an app is registered to use LE only features, - * Bluetooth will remain on in LE only mode for the app. - * - * When Bluetooth is in LE only mode, it is not shown as ON to the UI. - * - * <p>This is an asynchronous call: it returns immediately, and - * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} - * to be notified of adapter state changes. - * - * If this call returns * true, then the adapter state is either in a mode where - * LE is available, or will transition from {@link #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, - * and some time later transition to either {@link #STATE_OFF} or {@link #STATE_BLE_ON}. - * - * If this call returns false then there was an immediate problem that prevents the - * adapter from being turned on - such as Airplane mode. - * - * {@link #ACTION_BLE_STATE_CHANGED} returns the Bluetooth Adapter's various - * states, It includes all the classic Bluetooth Adapter states along with - * internal BLE only states - * - * @return true to indicate Bluetooth LE will be available, or false on immediate error - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean enableBLE() { - if (!isBleScanAlwaysAvailable()) { - return false; - } - try { - return mManagerService.enableBle(mAttributionSource, mToken); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - - return false; - } - - private static final String BLUETOOTH_GET_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_state"; - - private final PropertyInvalidatedCache<Void, Integer> mBluetoothGetStateCache = - new PropertyInvalidatedCache<Void, Integer>( - 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) { - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - protected Integer recompute(Void query) { - try { - return mService.getState(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - }; - - /** @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 - * OFF. - */ - @AdapterState - private int getStateInternal() { - int state = BluetoothAdapter.STATE_OFF; - 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; - } - } finally { - mServiceLock.readLock().unlock(); - } - return state; - } - - /** - * Get the current state of the local Bluetooth adapter. - * <p>Possible return values are - * {@link #STATE_OFF}, - * {@link #STATE_TURNING_ON}, - * {@link #STATE_ON}, - * {@link #STATE_TURNING_OFF}. - * - * @return current state of Bluetooth adapter - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - @AdapterState - public int getState() { - int state = getStateInternal(); - - // Consider all internal states as OFF - if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON - || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { - if (VDBG) { - Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF"); - } - state = BluetoothAdapter.STATE_OFF; - } - if (VDBG) { - Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState( - state)); - } - return state; - } - - /** - * Get the current state of the local Bluetooth adapter - * <p>This returns current internal state of Adapter including LE ON/OFF - * - * <p>Possible return values are - * {@link #STATE_OFF}, - * {@link #STATE_BLE_TURNING_ON}, - * {@link #STATE_BLE_ON}, - * {@link #STATE_TURNING_ON}, - * {@link #STATE_ON}, - * {@link #STATE_TURNING_OFF}, - * {@link #STATE_BLE_TURNING_OFF}. - * - * @return current state of Bluetooth adapter - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - @AdapterState - @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine " - + "whether you can use BLE & BT classic.") - public int getLeState() { - int state = getStateInternal(); - - if (VDBG) { - Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state)); - } - return state; - } - - boolean getLeAccess() { - if (getLeState() == STATE_ON) { - return true; - } else if (getLeState() == STATE_BLE_ON) { - return true; // TODO: FILTER SYSTEM APPS HERE <-- - } - - return false; - } - - /** - * Turn on the local Bluetooth adapter—do not use without explicit - * user action to turn on Bluetooth. - * <p>This powers on the underlying Bluetooth hardware, and starts all - * Bluetooth system services. - * <p class="caution"><strong>Bluetooth should never be enabled without - * direct user consent</strong>. If you want to turn on Bluetooth in order - * to create a wireless connection, you should use the {@link - * #ACTION_REQUEST_ENABLE} Intent, which will raise a dialog that requests - * user permission to turn on Bluetooth. The {@link #enable()} method is - * provided only for applications that include a user interface for changing - * system settings, such as a "power manager" app.</p> - * <p>This is an asynchronous call: it will return immediately, and - * clients should listen for {@link #ACTION_STATE_CHANGED} - * to be notified of subsequent adapter state changes. If this call returns - * true, then the adapter state will immediately transition from {@link - * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time - * later transition to either {@link #STATE_OFF} or {@link - * #STATE_ON}. If this call returns false then there was an - * immediate problem that will prevent the adapter from being turned on - - * such as Airplane mode, or the adapter is already turned on. - * - * @return true to indicate adapter startup has begun, or false on immediate error - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean enable() { - if (isEnabled()) { - if (DBG) { - Log.d(TAG, "enable(): BT already enabled!"); - } - return true; - } - try { - return mManagerService.enable(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Turn off the local Bluetooth adapter—do not use without explicit - * user action to turn off Bluetooth. - * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth - * system services, and powers down the underlying Bluetooth hardware. - * <p class="caution"><strong>Bluetooth should never be disabled without - * direct user consent</strong>. The {@link #disable()} method is - * provided only for applications that include a user interface for changing - * system settings, such as a "power manager" app.</p> - * <p>This is an asynchronous call: it will return immediately, and - * clients should listen for {@link #ACTION_STATE_CHANGED} - * to be notified of subsequent adapter state changes. If this call returns - * true, then the adapter state will immediately transition from {@link - * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time - * later transition to either {@link #STATE_OFF} or {@link - * #STATE_ON}. If this call returns false then there was an - * immediate problem that will prevent the adapter from being turned off - - * such as the adapter already being turned off. - * - * @return true to indicate adapter shutdown has begun, or false on immediate error - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disable() { - try { - return mManagerService.disable(mAttributionSource, true); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Turn off the local Bluetooth adapter and don't persist the setting. - * - * @param persist Indicate whether the off state should be persisted following the next reboot - * @return true to indicate adapter shutdown has begun, or false on immediate error - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean disable(boolean persist) { - - try { - return mManagerService.disable(mAttributionSource, persist); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Returns the hardware address of the local Bluetooth adapter. - * <p>For example, "00:11:22:AA:BB:CC". - * - * @return Bluetooth hardware address as string - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.LOCAL_MAC_ADDRESS, - }) - public String getAddress() { - try { - return mManagerService.getAddress(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return null; - } - - /** - * Get the friendly Bluetooth name of the local Bluetooth adapter. - * <p>This name is visible to remote Bluetooth devices. - * - * @return the Bluetooth name, or null on error - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public String getName() { - try { - return mManagerService.getName(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return null; - } - - /** {@hide} */ - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public int getNameLengthForAdvertise() { - try { - return mService.getNameLengthForAdvertise(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return -1; - } - - /** - * Factory reset bluetooth settings. - * - * @return true to indicate that the config file was successfully cleared - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean factoryReset() { - try { - mServiceLock.readLock().lock(); - if (mService != null && mService.factoryReset(mAttributionSource) - && mManagerService != null - && mManagerService.onFactoryReset(mAttributionSource)) { - return true; - } - Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later"); - BluetoothProperties.factory_reset(true); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Get the UUIDs supported by the local Bluetooth adapter. - * - * @return the UUIDs supported by the local Bluetooth Adapter. - * @hide - */ - @UnsupportedAppUsage - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @Nullable ParcelUuid[] getUuids() { - if (getState() != STATE_ON) { - return null; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getUuids(mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return null; - } - - /** - * Set the friendly Bluetooth name of the local Bluetooth adapter. - * <p>This name is visible to remote Bluetooth devices. - * <p>Valid Bluetooth names are a maximum of 248 bytes using UTF-8 - * encoding, although many remote devices can only display the first - * 40 characters, and some may be limited to just 20. - * <p>If Bluetooth state is not {@link #STATE_ON}, this API - * will return false. After turning on Bluetooth, - * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} - * to get the updated value. - * - * @param name a valid Bluetooth name - * @return true if the name was set, false otherwise - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setName(String name) { - if (getState() != STATE_ON) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.setName(name, mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Returns the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth - * adapter. - * - * @return {@link BluetoothClass} Bluetooth CoD of local Bluetooth device. - * - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothClass getBluetoothClass() { - if (getState() != STATE_ON) { - return null; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getBluetoothClass(mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return null; - } - - /** - * Sets the {@link BluetoothClass} Bluetooth Class of Device (CoD) of the local Bluetooth - * adapter. - * - * <p>Note: This value persists across system reboot. - * - * @param bluetoothClass {@link BluetoothClass} to set the local Bluetooth adapter to. - * @return true if successful, false if unsuccessful. - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setBluetoothClass(BluetoothClass bluetoothClass) { - if (getState() != STATE_ON) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.setBluetoothClass(bluetoothClass, mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Returns the Input/Output capability of the device for classic Bluetooth. - * - * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT}, - * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE}, - * {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}. - * - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @IoCapability - public int getIoCapability() { - if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; - try { - mServiceLock.readLock().lock(); - if (mService != null) return mService.getIoCapability(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.getMessage(), e); - } finally { - mServiceLock.readLock().unlock(); - } - return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; - } - - /** - * Sets the Input/Output capability of the device for classic Bluetooth. - * - * <p>Changing the Input/Output capability of a device only takes effect on restarting the - * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()} - * and {@link BluetoothAdapter#enable()} to see the changes. - * - * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT}, - * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, - * {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}. - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setIoCapability(@IoCapability int capability) { - if (getState() != STATE_ON) return false; - try { - mServiceLock.readLock().lock(); - if (mService != null) return mService.setIoCapability(capability, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.getMessage(), e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Returns the Input/Output capability of the device for BLE operations. - * - * @return Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT}, - * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, {@link #IO_CAPABILITY_NONE}, - * {@link #IO_CAPABILITY_KBDISP} or {@link #IO_CAPABILITY_UNKNOWN}. - * - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @IoCapability - public int getLeIoCapability() { - if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; - try { - mServiceLock.readLock().lock(); - if (mService != null) return mService.getLeIoCapability(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.getMessage(), e); - } finally { - mServiceLock.readLock().unlock(); - } - return BluetoothAdapter.IO_CAPABILITY_UNKNOWN; - } - - /** - * Sets the Input/Output capability of the device for BLE operations. - * - * <p>Changing the Input/Output capability of a device only takes effect on restarting the - * Bluetooth stack. You would need to restart the stack using {@link BluetoothAdapter#disable()} - * and {@link BluetoothAdapter#enable()} to see the changes. - * - * @param capability Input/Output capability of the device. One of {@link #IO_CAPABILITY_OUT}, - * {@link #IO_CAPABILITY_IO}, {@link #IO_CAPABILITY_IN}, - * {@link #IO_CAPABILITY_NONE} or {@link #IO_CAPABILITY_KBDISP}. - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setLeIoCapability(@IoCapability int capability) { - if (getState() != STATE_ON) return false; - try { - mServiceLock.readLock().lock(); - if (mService != null) return mService.setLeIoCapability(capability, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.getMessage(), e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Get the current Bluetooth scan mode of the local Bluetooth adapter. - * <p>The Bluetooth scan mode determines if the local adapter is - * connectable and/or discoverable from remote Bluetooth devices. - * <p>Possible values are: - * {@link #SCAN_MODE_NONE}, - * {@link #SCAN_MODE_CONNECTABLE}, - * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. - * <p>If Bluetooth state is not {@link #STATE_ON}, this API - * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth, - * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} - * to get the updated value. - * - * @return scan mode - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - @ScanMode - public int getScanMode() { - if (getState() != STATE_ON) { - return SCAN_MODE_NONE; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getScanMode(mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return SCAN_MODE_NONE; - } - - /** - * Set the Bluetooth scan mode of the local Bluetooth adapter. - * <p>The Bluetooth scan mode determines if the local adapter is - * connectable and/or discoverable from remote Bluetooth devices. - * <p>For privacy reasons, discoverable mode is automatically turned off - * after <code>durationMillis</code> milliseconds. For example, 120000 milliseconds should be - * enough for a remote device to initiate and complete its discovery process. - * <p>Valid scan mode values are: - * {@link #SCAN_MODE_NONE}, - * {@link #SCAN_MODE_CONNECTABLE}, - * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. - * <p>If Bluetooth state is not {@link #STATE_ON}, this API - * will return false. After turning on Bluetooth, - * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} - * to get the updated value. - * <p>Applications cannot set the scan mode. They should use - * <code>startActivityForResult( - * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE}) - * </code>instead. - * - * @param mode valid scan mode - * @param durationMillis time in milliseconds to apply scan mode, only used for {@link - * #SCAN_MODE_CONNECTABLE_DISCOVERABLE} - * @return true if the scan mode was set, false otherwise - * @hide - */ - @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which " - + "shows UI that confirms the user wants to go into discoverable mode.") - @RequiresLegacyBluetoothPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public boolean setScanMode(@ScanMode int mode, long durationMillis) { - if (getState() != STATE_ON) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - int durationSeconds = Math.toIntExact(durationMillis / 1000); - return mService.setScanMode(mode, durationSeconds, mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } catch (ArithmeticException ex) { - Log.e(TAG, "setScanMode: Duration in seconds outside of the bounds of an int"); - throw new IllegalArgumentException("Duration not in bounds. In seconds, the " - + "durationMillis must be in the range of an int"); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Set the Bluetooth scan mode of the local Bluetooth adapter. - * <p>The Bluetooth scan mode determines if the local adapter is - * connectable and/or discoverable from remote Bluetooth devices. - * <p>For privacy reasons, discoverable mode is automatically turned off - * after <code>duration</code> seconds. For example, 120 seconds should be - * enough for a remote device to initiate and complete its discovery - * process. - * <p>Valid scan mode values are: - * {@link #SCAN_MODE_NONE}, - * {@link #SCAN_MODE_CONNECTABLE}, - * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. - * <p>If Bluetooth state is not {@link #STATE_ON}, this API - * will return false. After turning on Bluetooth, - * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} - * to get the updated value. - * <p>Applications cannot set the scan mode. They should use - * <code>startActivityForResult( - * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE}) - * </code>instead. - * - * @param mode valid scan mode - * @return true if the scan mode was set, false otherwise - * @hide - */ - @UnsupportedAppUsage - @RequiresLegacyBluetoothPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public boolean setScanMode(@ScanMode int mode) { - if (getState() != STATE_ON) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.setScanMode(mode, getDiscoverableTimeout(), mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** @hide */ - @UnsupportedAppUsage - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public int getDiscoverableTimeout() { - if (getState() != STATE_ON) { - return -1; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getDiscoverableTimeout(mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return -1; - } - - /** @hide */ - @UnsupportedAppUsage - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void setDiscoverableTimeout(int timeout) { - if (getState() != STATE_ON) { - return; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - mService.setDiscoverableTimeout(timeout, mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - } - - /** - * Get the end time of the latest remote device discovery process. - * - * @return the latest time that the bluetooth adapter was/will be in discovery mode, in - * milliseconds since the epoch. This time can be in the future if {@link #startDiscovery()} has - * been called recently. - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public long getDiscoveryEndMillis() { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getDiscoveryEndMillis(mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return -1; - } - - /** - * Start the remote device discovery process. - * <p>The discovery process usually involves an inquiry scan of about 12 - * seconds, followed by a page scan of each new device to retrieve its - * Bluetooth name. - * <p>This is an asynchronous call, it will return immediately. Register - * for {@link #ACTION_DISCOVERY_STARTED} and {@link - * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the - * discovery starts and completes. Register for {@link - * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices - * are found. - * <p>Device discovery is a heavyweight procedure. New connections to - * remote Bluetooth devices should not be attempted while discovery is in - * progress, and existing connections will experience limited bandwidth - * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing - * discovery. Discovery is not managed by the Activity, - * but is run as a system service, so an application should always call - * {@link BluetoothAdapter#cancelDiscovery()} even if it - * did not directly request a discovery, just to be sure. - * <p>Device discovery will only find remote devices that are currently - * <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are - * not discoverable by default, and need to be entered into a special mode. - * <p>If Bluetooth state is not {@link #STATE_ON}, this API - * will return false. After turning on Bluetooth, wait for {@link #ACTION_STATE_CHANGED} - * with {@link #STATE_ON} to get the updated value. - * <p>If a device is currently bonding, this request will be queued and executed once that - * device has finished bonding. If a request is already queued, this request will be ignored. - * - * @return true on success, false on error - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresBluetoothLocationPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public boolean startDiscovery() { - if (getState() != STATE_ON) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.startDiscovery(mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Cancel the current device discovery process. - * <p>Because discovery is a heavyweight procedure for the Bluetooth - * adapter, this method should always be called before attempting to connect - * to a remote device with {@link - * android.bluetooth.BluetoothSocket#connect()}. Discovery is not managed by - * the Activity, but is run as a system service, so an application should - * always call cancel discovery even if it did not directly request a - * discovery, just to be sure. - * <p>If Bluetooth state is not {@link #STATE_ON}, this API - * will return false. After turning on Bluetooth, - * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} - * to get the updated value. - * - * @return true on success, false on error - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public boolean cancelDiscovery() { - if (getState() != STATE_ON) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.cancelDiscovery(mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Return true if the local Bluetooth adapter is currently in the device - * discovery process. - * <p>Device discovery is a heavyweight procedure. New connections to - * remote Bluetooth devices should not be attempted while discovery is in - * progress, and existing connections will experience limited bandwidth - * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing - * discovery. - * <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED} - * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery - * starts or completes. - * <p>If Bluetooth state is not {@link #STATE_ON}, this API - * will return false. After turning on Bluetooth, - * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} - * to get the updated value. - * - * @return true if discovering - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public boolean isDiscovering() { - if (getState() != STATE_ON) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isDiscovering(mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Removes the active device for the grouping of @ActiveDeviceUse specified - * - * @param profiles represents the purpose for which we are setting this as the active device. - * Possible values are: - * {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO}, - * {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL}, - * {@link BluetoothAdapter#ACTIVE_DEVICE_ALL} - * @return false on immediate error, true otherwise - * @throws IllegalArgumentException if device is null or profiles is not one of - * {@link ActiveDeviceUse} - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public boolean removeActiveDevice(@ActiveDeviceUse int profiles) { - if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL - && profiles != ACTIVE_DEVICE_ALL) { - Log.e(TAG, "Invalid profiles param value in removeActiveDevice"); - throw new IllegalArgumentException("Profiles must be one of " - + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, " - + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or " - + "BluetoothAdapter.ACTIVE_DEVICE_ALL"); - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles); - return mService.removeActiveDevice(profiles, mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - - return false; - } - - /** - * Sets device as the active devices for the profiles passed into the function - * - * @param device is the remote bluetooth device - * @param profiles represents the purpose for which we are setting this as the active device. - * Possible values are: - * {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO}, - * {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL}, - * {@link BluetoothAdapter#ACTIVE_DEVICE_ALL} - * @return false on immediate error, true otherwise - * @throws IllegalArgumentException if device is null or profiles is not one of - * {@link ActiveDeviceUse} - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public boolean setActiveDevice(@NonNull BluetoothDevice device, - @ActiveDeviceUse int profiles) { - if (device == null) { - Log.e(TAG, "setActiveDevice: Null device passed as parameter"); - throw new IllegalArgumentException("device cannot be null"); - } - if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL - && profiles != ACTIVE_DEVICE_ALL) { - Log.e(TAG, "Invalid profiles param value in setActiveDevice"); - throw new IllegalArgumentException("Profiles must be one of " - + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, " - + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or " - + "BluetoothAdapter.ACTIVE_DEVICE_ALL"); - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - if (DBG) { - Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles); - } - return mService.setActiveDevice(device, profiles, mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - - return false; - } - - /** - * Get the active devices for the BluetoothProfile specified - * - * @param profile is the profile from which we want the active devices. - * Possible values are: - * {@link BluetoothProfile#HEADSET}, - * {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#HEARING_AID} - * {@link BluetoothProfile#LE_AUDIO} - * @return A list of active bluetooth devices - * @throws IllegalArgumentException If profile is not one of {@link ActiveDeviceProfile} - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @NonNull List<BluetoothDevice> getActiveDevices(@ActiveDeviceProfile int profile) { - if (profile != BluetoothProfile.HEADSET - && profile != BluetoothProfile.A2DP - && profile != BluetoothProfile.HEARING_AID - && profile != BluetoothProfile.LE_AUDIO) { - Log.e(TAG, "Invalid profile param value in getActiveDevices"); - throw new IllegalArgumentException("Profiles must be one of " - + "BluetoothProfile.A2DP, " - + "BluetoothProfile.HEARING_AID, or" - + "BluetoothProfile.HEARING_AID" - + "BluetoothProfile.LE_AUDIO"); - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - if (DBG) { - Log.d(TAG, "getActiveDevices(profile= " - + BluetoothProfile.getProfileName(profile) + ")"); - } - return mService.getActiveDevices(profile, mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - - return new ArrayList<>(); - } - - /** - * Return true if the multi advertisement is supported by the chipset - * - * @return true if Multiple Advertisement feature is supported - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public boolean isMultipleAdvertisementSupported() { - if (getState() != STATE_ON) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isMultiAdvertisementSupported(); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Returns {@code true} if BLE scan is always available, {@code false} otherwise. <p> - * - * If this returns {@code true}, application can issue {@link BluetoothLeScanner#startScan} and - * fetch scan results even when Bluetooth is turned off.<p> - * - * To change this setting, use {@link #ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE}. - * - * @hide - */ - @SystemApi - @RequiresNoPermission - public boolean isBleScanAlwaysAvailable() { - try { - return mManagerService.isBleScanAlwaysAvailable(); - } catch (RemoteException e) { - Log.e(TAG, "remote exception when calling isBleScanAlwaysAvailable", e); - return false; - } - } - - private static final String BLUETOOTH_FILTERING_CACHE_PROPERTY = - "cache_key.bluetooth.is_offloaded_filtering_supported"; - private final PropertyInvalidatedCache<Void, Boolean> mBluetoothFilteringCache = - new PropertyInvalidatedCache<Void, Boolean>( - 8, BLUETOOTH_FILTERING_CACHE_PROPERTY) { - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - protected Boolean recompute(Void query) { - 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; - - } - }; - - /** @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 - * - * @return true if chipset supports on-chip filtering - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public boolean isOffloadedFilteringSupported() { - if (!getLeAccess()) { - return false; - } - return mBluetoothFilteringCache.query(null); - } - - /** - * Return true if offloaded scan batching is supported - * - * @return true if chipset supports on-chip scan batching - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public boolean isOffloadedScanBatchingSupported() { - if (!getLeAccess()) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isOffloadedScanBatchingSupported(); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Return true if LE 2M PHY feature is supported. - * - * @return true if chipset supports LE 2M PHY feature - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public boolean isLe2MPhySupported() { - if (!getLeAccess()) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isLe2MPhySupported(); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to get isExtendedAdvertisingSupported, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Return true if LE Coded PHY feature is supported. - * - * @return true if chipset supports LE Coded PHY feature - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public boolean isLeCodedPhySupported() { - if (!getLeAccess()) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isLeCodedPhySupported(); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to get isLeCodedPhySupported, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Return true if LE Extended Advertising feature is supported. - * - * @return true if chipset supports LE Extended Advertising feature - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public boolean isLeExtendedAdvertisingSupported() { - if (!getLeAccess()) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isLeExtendedAdvertisingSupported(); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to get isLeExtendedAdvertisingSupported, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** - * Return true if LE Periodic Advertising feature is supported. - * - * @return true if chipset supports LE Periodic Advertising feature - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public boolean isLePeriodicAdvertisingSupported() { - if (!getLeAccess()) { - return false; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isLePeriodicAdvertisingSupported(); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to get isLePeriodicAdvertisingSupported, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return false; - } - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - BluetoothStatusCodes.SUCCESS, - BluetoothStatusCodes.ERROR_UNKNOWN, - BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, - BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED, - }) - public @interface LeFeatureReturnValues {} - - /** - * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio feature is - * supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if - * the feature is not supported or an error code. - * - * @return whether the LE audio is supported - */ - @RequiresNoPermission - public @LeFeatureReturnValues int isLeAudioSupported() { - if (!getLeAccess()) { - return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isLeAudioSupported(); - } - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } finally { - mServiceLock.readLock().unlock(); - } - return BluetoothStatusCodes.ERROR_UNKNOWN; - } - - /** - * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Periodic Advertising Sync Transfer Sender - * feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the - * feature is not supported or an error code - * - * @return whether the chipset supports the LE Periodic Advertising Sync Transfer Sender feature - */ - @RequiresNoPermission - public @LeFeatureReturnValues int isLePeriodicAdvertisingSyncTransferSenderSupported() { - if (!getLeAccess()) { - return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.isLePeriodicAdvertisingSyncTransferSenderSupported(); - } - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } finally { - mServiceLock.readLock().unlock(); - } - return BluetoothStatusCodes.ERROR_UNKNOWN; - } - - /** - * Return the maximum LE advertising data length in bytes, - * if LE Extended Advertising feature is supported, 0 otherwise. - * - * @return the maximum LE advertising data length. - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public int getLeMaximumAdvertisingDataLength() { - if (!getLeAccess()) { - return 0; - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getLeMaximumAdvertisingDataLength(); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to get getLeMaximumAdvertisingDataLength, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return 0; - } - - /** - * Return true if Hearing Aid Profile is supported. - * - * @return true if phone supports Hearing Aid Profile - */ - @RequiresNoPermission - private boolean isHearingAidProfileSupported() { - try { - return mManagerService.isHearingAidProfileSupported(); - } catch (RemoteException e) { - Log.e(TAG, "remote exception when calling isHearingAidProfileSupported", e); - return false; - } - } - - /** - * Get the maximum number of connected audio devices. - * - * @return the maximum number of connected audio devices - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getMaxConnectedAudioDevices() { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getMaxConnectedAudioDevices(mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e); - } finally { - mServiceLock.readLock().unlock(); - } - return 1; - } - - /** - * Return true if hardware has entries available for matching beacons - * - * @return true if there are hw entries available for matching beacons - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isHardwareTrackingFiltersAvailable() { - if (!getLeAccess()) { - return false; - } - try { - IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); - if (iGatt == null) { - // BLE is not supported - return false; - } - return (iGatt.numHwTrackFiltersAvailable(mAttributionSource) != 0); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Request the record of {@link BluetoothActivityEnergyInfo} object that - * has the activity and energy info. This can be used to ascertain what - * the controller has been up to, since the last sample. - * - * A null value for the activity info object may be sent if the bluetooth service is - * unreachable or the device does not support reporting such information. - * - * @param result The callback to which to send the activity info. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public void requestControllerActivityEnergyInfo(ResultReceiver result) { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - mService.requestActivityInfo(result, mAttributionSource); - result = null; - } - } catch (RemoteException e) { - Log.e(TAG, "getControllerActivityEnergyInfoCallback: " + e); - } finally { - mServiceLock.readLock().unlock(); - if (result != null) { - // Only send an immediate result if we failed. - result.send(0, null); - } - } - } - - /** - * Fetches a list of the most recently connected bluetooth devices ordered by how recently they - * were connected with most recently first and least recently last - * - * @return {@link List} of bonded {@link BluetoothDevice} ordered by how recently they were - * connected - * - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @NonNull List<BluetoothDevice> getMostRecentlyConnectedDevices() { - if (getState() != STATE_ON) { - return new ArrayList<>(); - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return Attributable.setAttributionSource( - mService.getMostRecentlyConnectedDevices(mAttributionSource), - mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return new ArrayList<>(); - } - - /** - * Return the set of {@link BluetoothDevice} objects that are bonded - * (paired) to the local adapter. - * <p>If Bluetooth state is not {@link #STATE_ON}, this API - * will return an empty set. After turning on Bluetooth, - * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} - * to get the updated value. - * - * @return unmodifiable set of {@link BluetoothDevice}, or null on error - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public Set<BluetoothDevice> getBondedDevices() { - if (getState() != STATE_ON) { - return toDeviceSet(Arrays.asList()); - } - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return toDeviceSet(Attributable.setAttributionSource( - Arrays.asList(mService.getBondedDevices(mAttributionSource)), - mAttributionSource)); - } - return toDeviceSet(Arrays.asList()); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - return null; - } - - /** - * Gets the currently supported profiles by the adapter. - * - * <p> This can be used to check whether a profile is supported before attempting - * to connect to its respective proxy. - * - * @return a list of integers indicating the ids of supported profiles as defined in {@link - * BluetoothProfile}. - * @hide - */ - @RequiresNoPermission - public @NonNull List<Integer> getSupportedProfiles() { - final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>(); - - try { - synchronized (mManagerCallback) { - if (mService != null) { - final long supportedProfilesBitMask = mService.getSupportedProfiles(); - - for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) { - if ((supportedProfilesBitMask & (1 << i)) != 0) { - supportedProfiles.add(i); - } - } - } else { - // Bluetooth is disabled. Just fill in known supported Profiles - if (isHearingAidProfileSupported()) { - supportedProfiles.add(BluetoothProfile.HEARING_AID); - } - } - } - } catch (RemoteException e) { - Log.e(TAG, "getSupportedProfiles:", e); - } - 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) { - try { - return mService.getAdapterConnectionState(); - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); - } - } - }; - - /** @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. - * This can be used to check whether the local Bluetooth adapter is connected - * to any profile of any other remote Bluetooth Device. - * - * <p> Use this function along with {@link #ACTION_CONNECTION_STATE_CHANGED} - * intent to get the connection state of the adapter. - * - * @return One of {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTED}, {@link - * #STATE_CONNECTING} or {@link #STATE_DISCONNECTED} - * @hide - */ - @UnsupportedAppUsage - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public int getConnectionState() { - if (getState() != STATE_ON) { - return BluetoothAdapter.STATE_DISCONNECTED; - } - 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; - } - } 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> - mGetProfileConnectionStateCache = - new PropertyInvalidatedCache<Integer, Integer>( - 8, BLUETOOTH_PROFILE_CACHE_PROPERTY) { - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - protected Integer recompute(Integer query) { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getProfileConnectionState(query); - } - } catch (RemoteException e) { - Log.e(TAG, "getProfileConnectionState:", e); - } finally { - mServiceLock.readLock().unlock(); - } - return BluetoothProfile.STATE_DISCONNECTED; - } - @Override - public String queryToString(Integer query) { - return String.format("getProfileConnectionState(profile=\"%d\")", - 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. - * This function can be used to check whether the local Bluetooth adapter - * is connected to any remote device for a specific profile. - * Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. - * - * <p> Return value can be one of - * {@link BluetoothProfile#STATE_DISCONNECTED}, - * {@link BluetoothProfile#STATE_CONNECTING}, - * {@link BluetoothProfile#STATE_CONNECTED}, - * {@link BluetoothProfile#STATE_DISCONNECTING} - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public int getProfileConnectionState(int profile) { - if (getState() != STATE_ON) { - return BluetoothProfile.STATE_DISCONNECTED; - } - return mGetProfileConnectionStateCache.query(new Integer(profile)); - } - - /** - * Create a listening, secure RFCOMM Bluetooth socket. - * <p>A remote device connecting to this socket will be authenticated and - * communication on this socket will be encrypted. - * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming - * connections from a listening {@link BluetoothServerSocket}. - * <p>Valid RFCOMM channels are in range 1 to 30. - * - * @param channel RFCOMM channel to listen on - * @return a listening RFCOMM BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions, or channel in use. - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { - return listenUsingRfcommOn(channel, false, false); - } - - /** - * Create a listening, secure RFCOMM Bluetooth socket. - * <p>A remote device connecting to this socket will be authenticated and - * communication on this socket will be encrypted. - * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming - * connections from a listening {@link BluetoothServerSocket}. - * <p>Valid RFCOMM channels are in range 1 to 30. - * <p>To auto assign a channel without creating a SDP record use - * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number. - * - * @param channel RFCOMM channel to listen on - * @param mitm enforce person-in-the-middle protection for authentication. - * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 - * connections. - * @return a listening RFCOMM BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions, or channel in use. - * @hide - */ - @UnsupportedAppUsage - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm, - boolean min16DigitPin) throws IOException { - BluetoothServerSocket socket = - new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm, - min16DigitPin); - int errno = socket.mSocket.bindListen(); - if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - socket.setChannel(socket.mSocket.getPort()); - } - if (errno != 0) { - //TODO(BT): Throw the same exception error code - // that the previous code was using. - //socket.mSocket.throwErrnoNative(errno); - throw new IOException("Error: " + errno); - } - return socket; - } - - /** - * Create a listening, secure RFCOMM Bluetooth socket with Service Record. - * <p>A remote device connecting to this socket will be authenticated and - * communication on this socket will be encrypted. - * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming - * connections from a listening {@link BluetoothServerSocket}. - * <p>The system will assign an unused RFCOMM channel to listen on. - * <p>The system will also register a Service Discovery - * Protocol (SDP) record with the local SDP server containing the specified - * UUID, service name, and auto-assigned channel. Remote Bluetooth devices - * can use the same UUID to query our SDP server and discover which channel - * to connect to. This SDP record will be removed when this socket is - * closed, or if this application closes unexpectedly. - * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to - * connect to this socket from another device using the same {@link UUID}. - * - * @param name service name for SDP record - * @param uuid uuid for SDP record - * @return a listening RFCOMM BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions, or channel in use. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) - throws IOException { - return createNewRfcommSocketAndRecord(name, uuid, true, true); - } - - /** - * Create a listening, insecure RFCOMM Bluetooth socket with Service Record. - * <p>The link key is not required to be authenticated, i.e the communication may be - * vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices, - * the link will be encrypted, as encryption is mandatory. - * For legacy devices (pre Bluetooth 2.1 devices) the link will not - * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an - * encrypted and authenticated communication channel is desired. - * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming - * connections from a listening {@link BluetoothServerSocket}. - * <p>The system will assign an unused RFCOMM channel to listen on. - * <p>The system will also register a Service Discovery - * Protocol (SDP) record with the local SDP server containing the specified - * UUID, service name, and auto-assigned channel. Remote Bluetooth devices - * can use the same UUID to query our SDP server and discover which channel - * to connect to. This SDP record will be removed when this socket is - * closed, or if this application closes unexpectedly. - * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to - * connect to this socket from another device using the same {@link UUID}. - * - * @param name service name for SDP record - * @param uuid uuid for SDP record - * @return a listening RFCOMM BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions, or channel in use. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) - throws IOException { - return createNewRfcommSocketAndRecord(name, uuid, false, false); - } - - /** - * Create a listening, encrypted, - * RFCOMM Bluetooth socket with Service Record. - * <p>The link will be encrypted, but the link key is not required to be authenticated - * i.e the communication is vulnerable to Person In the Middle attacks. Use - * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key. - * <p> Use this socket if authentication of link key is not possible. - * For example, for Bluetooth 2.1 devices, if any of the devices does not have - * an input and output capability or just has the ability to display a numeric key, - * a secure socket connection is not possible and this socket can be used. - * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required. - * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandatory. - * For more details, refer to the Security Model section 5.2 (vol 3) of - * Bluetooth Core Specification version 2.1 + EDR. - * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming - * connections from a listening {@link BluetoothServerSocket}. - * <p>The system will assign an unused RFCOMM channel to listen on. - * <p>The system will also register a Service Discovery - * Protocol (SDP) record with the local SDP server containing the specified - * UUID, service name, and auto-assigned channel. Remote Bluetooth devices - * can use the same UUID to query our SDP server and discover which channel - * to connect to. This SDP record will be removed when this socket is - * closed, or if this application closes unexpectedly. - * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to - * connect to this socket from another device using the same {@link UUID}. - * - * @param name service name for SDP record - * @param uuid uuid for SDP record - * @return a listening RFCOMM BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions, or channel in use. - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid) - throws IOException { - return createNewRfcommSocketAndRecord(name, uuid, false, true); - } - - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid, - boolean auth, boolean encrypt) throws IOException { - BluetoothServerSocket socket; - socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth, encrypt, - new ParcelUuid(uuid)); - socket.setServiceName(name); - int errno = socket.mSocket.bindListen(); - if (errno != 0) { - //TODO(BT): Throw the same exception error code - // that the previous code was using. - //socket.mSocket.throwErrnoNative(errno); - throw new IOException("Error: " + errno); - } - return socket; - } - - /** - * Construct an unencrypted, unauthenticated, RFCOMM server socket. - * Call #accept to retrieve connections to this socket. - * - * @return An RFCOMM BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or insufficient - * permissions. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { - BluetoothServerSocket socket = - new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port); - int errno = socket.mSocket.bindListen(); - if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - socket.setChannel(socket.mSocket.getPort()); - } - if (errno != 0) { - //TODO(BT): Throw the same exception error code - // that the previous code was using. - //socket.mSocket.throwErrnoNative(errno); - throw new IOException("Error: " + errno); - } - return socket; - } - - /** - * Construct an encrypted, authenticated, L2CAP server socket. - * Call #accept to retrieve connections to this socket. - * <p>To auto assign a port without creating a SDP record use - * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. - * - * @param port the PSM to listen on - * @param mitm enforce person-in-the-middle protection for authentication. - * @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2 - * connections. - * @return An L2CAP BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or insufficient - * permissions. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin) - throws IOException { - BluetoothServerSocket socket = - new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, true, true, port, mitm, - min16DigitPin); - int errno = socket.mSocket.bindListen(); - if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - int assignedChannel = socket.mSocket.getPort(); - if (DBG) Log.d(TAG, "listenUsingL2capOn: set assigned channel to " + assignedChannel); - socket.setChannel(assignedChannel); - } - if (errno != 0) { - //TODO(BT): Throw the same exception error code - // that the previous code was using. - //socket.mSocket.throwErrnoNative(errno); - throw new IOException("Error: " + errno); - } - return socket; - } - - /** - * Construct an encrypted, authenticated, L2CAP server socket. - * Call #accept to retrieve connections to this socket. - * <p>To auto assign a port without creating a SDP record use - * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. - * - * @param port the PSM to listen on - * @return An L2CAP BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or insufficient - * permissions. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { - return listenUsingL2capOn(port, false, false); - } - - /** - * Construct an insecure L2CAP server socket. - * Call #accept to retrieve connections to this socket. - * <p>To auto assign a port without creating a SDP record use - * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. - * - * @param port the PSM to listen on - * @return An L2CAP BluetoothServerSocket - * @throws IOException On error, for example Bluetooth not available, or insufficient - * permissions. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { - Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port); - BluetoothServerSocket socket = - new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false, - false); - int errno = socket.mSocket.bindListen(); - if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - int assignedChannel = socket.mSocket.getPort(); - if (DBG) { - Log.d(TAG, "listenUsingInsecureL2capOn: set assigned channel to " - + assignedChannel); - } - socket.setChannel(assignedChannel); - } - if (errno != 0) { - //TODO(BT): Throw the same exception error code - // that the previous code was using. - //socket.mSocket.throwErrnoNative(errno); - throw new IOException("Error: " + errno); - } - return socket; - - } - - /** - * Read the local Out of Band Pairing Data - * - * @return Pair<byte[], byte[]> of Hash and Randomizer - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public Pair<byte[], byte[]> readOutOfBandData() { - return null; - } - - /** - * Get the profile proxy object associated with the profile. - * - * <p>Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#GATT}, {@link BluetoothProfile#HEARING_AID}, or {@link - * BluetoothProfile#GATT_SERVER}. Clients must implement {@link - * BluetoothProfile.ServiceListener} to get notified of the connection status and to get the - * proxy object. - * - * @param context Context of the application - * @param listener The service Listener for connection callbacks. - * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET}, - * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, {@link - * BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#GATT_SERVER}. - * @return true on success, false on error - */ - @SuppressLint({ - "AndroidFrameworkRequiresPermission", - "AndroidFrameworkBluetoothPermission" - }) - public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, - int profile) { - if (context == null || listener == null) { - return false; - } - - if (profile == BluetoothProfile.HEADSET) { - BluetoothHeadset headset = new BluetoothHeadset(context, listener, this); - return true; - } else if (profile == BluetoothProfile.A2DP) { - BluetoothA2dp a2dp = new BluetoothA2dp(context, listener, this); - return true; - } else if (profile == BluetoothProfile.A2DP_SINK) { - BluetoothA2dpSink a2dpSink = new BluetoothA2dpSink(context, listener, this); - return true; - } else if (profile == BluetoothProfile.AVRCP_CONTROLLER) { - BluetoothAvrcpController avrcp = new BluetoothAvrcpController(context, listener, this); - return true; - } else if (profile == BluetoothProfile.HID_HOST) { - BluetoothHidHost iDev = new BluetoothHidHost(context, listener, this); - return true; - } else if (profile == BluetoothProfile.PAN) { - BluetoothPan pan = new BluetoothPan(context, listener, this); - return true; - } else if (profile == BluetoothProfile.PBAP) { - BluetoothPbap pbap = new BluetoothPbap(context, listener, this); - return true; - } else if (profile == BluetoothProfile.HEALTH) { - Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated"); - return false; - } else if (profile == BluetoothProfile.MAP) { - BluetoothMap map = new BluetoothMap(context, listener, this); - return true; - } else if (profile == BluetoothProfile.HEADSET_CLIENT) { - BluetoothHeadsetClient headsetClient = - new BluetoothHeadsetClient(context, listener, this); - return true; - } else if (profile == BluetoothProfile.SAP) { - BluetoothSap sap = new BluetoothSap(context, listener, this); - return true; - } else if (profile == BluetoothProfile.PBAP_CLIENT) { - BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener, this); - return true; - } else if (profile == BluetoothProfile.MAP_CLIENT) { - BluetoothMapClient mapClient = new BluetoothMapClient(context, listener, this); - return true; - } else if (profile == BluetoothProfile.HID_DEVICE) { - BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this); - return true; - } else if (profile == BluetoothProfile.HEARING_AID) { - if (isHearingAidProfileSupported()) { - BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this); - return true; - } - return false; - } else if (profile == BluetoothProfile.LE_AUDIO) { - BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this); - return true; - } else if (profile == BluetoothProfile.VOLUME_CONTROL) { - BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this); - return true; - } else if (profile == BluetoothProfile.CSIP_SET_COORDINATOR) { - BluetoothCsipSetCoordinator csipSetCoordinator = - new BluetoothCsipSetCoordinator(context, listener, this); - return true; - } else { - return false; - } - } - - /** - * Close the connection of the profile proxy to the Service. - * - * <p> Clients should call this when they are no longer using - * the proxy obtained from {@link #getProfileProxy}. - * Profile can be one of {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP} - * - * @param profile - * @param proxy Profile proxy object - */ - @SuppressLint({ - "AndroidFrameworkRequiresPermission", - "AndroidFrameworkBluetoothPermission" - }) - public void closeProfileProxy(int profile, BluetoothProfile proxy) { - if (proxy == null) { - return; - } - - switch (profile) { - case BluetoothProfile.HEADSET: - BluetoothHeadset headset = (BluetoothHeadset) proxy; - headset.close(); - break; - case BluetoothProfile.A2DP: - BluetoothA2dp a2dp = (BluetoothA2dp) proxy; - a2dp.close(); - break; - case BluetoothProfile.A2DP_SINK: - BluetoothA2dpSink a2dpSink = (BluetoothA2dpSink) proxy; - a2dpSink.close(); - break; - case BluetoothProfile.AVRCP_CONTROLLER: - BluetoothAvrcpController avrcp = (BluetoothAvrcpController) proxy; - avrcp.close(); - break; - case BluetoothProfile.HID_HOST: - BluetoothHidHost iDev = (BluetoothHidHost) proxy; - iDev.close(); - break; - case BluetoothProfile.PAN: - BluetoothPan pan = (BluetoothPan) proxy; - pan.close(); - break; - case BluetoothProfile.PBAP: - BluetoothPbap pbap = (BluetoothPbap) proxy; - pbap.close(); - break; - case BluetoothProfile.GATT: - BluetoothGatt gatt = (BluetoothGatt) proxy; - gatt.close(); - break; - case BluetoothProfile.GATT_SERVER: - BluetoothGattServer gattServer = (BluetoothGattServer) proxy; - gattServer.close(); - break; - case BluetoothProfile.MAP: - BluetoothMap map = (BluetoothMap) proxy; - map.close(); - break; - case BluetoothProfile.HEADSET_CLIENT: - BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient) proxy; - headsetClient.close(); - break; - case BluetoothProfile.SAP: - BluetoothSap sap = (BluetoothSap) proxy; - sap.close(); - break; - case BluetoothProfile.PBAP_CLIENT: - BluetoothPbapClient pbapClient = (BluetoothPbapClient) proxy; - pbapClient.close(); - break; - case BluetoothProfile.MAP_CLIENT: - BluetoothMapClient mapClient = (BluetoothMapClient) proxy; - mapClient.close(); - break; - case BluetoothProfile.HID_DEVICE: - BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy; - hidDevice.close(); - break; - case BluetoothProfile.HEARING_AID: - BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; - hearingAid.close(); - break; - case BluetoothProfile.LE_AUDIO: - BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy; - leAudio.close(); - break; - case BluetoothProfile.VOLUME_CONTROL: - BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy; - vcs.close(); - break; - case BluetoothProfile.CSIP_SET_COORDINATOR: - BluetoothCsipSetCoordinator csipSetCoordinator = - (BluetoothCsipSetCoordinator) proxy; - csipSetCoordinator.close(); - break; - } - } - - private static final IBluetoothManagerCallback sManagerCallback = - new IBluetoothManagerCallback.Stub() { - public void onBluetoothServiceUp(IBluetooth bluetoothService) { - if (DBG) { - Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); - } - - synchronized (sServiceLock) { - sService = bluetoothService; - for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { - try { - if (cb != null) { - cb.onBluetoothServiceUp(bluetoothService); - } else { - Log.d(TAG, "onBluetoothServiceUp: cb is null!"); - } - } catch (Exception e) { - Log.e(TAG, "", e); - } - } - } - } - - public void onBluetoothServiceDown() { - if (DBG) { - Log.d(TAG, "onBluetoothServiceDown"); - } - - synchronized (sServiceLock) { - sService = null; - for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { - try { - if (cb != null) { - cb.onBluetoothServiceDown(); - } else { - Log.d(TAG, "onBluetoothServiceDown: cb is null!"); - } - } catch (Exception e) { - Log.e(TAG, "", e); - } - } - } - } - - public void onBrEdrDown() { - if (VDBG) { - Log.i(TAG, "onBrEdrDown"); - } - - synchronized (sServiceLock) { - for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) { - try { - if (cb != null) { - cb.onBrEdrDown(); - } else { - Log.d(TAG, "onBrEdrDown: cb is null!"); - } - } catch (Exception e) { - Log.e(TAG, "", e); - } - } - } - } - }; - - private final IBluetoothManagerCallback mManagerCallback = - new IBluetoothManagerCallback.Stub() { - public void onBluetoothServiceUp(IBluetooth bluetoothService) { - synchronized (mServiceLock.writeLock()) { - mService = bluetoothService; - } - synchronized (mMetadataListeners) { - mMetadataListeners.forEach((device, pair) -> { - try { - mService.registerMetadataListener(mBluetoothMetadataListener, - device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Failed to register metadata listener", e); - } - }); - } - synchronized (mBluetoothConnectionCallbackExecutorMap) { - if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { - try { - mService.registerBluetoothConnectionCallback(mConnectionCallback, - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth" - + "connection callback", e); - } - } - } - } - - public void onBluetoothServiceDown() { - synchronized (mServiceLock.writeLock()) { - mService = null; - if (mLeScanClients != null) { - mLeScanClients.clear(); - } - if (mBluetoothLeAdvertiser != null) { - mBluetoothLeAdvertiser.cleanup(); - } - if (mBluetoothLeScanner != null) { - mBluetoothLeScanner.cleanup(); - } - } - } - - public void onBrEdrDown() { - } - }; - - /** - * Enable the Bluetooth Adapter, but don't auto-connect devices - * and don't persist state. Only for use by system applications. - * - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean enableNoAutoConnect() { - if (isEnabled()) { - if (DBG) { - Log.d(TAG, "enableNoAutoConnect(): BT already enabled!"); - } - return true; - } - try { - return mManagerService.enableNoAutoConnect(mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - BluetoothStatusCodes.ERROR_UNKNOWN, - BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, - BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST, - }) - public @interface OobError {} - - /** - * Provides callback methods for receiving {@link OobData} from the host stack, as well as an - * error interface in order to allow the caller to determine next steps based on the {@code - * ErrorCode}. - * - * @hide - */ - @SystemApi - public interface OobDataCallback { - /** - * Handles the {@link OobData} received from the host stack. - * - * @param transport - whether the {@link OobData} is generated for LE or Classic. - * @param oobData - data generated in the host stack(LE) or controller (Classic) - */ - void onOobData(@Transport int transport, @NonNull OobData oobData); - - /** - * Provides feedback when things don't go as expected. - * - * @param errorCode - the code describing the type of error that occurred. - */ - void onError(@OobError int errorCode); - } - - /** - * Wraps an AIDL interface around an {@link OobDataCallback} interface. - * - * @see {@link IBluetoothOobDataCallback} for interface definition. - * - * @hide - */ - public class WrappedOobDataCallback extends IBluetoothOobDataCallback.Stub { - private final OobDataCallback mCallback; - private final Executor mExecutor; - - /** - * @param callback - object to receive {@link OobData} must be a non null argument - * - * @throws NullPointerException if the callback is null. - */ - WrappedOobDataCallback(@NonNull OobDataCallback callback, - @NonNull @CallbackExecutor Executor executor) { - requireNonNull(callback); - requireNonNull(executor); - mCallback = callback; - mExecutor = executor; - } - /** - * Wrapper function to relay to the {@link OobDataCallback#onOobData} - * - * @param transport - whether the {@link OobData} is generated for LE or Classic. - * @param oobData - data generated in the host stack(LE) or controller (Classic) - * - * @hide - */ - public void onOobData(@Transport int transport, @NonNull OobData oobData) { - mExecutor.execute(new Runnable() { - public void run() { - mCallback.onOobData(transport, oobData); - } - }); - } - /** - * Wrapper function to relay to the {@link OobDataCallback#onError} - * - * @param errorCode - the code descibing the type of error that occurred. - * - * @hide - */ - public void onError(@OobError int errorCode) { - mExecutor.execute(new Runnable() { - public void run() { - mCallback.onError(errorCode); - } - }); - } - } - - /** - * Fetches a secret data value that can be used for a secure and simple pairing experience. - * - * <p>This is the Local Out of Band data the comes from the - * - * <p>This secret is the local Out of Band data. This data is used to securely and quickly - * pair two devices with minimal user interaction. - * - * <p>For example, this secret can be transferred to a remote device out of band (meaning any - * other way besides using bluetooth). Once the remote device finds this device using the - * information given in the data, such as the PUBLIC ADDRESS, the remote device could then - * connect to this device using this secret when the pairing sequenece asks for the secret. - * This device will respond by automatically accepting the pairing due to the secret being so - * trustworthy. - * - * @param transport - provide type of transport (e.g. LE or Classic). - * @param callback - target object to receive the {@link OobData} value. - * - * @throws NullPointerException if callback is null. - * @throws IllegalArgumentException if the transport is not valid. - * - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public void generateLocalOobData(@Transport int transport, - @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) { - if (transport != BluetoothDevice.TRANSPORT_BREDR && transport - != BluetoothDevice.TRANSPORT_LE) { - throw new IllegalArgumentException("Invalid transport '" + transport + "'!"); - } - requireNonNull(callback); - if (!isEnabled()) { - Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!"); - callback.onError(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED); - } else { - try { - mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback, - executor), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - } - - /** - * Enable control of the Bluetooth Adapter for a single application. - * - * <p>Some applications need to use Bluetooth for short periods of time to - * transfer data but don't want all the associated implications like - * automatic connection to headsets etc. - * - * <p> Multiple applications can call this. This is reference counted and - * Bluetooth disabled only when no one else is using it. There will be no UI - * shown to the user while bluetooth is being enabled. Any user action will - * override this call. For example, if user wants Bluetooth on and the last - * user of this API wanted to disable Bluetooth, Bluetooth will not be - * turned off. - * - * <p> This API is only meant to be used by internal applications. Third - * party applications but use {@link #enable} and {@link #disable} APIs. - * - * <p> If this API returns true, it means the callback will be called. - * The callback will be called with the current state of Bluetooth. - * If the state is not what was requested, an internal error would be the - * reason. If Bluetooth is already on and if this function is called to turn - * it on, the api will return true and a callback will be called. - * - * @param on True for on, false for off. - * @param callback The callback to notify changes to the state. - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public boolean changeApplicationBluetoothState(boolean on, - BluetoothStateChangeCallback callback) { - return false; - } - - /** - * @hide - */ - public interface BluetoothStateChangeCallback { - /** - * @hide - */ - void onBluetoothStateChange(boolean on); - } - - /** - * @hide - */ - public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub { - private BluetoothStateChangeCallback mCallback; - - StateChangeCallbackWrapper(BluetoothStateChangeCallback callback) { - mCallback = callback; - } - - @Override - public void onBluetoothStateChange(boolean on) { - mCallback.onBluetoothStateChange(on); - } - } - - private Set<BluetoothDevice> toDeviceSet(List<BluetoothDevice> devices) { - Set<BluetoothDevice> deviceSet = new HashSet<BluetoothDevice>(devices); - return Collections.unmodifiableSet(deviceSet); - } - - protected void finalize() throws Throwable { - try { - removeServiceStateCallback(mManagerCallback); - } finally { - super.finalize(); - } - } - - /** - * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0" - * <p>Alphabetic characters must be uppercase to be valid. - * - * @param address Bluetooth address as string - * @return true if the address is valid, false otherwise - */ - public static boolean checkBluetoothAddress(String address) { - if (address == null || address.length() != ADDRESS_LENGTH) { - return false; - } - for (int i = 0; i < ADDRESS_LENGTH; i++) { - char c = address.charAt(i); - switch (i % 3) { - case 0: - case 1: - if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { - // hex character, OK - break; - } - return false; - case 2: - if (c == ':') { - break; // OK - } - return false; - } - } - return true; - } - - /** - * Determines whether a String Bluetooth address, such as "F0:43:A8:23:10:00" - * is a RANDOM STATIC address. - * - * RANDOM STATIC: (addr & 0xC0) == 0xC0 - * RANDOM RESOLVABLE: (addr & 0xC0) == 0x40 - * RANDOM non-RESOLVABLE: (addr & 0xC0) == 0x00 - * - * @param address Bluetooth address as string - * @return true if the 2 Most Significant Bits of the address equals 0xC0. - * - * @hide - */ - public static boolean isAddressRandomStatic(@NonNull String address) { - requireNonNull(address); - return checkBluetoothAddress(address) - && (Integer.parseInt(address.split(":")[0], 16) & 0xC0) == 0xC0; - } - - /** {@hide} */ - @UnsupportedAppUsage - @RequiresNoPermission - public IBluetoothManager getBluetoothManager() { - return mManagerService; - } - - /** {@hide} */ - @RequiresNoPermission - public AttributionSource getAttributionSource() { - return mAttributionSource; - } - - @GuardedBy("sServiceLock") - private static final WeakHashMap<IBluetoothManagerCallback, Void> sProxyServiceStateCallbacks = - new WeakHashMap<>(); - - /*package*/ IBluetooth getBluetoothService() { - synchronized (sServiceLock) { - if (sProxyServiceStateCallbacks.isEmpty()) { - throw new IllegalStateException( - "Anonymous service access requires at least one lifecycle in process"); - } - return sService; - } - } - - @UnsupportedAppUsage - /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) { - Objects.requireNonNull(cb); - synchronized (sServiceLock) { - sProxyServiceStateCallbacks.put(cb, null); - registerOrUnregisterAdapterLocked(); - return sService; - } - } - - /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) { - Objects.requireNonNull(cb); - synchronized (sServiceLock) { - sProxyServiceStateCallbacks.remove(cb); - registerOrUnregisterAdapterLocked(); - } - } - - /** - * Handle registering (or unregistering) a single process-wide - * {@link IBluetoothManagerCallback} based on the presence of local - * {@link #sProxyServiceStateCallbacks} clients. - */ - @GuardedBy("sServiceLock") - private void registerOrUnregisterAdapterLocked() { - final boolean isRegistered = sServiceRegistered; - final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty(); - - if (isRegistered != wantRegistered) { - if (wantRegistered) { - try { - sService = mManagerService.registerAdapter(sManagerCallback); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } else { - try { - mManagerService.unregisterAdapter(sManagerCallback); - sService = null; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - sServiceRegistered = wantRegistered; - } - } - - /** - * Callback interface used to deliver LE scan results. - * - * @see #startLeScan(LeScanCallback) - * @see #startLeScan(UUID[], LeScanCallback) - */ - public interface LeScanCallback { - /** - * Callback reporting an LE device found during a device scan initiated - * by the {@link BluetoothAdapter#startLeScan} function. - * - * @param device Identifies the remote device - * @param rssi The RSSI value for the remote device as reported by the Bluetooth hardware. 0 - * if no RSSI value is available. - * @param scanRecord The content of the advertisement record offered by the remote device. - */ - void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord); - } - - /** - * Register a callback to receive events whenever the bluetooth stack goes down and back up, - * e.g. in the event the bluetooth is turned off/on via settings. - * - * If the bluetooth stack is currently up, there will not be an initial callback call. - * You can use the return value as an indication of this being the case. - * - * Callbacks will be delivered on a binder thread. - * - * @return whether bluetooth is already up currently - * - * @hide - */ - public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) { - return getBluetoothService(callback.mRemote) != null; - } - - /** - * Unregister a callback registered via {@link #registerServiceLifecycleCallback} - * - * @hide - */ - public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) { - removeServiceStateCallback(callback.mRemote); - } - - /** - * A callback for {@link #registerServiceLifecycleCallback} - * - * @hide - */ - public abstract static class ServiceLifecycleCallback { - - /** Called when the bluetooth stack is up */ - public abstract void onBluetoothServiceUp(); - - /** Called when the bluetooth stack is down */ - public abstract void onBluetoothServiceDown(); - - IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() { - @Override - public void onBluetoothServiceUp(IBluetooth bluetoothService) { - ServiceLifecycleCallback.this.onBluetoothServiceUp(); - } - - @Override - public void onBluetoothServiceDown() { - ServiceLifecycleCallback.this.onBluetoothServiceDown(); - } - - @Override - public void onBrEdrDown() {} - }; - } - - /** - * Starts a scan for Bluetooth LE devices. - * - * <p>Results of the scan are reported using the - * {@link LeScanCallback#onLeScan} callback. - * - * @param callback the callback LE scan results are delivered - * @return true, if the scan was started successfully - * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)} - * instead. - */ - @Deprecated - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresBluetoothLocationPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public boolean startLeScan(LeScanCallback callback) { - return startLeScan(null, callback); - } - - /** - * Starts a scan for Bluetooth LE devices, looking for devices that - * advertise given services. - * - * <p>Devices which advertise all specified services are reported using the - * {@link LeScanCallback#onLeScan} callback. - * - * @param serviceUuids Array of services to look for - * @param callback the callback LE scan results are delivered - * @return true, if the scan was started successfully - * @deprecated use {@link BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)} - * instead. - */ - @Deprecated - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresBluetoothLocationPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) { - if (DBG) { - Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids)); - } - if (callback == null) { - if (DBG) { - Log.e(TAG, "startLeScan: null callback"); - } - return false; - } - BluetoothLeScanner scanner = getBluetoothLeScanner(); - if (scanner == null) { - if (DBG) { - Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner"); - } - return false; - } - - synchronized (mLeScanClients) { - if (mLeScanClients.containsKey(callback)) { - if (DBG) { - Log.e(TAG, "LE Scan has already started"); - } - return false; - } - - try { - IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); - if (iGatt == null) { - // BLE is not supported - return false; - } - - @SuppressLint("AndroidFrameworkBluetoothPermission") - ScanCallback scanCallback = new ScanCallback() { - @Override - public void onScanResult(int callbackType, ScanResult result) { - if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) { - // Should not happen. - Log.e(TAG, "LE Scan has already started"); - return; - } - ScanRecord scanRecord = result.getScanRecord(); - if (scanRecord == null) { - return; - } - if (serviceUuids != null) { - List<ParcelUuid> uuids = new ArrayList<ParcelUuid>(); - for (UUID uuid : serviceUuids) { - uuids.add(new ParcelUuid(uuid)); - } - List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids(); - if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) { - if (DBG) { - Log.d(TAG, "uuids does not match"); - } - return; - } - } - callback.onLeScan(result.getDevice(), result.getRssi(), - scanRecord.getBytes()); - } - }; - ScanSettings settings = new ScanSettings.Builder().setCallbackType( - ScanSettings.CALLBACK_TYPE_ALL_MATCHES) - .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) - .build(); - - List<ScanFilter> filters = new ArrayList<ScanFilter>(); - if (serviceUuids != null && serviceUuids.length > 0) { - // Note scan filter does not support matching an UUID array so we put one - // UUID to hardware and match the whole array in callback. - ScanFilter filter = - new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuids[0])) - .build(); - filters.add(filter); - } - scanner.startScan(filters, settings, scanCallback); - - mLeScanClients.put(callback, scanCallback); - return true; - - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - return false; - } - - /** - * Stops an ongoing Bluetooth LE device scan. - * - * @param callback used to identify which scan to stop must be the same handle used to start the - * scan - * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead. - */ - @Deprecated - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void stopLeScan(LeScanCallback callback) { - if (DBG) { - Log.d(TAG, "stopLeScan()"); - } - BluetoothLeScanner scanner = getBluetoothLeScanner(); - if (scanner == null) { - return; - } - synchronized (mLeScanClients) { - ScanCallback scanCallback = mLeScanClients.remove(callback); - if (scanCallback == null) { - if (DBG) { - Log.d(TAG, "scan not started yet"); - } - return; - } - scanner.stopScan(scanCallback); - } - } - - /** - * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and - * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen - * for incoming connections. The supported Bluetooth transport is LE only. - * <p>A remote device connecting to this socket will be authenticated and communication on this - * socket will be encrypted. - * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening - * {@link BluetoothServerSocket}. - * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {@link - * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is - * closed, Bluetooth is turned off, or the application exits unexpectedly. - * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is - * defined and performed by the application. - * <p>Use {@link BluetoothDevice#createL2capChannel(int)} to connect to this server - * socket from another Android device that is given the PSM value. - * - * @return an L2CAP CoC BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions, or unable to start this CoC - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @NonNull BluetoothServerSocket listenUsingL2capChannel() - throws IOException { - BluetoothServerSocket socket = - new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true, - SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false); - int errno = socket.mSocket.bindListen(); - if (errno != 0) { - throw new IOException("Error: " + errno); - } - - int assignedPsm = socket.mSocket.getPort(); - if (assignedPsm == 0) { - throw new IOException("Error: Unable to assign PSM value"); - } - if (DBG) { - Log.d(TAG, "listenUsingL2capChannel: set assigned PSM to " - + assignedPsm); - } - socket.setChannel(assignedPsm); - - return socket; - } - - /** - * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and - * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The - * supported Bluetooth transport is LE only. - * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable - * to person-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and - * authenticated communication channel is desired. - * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening - * {@link BluetoothServerSocket}. - * <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value - * can be read from the {@link BluetoothServerSocket#getPsm()} and this value will be released - * when this server socket is closed, Bluetooth is turned off, or the application exits - * unexpectedly. - * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is - * defined and performed by the application. - * <p>Use {@link BluetoothDevice#createInsecureL2capChannel(int)} to connect to this server - * socket from another Android device that is given the PSM value. - * - * @return an L2CAP CoC BluetoothServerSocket - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions, or unable to start this CoC - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @NonNull BluetoothServerSocket listenUsingInsecureL2capChannel() - throws IOException { - BluetoothServerSocket socket = - new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false, - SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false); - int errno = socket.mSocket.bindListen(); - if (errno != 0) { - throw new IOException("Error: " + errno); - } - - int assignedPsm = socket.mSocket.getPort(); - if (assignedPsm == 0) { - throw new IOException("Error: Unable to assign PSM value"); - } - if (DBG) { - Log.d(TAG, "listenUsingInsecureL2capChannel: set assigned PSM to " - + assignedPsm); - } - socket.setChannel(assignedPsm); - - return socket; - } - - /** - * Register a {@link #OnMetadataChangedListener} to receive update about metadata - * changes for this {@link BluetoothDevice}. - * Registration must be done when Bluetooth is ON and will last until - * {@link #removeOnMetadataChangedListener(BluetoothDevice)} is called, even when Bluetooth - * restarted in the middle. - * All input parameters should not be null or {@link NullPointerException} will be triggered. - * The same {@link BluetoothDevice} and {@link #OnMetadataChangedListener} pair can only be - * registered once, double registration would cause {@link IllegalArgumentException}. - * - * @param device {@link BluetoothDevice} that will be registered - * @param executor the executor for listener callback - * @param listener {@link #OnMetadataChangedListener} that will receive asynchronous callbacks - * @return true on success, false on error - * @throws NullPointerException If one of {@code listener}, {@code device} or {@code executor} - * is null. - * @throws IllegalArgumentException The same {@link #OnMetadataChangedListener} and - * {@link BluetoothDevice} are registered twice. - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device, - @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) { - if (DBG) Log.d(TAG, "addOnMetadataChangedListener()"); - - final IBluetooth service = mService; - if (service == null) { - Log.e(TAG, "Bluetooth is not enabled. Cannot register metadata listener"); - return false; - } - if (listener == null) { - throw new NullPointerException("listener is null"); - } - if (device == null) { - throw new NullPointerException("device is null"); - } - if (executor == null) { - throw new NullPointerException("executor is null"); - } - - synchronized (mMetadataListeners) { - List<Pair<OnMetadataChangedListener, Executor>> listenerList = - mMetadataListeners.get(device); - if (listenerList == null) { - // Create new listener/executor list for registeration - listenerList = new ArrayList<>(); - mMetadataListeners.put(device, listenerList); - } else { - // Check whether this device was already registed by the lisenter - if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) { - throw new IllegalArgumentException("listener was already regestered" - + " for the device"); - } - } - - Pair<OnMetadataChangedListener, Executor> listenerPair = new Pair(listener, executor); - listenerList.add(listenerPair); - - boolean ret = false; - try { - ret = service.registerMetadataListener(mBluetoothMetadataListener, device, - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "registerMetadataListener fail", e); - } finally { - if (!ret) { - // Remove listener registered earlier when fail. - listenerList.remove(listenerPair); - if (listenerList.isEmpty()) { - // Remove the device if its listener list is empty - mMetadataListeners.remove(device); - } - } - } - return ret; - } - } - - /** - * Unregister a {@link #OnMetadataChangedListener} from a registered {@link BluetoothDevice}. - * Unregistration can be done when Bluetooth is either ON or OFF. - * {@link #addOnMetadataChangedListener(OnMetadataChangedListener, BluetoothDevice, Executor)} - * must be called before unregisteration. - * - * @param device {@link BluetoothDevice} that will be unregistered. It - * should not be null or {@link NullPointerException} will be triggered. - * @param listener {@link OnMetadataChangedListener} that will be unregistered. It - * should not be null or {@link NullPointerException} will be triggered. - * @return true on success, false on error - * @throws NullPointerException If {@code listener} or {@code device} is null. - * @throws IllegalArgumentException If {@code device} has not been registered before. - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device, - @NonNull OnMetadataChangedListener listener) { - if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()"); - if (device == null) { - throw new NullPointerException("device is null"); - } - if (listener == null) { - throw new NullPointerException("listener is null"); - } - - synchronized (mMetadataListeners) { - if (!mMetadataListeners.containsKey(device)) { - throw new IllegalArgumentException("device was not registered"); - } - // Remove issued listener from the registered device - mMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener))); - - if (mMetadataListeners.get(device).isEmpty()) { - // Unregister to Bluetooth service if all listeners are removed from - // the registered device - mMetadataListeners.remove(device); - final IBluetooth service = mService; - if (service == null) { - // Bluetooth is OFF, do nothing to Bluetooth service. - return true; - } - try { - return service.unregisterMetadataListener(device, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "unregisterMetadataListener fail", e); - return false; - } - } - } - return true; - } - - /** - * This interface is used to implement {@link BluetoothAdapter} metadata listener. - * @hide - */ - @SystemApi - public interface OnMetadataChangedListener { - /** - * Callback triggered if the metadata of {@link BluetoothDevice} registered in - * {@link #addOnMetadataChangedListener}. - * - * @param device changed {@link BluetoothDevice}. - * @param key changed metadata key, one of BluetoothDevice.METADATA_*. - * @param value the new value of metadata as byte array. - */ - void onMetadataChanged(@NonNull BluetoothDevice device, int key, - @Nullable byte[] value); - } - - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final IBluetoothConnectionCallback mConnectionCallback = - new IBluetoothConnectionCallback.Stub() { - @Override - public void onDeviceConnected(BluetoothDevice device) { - Attributable.setAttributionSource(device, mAttributionSource); - for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry: - mBluetoothConnectionCallbackExecutorMap.entrySet()) { - BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); - Executor executor = callbackExecutorEntry.getValue(); - executor.execute(() -> callback.onDeviceConnected(device)); - } - } - - @Override - public void onDeviceDisconnected(BluetoothDevice device, int hciReason) { - Attributable.setAttributionSource(device, mAttributionSource); - for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry: - mBluetoothConnectionCallbackExecutorMap.entrySet()) { - BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); - Executor executor = callbackExecutorEntry.getValue(); - executor.execute(() -> callback.onDeviceDisconnected(device, hciReason)); - } - } - }; - - /** - * Registers the BluetoothConnectionCallback to receive callback events when a bluetooth device - * (classic or low energy) is connected or disconnected. - * - * @param executor is the callback executor - * @param callback is the connection callback you wish to register - * @return true if the callback was registered successfully, false otherwise - * @throws IllegalArgumentException if the callback is already registered - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull BluetoothConnectionCallback callback) { - if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()"); - if (callback == null) { - return false; - } - - synchronized (mBluetoothConnectionCallbackExecutorMap) { - // If the callback map is empty, we register the service-to-app callback - if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - if (!mService.registerBluetoothConnectionCallback(mConnectionCallback, - mAttributionSource)) { - return false; - } - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - mBluetoothConnectionCallbackExecutorMap.remove(callback); - } finally { - mServiceLock.readLock().unlock(); - } - } - - // Adds the passed in callback to our map of callbacks to executors - if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) { - throw new IllegalArgumentException("This callback has already been registered"); - } - mBluetoothConnectionCallbackExecutorMap.put(callback, executor); - } - - return true; - } - - /** - * Unregisters the BluetoothConnectionCallback that was previously registered by the application - * - * @param callback is the connection callback you wish to unregister - * @return true if the callback was unregistered successfully, false otherwise - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean unregisterBluetoothConnectionCallback( - @NonNull BluetoothConnectionCallback callback) { - if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()"); - if (callback == null) { - return false; - } - - synchronized (mBluetoothConnectionCallbackExecutorMap) { - if (mBluetoothConnectionCallbackExecutorMap.remove(callback) != null) { - return false; - } - } - - if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { - return true; - } - - // If the callback map is empty, we unregister the service-to-app callback - try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.unregisterBluetoothConnectionCallback(mConnectionCallback, - mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - - return false; - } - - /** - * This abstract class is used to implement callbacks for when a bluetooth classic or Bluetooth - * Low Energy (BLE) device is either connected or disconnected. - * - * @hide - */ - public abstract static class BluetoothConnectionCallback { - /** - * Callback triggered when a bluetooth device (classic or BLE) is connected - * @param device is the connected bluetooth device - */ - public void onDeviceConnected(BluetoothDevice device) {} - - /** - * Callback triggered when a bluetooth device (classic or BLE) is disconnected - * @param device is the disconnected bluetooth device - * @param reason is the disconnect reason - */ - public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {} - - /** - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = { "REASON_" }, value = { - BluetoothStatusCodes.ERROR_UNKNOWN, - BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST, - BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST, - BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL, - BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE, - BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT, - BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY, - BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY, - BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED, - BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS, - BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS}) - public @interface DisconnectReason {} - - /** - * Returns human-readable strings corresponding to {@link DisconnectReason}. - */ - public static String disconnectReasonText(@DisconnectReason int reason) { - switch (reason) { - case BluetoothStatusCodes.ERROR_UNKNOWN: - return "Reason unknown"; - case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST: - return "Local request"; - case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST: - return "Remote request"; - case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL: - return "Local error"; - case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE: - return "Remote error"; - case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT: - return "Timeout"; - case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY: - return "Security"; - case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY: - return "System policy"; - case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED: - return "Resource constrained"; - case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS: - return "Connection already exists"; - case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS: - return "Bad parameters"; - default: - return "Unrecognized disconnect reason: " + reason; - } - } - } - - /** - * Converts old constant of priority to the new for connection policy - * - * @param priority is the priority to convert to connection policy - * @return the equivalent connection policy constant to the priority - * - * @hide - */ - public static @ConnectionPolicy int priorityToConnectionPolicy(int priority) { - switch(priority) { - case BluetoothProfile.PRIORITY_AUTO_CONNECT: - return BluetoothProfile.CONNECTION_POLICY_ALLOWED; - case BluetoothProfile.PRIORITY_ON: - return BluetoothProfile.CONNECTION_POLICY_ALLOWED; - case BluetoothProfile.PRIORITY_OFF: - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - case BluetoothProfile.PRIORITY_UNDEFINED: - return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; - default: - Log.e(TAG, "setPriority: Invalid priority: " + priority); - return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; - } - } - - /** - * Converts new constant of connection policy to the old for priority - * - * @param connectionPolicy is the connection policy to convert to priority - * @return the equivalent priority constant to the connectionPolicy - * - * @hide - */ - public static int connectionPolicyToPriority(@ConnectionPolicy int connectionPolicy) { - switch(connectionPolicy) { - case BluetoothProfile.CONNECTION_POLICY_ALLOWED: - return BluetoothProfile.PRIORITY_ON; - case BluetoothProfile.CONNECTION_POLICY_FORBIDDEN: - return BluetoothProfile.PRIORITY_OFF; - case BluetoothProfile.CONNECTION_POLICY_UNKNOWN: - return BluetoothProfile.PRIORITY_UNDEFINED; - } - return BluetoothProfile.PRIORITY_UNDEFINED; - } -} diff --git a/core/java/android/bluetooth/BluetoothAssignedNumbers.java b/core/java/android/bluetooth/BluetoothAssignedNumbers.java deleted file mode 100644 index 41a34e061845..000000000000 --- a/core/java/android/bluetooth/BluetoothAssignedNumbers.java +++ /dev/null @@ -1,1171 +0,0 @@ -/* - * Copyright (C) 2010 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.bluetooth; - -/** - * Bluetooth Assigned Numbers. - * <p> - * For now we only include Company ID values. - * - * @see <a href="https://www.bluetooth.org/technical/assignednumbers/identifiers.htm"> The Official - * Bluetooth SIG Member Website | Company Identifiers</a> - */ -public class BluetoothAssignedNumbers { - - // Bluetooth SIG Company ID values - /* - * Ericsson Technology Licensing. - */ - public static final int ERICSSON_TECHNOLOGY = 0x0000; - - /* - * Nokia Mobile Phones. - */ - public static final int NOKIA_MOBILE_PHONES = 0x0001; - - /* - * Intel Corp. - */ - public static final int INTEL = 0x0002; - - /* - * IBM Corp. - */ - public static final int IBM = 0x0003; - - /* - * Toshiba Corp. - */ - public static final int TOSHIBA = 0x0004; - - /* - * 3Com. - */ - public static final int THREECOM = 0x0005; - - /* - * Microsoft. - */ - public static final int MICROSOFT = 0x0006; - - /* - * Lucent. - */ - public static final int LUCENT = 0x0007; - - /* - * Motorola. - */ - public static final int MOTOROLA = 0x0008; - - /* - * Infineon Technologies AG. - */ - public static final int INFINEON_TECHNOLOGIES = 0x0009; - - /* - * Cambridge Silicon Radio. - */ - public static final int CAMBRIDGE_SILICON_RADIO = 0x000A; - - /* - * Silicon Wave. - */ - public static final int SILICON_WAVE = 0x000B; - - /* - * Digianswer A/S. - */ - public static final int DIGIANSWER = 0x000C; - - /* - * Texas Instruments Inc. - */ - public static final int TEXAS_INSTRUMENTS = 0x000D; - - /* - * Parthus Technologies Inc. - */ - public static final int PARTHUS_TECHNOLOGIES = 0x000E; - - /* - * Broadcom Corporation. - */ - public static final int BROADCOM = 0x000F; - - /* - * Mitel Semiconductor. - */ - public static final int MITEL_SEMICONDUCTOR = 0x0010; - - /* - * Widcomm, Inc. - */ - public static final int WIDCOMM = 0x0011; - - /* - * Zeevo, Inc. - */ - public static final int ZEEVO = 0x0012; - - /* - * Atmel Corporation. - */ - public static final int ATMEL = 0x0013; - - /* - * Mitsubishi Electric Corporation. - */ - public static final int MITSUBISHI_ELECTRIC = 0x0014; - - /* - * RTX Telecom A/S. - */ - public static final int RTX_TELECOM = 0x0015; - - /* - * KC Technology Inc. - */ - public static final int KC_TECHNOLOGY = 0x0016; - - /* - * Newlogic. - */ - public static final int NEWLOGIC = 0x0017; - - /* - * Transilica, Inc. - */ - public static final int TRANSILICA = 0x0018; - - /* - * Rohde & Schwarz GmbH & Co. KG. - */ - public static final int ROHDE_AND_SCHWARZ = 0x0019; - - /* - * TTPCom Limited. - */ - public static final int TTPCOM = 0x001A; - - /* - * Signia Technologies, Inc. - */ - public static final int SIGNIA_TECHNOLOGIES = 0x001B; - - /* - * Conexant Systems Inc. - */ - public static final int CONEXANT_SYSTEMS = 0x001C; - - /* - * Qualcomm. - */ - public static final int QUALCOMM = 0x001D; - - /* - * Inventel. - */ - public static final int INVENTEL = 0x001E; - - /* - * AVM Berlin. - */ - public static final int AVM_BERLIN = 0x001F; - - /* - * BandSpeed, Inc. - */ - public static final int BANDSPEED = 0x0020; - - /* - * Mansella Ltd. - */ - public static final int MANSELLA = 0x0021; - - /* - * NEC Corporation. - */ - public static final int NEC = 0x0022; - - /* - * WavePlus Technology Co., Ltd. - */ - public static final int WAVEPLUS_TECHNOLOGY = 0x0023; - - /* - * Alcatel. - */ - public static final int ALCATEL = 0x0024; - - /* - * Philips Semiconductors. - */ - public static final int PHILIPS_SEMICONDUCTORS = 0x0025; - - /* - * C Technologies. - */ - public static final int C_TECHNOLOGIES = 0x0026; - - /* - * Open Interface. - */ - public static final int OPEN_INTERFACE = 0x0027; - - /* - * R F Micro Devices. - */ - public static final int RF_MICRO_DEVICES = 0x0028; - - /* - * Hitachi Ltd. - */ - public static final int HITACHI = 0x0029; - - /* - * Symbol Technologies, Inc. - */ - public static final int SYMBOL_TECHNOLOGIES = 0x002A; - - /* - * Tenovis. - */ - public static final int TENOVIS = 0x002B; - - /* - * Macronix International Co. Ltd. - */ - public static final int MACRONIX = 0x002C; - - /* - * GCT Semiconductor. - */ - public static final int GCT_SEMICONDUCTOR = 0x002D; - - /* - * Norwood Systems. - */ - public static final int NORWOOD_SYSTEMS = 0x002E; - - /* - * MewTel Technology Inc. - */ - public static final int MEWTEL_TECHNOLOGY = 0x002F; - - /* - * ST Microelectronics. - */ - public static final int ST_MICROELECTRONICS = 0x0030; - - /* - * Synopsys. - */ - public static final int SYNOPSYS = 0x0031; - - /* - * Red-M (Communications) Ltd. - */ - public static final int RED_M = 0x0032; - - /* - * Commil Ltd. - */ - public static final int COMMIL = 0x0033; - - /* - * Computer Access Technology Corporation (CATC). - */ - public static final int CATC = 0x0034; - - /* - * Eclipse (HQ Espana) S.L. - */ - public static final int ECLIPSE = 0x0035; - - /* - * Renesas Technology Corp. - */ - public static final int RENESAS_TECHNOLOGY = 0x0036; - - /* - * Mobilian Corporation. - */ - public static final int MOBILIAN_CORPORATION = 0x0037; - - /* - * Terax. - */ - public static final int TERAX = 0x0038; - - /* - * Integrated System Solution Corp. - */ - public static final int INTEGRATED_SYSTEM_SOLUTION = 0x0039; - - /* - * Matsushita Electric Industrial Co., Ltd. - */ - public static final int MATSUSHITA_ELECTRIC = 0x003A; - - /* - * Gennum Corporation. - */ - public static final int GENNUM = 0x003B; - - /* - * Research In Motion. - */ - public static final int RESEARCH_IN_MOTION = 0x003C; - - /* - * IPextreme, Inc. - */ - public static final int IPEXTREME = 0x003D; - - /* - * Systems and Chips, Inc. - */ - public static final int SYSTEMS_AND_CHIPS = 0x003E; - - /* - * Bluetooth SIG, Inc. - */ - public static final int BLUETOOTH_SIG = 0x003F; - - /* - * Seiko Epson Corporation. - */ - public static final int SEIKO_EPSON = 0x0040; - - /* - * Integrated Silicon Solution Taiwan, Inc. - */ - public static final int INTEGRATED_SILICON_SOLUTION = 0x0041; - - /* - * CONWISE Technology Corporation Ltd. - */ - public static final int CONWISE_TECHNOLOGY = 0x0042; - - /* - * PARROT SA. - */ - public static final int PARROT = 0x0043; - - /* - * Socket Mobile. - */ - public static final int SOCKET_MOBILE = 0x0044; - - /* - * Atheros Communications, Inc. - */ - public static final int ATHEROS_COMMUNICATIONS = 0x0045; - - /* - * MediaTek, Inc. - */ - public static final int MEDIATEK = 0x0046; - - /* - * Bluegiga. - */ - public static final int BLUEGIGA = 0x0047; - - /* - * Marvell Technology Group Ltd. - */ - public static final int MARVELL = 0x0048; - - /* - * 3DSP Corporation. - */ - public static final int THREE_DSP = 0x0049; - - /* - * Accel Semiconductor Ltd. - */ - public static final int ACCEL_SEMICONDUCTOR = 0x004A; - - /* - * Continental Automotive Systems. - */ - public static final int CONTINENTAL_AUTOMOTIVE = 0x004B; - - /* - * Apple, Inc. - */ - public static final int APPLE = 0x004C; - - /* - * Staccato Communications, Inc. - */ - public static final int STACCATO_COMMUNICATIONS = 0x004D; - - /* - * Avago Technologies. - */ - public static final int AVAGO = 0x004E; - - /* - * APT Licensing Ltd. - */ - public static final int APT_LICENSING = 0x004F; - - /* - * SiRF Technology, Inc. - */ - public static final int SIRF_TECHNOLOGY = 0x0050; - - /* - * Tzero Technologies, Inc. - */ - public static final int TZERO_TECHNOLOGIES = 0x0051; - - /* - * J&M Corporation. - */ - public static final int J_AND_M = 0x0052; - - /* - * Free2move AB. - */ - public static final int FREE2MOVE = 0x0053; - - /* - * 3DiJoy Corporation. - */ - public static final int THREE_DIJOY = 0x0054; - - /* - * Plantronics, Inc. - */ - public static final int PLANTRONICS = 0x0055; - - /* - * Sony Ericsson Mobile Communications. - */ - public static final int SONY_ERICSSON = 0x0056; - - /* - * Harman International Industries, Inc. - */ - public static final int HARMAN_INTERNATIONAL = 0x0057; - - /* - * Vizio, Inc. - */ - public static final int VIZIO = 0x0058; - - /* - * Nordic Semiconductor ASA. - */ - public static final int NORDIC_SEMICONDUCTOR = 0x0059; - - /* - * EM Microelectronic-Marin SA. - */ - public static final int EM_MICROELECTRONIC_MARIN = 0x005A; - - /* - * Ralink Technology Corporation. - */ - public static final int RALINK_TECHNOLOGY = 0x005B; - - /* - * Belkin International, Inc. - */ - public static final int BELKIN_INTERNATIONAL = 0x005C; - - /* - * Realtek Semiconductor Corporation. - */ - public static final int REALTEK_SEMICONDUCTOR = 0x005D; - - /* - * Stonestreet One, LLC. - */ - public static final int STONESTREET_ONE = 0x005E; - - /* - * Wicentric, Inc. - */ - public static final int WICENTRIC = 0x005F; - - /* - * RivieraWaves S.A.S. - */ - public static final int RIVIERAWAVES = 0x0060; - - /* - * RDA Microelectronics. - */ - public static final int RDA_MICROELECTRONICS = 0x0061; - - /* - * Gibson Guitars. - */ - public static final int GIBSON_GUITARS = 0x0062; - - /* - * MiCommand Inc. - */ - public static final int MICOMMAND = 0x0063; - - /* - * Band XI International, LLC. - */ - public static final int BAND_XI_INTERNATIONAL = 0x0064; - - /* - * Hewlett-Packard Company. - */ - public static final int HEWLETT_PACKARD = 0x0065; - - /* - * 9Solutions Oy. - */ - public static final int NINE_SOLUTIONS = 0x0066; - - /* - * GN Netcom A/S. - */ - public static final int GN_NETCOM = 0x0067; - - /* - * General Motors. - */ - public static final int GENERAL_MOTORS = 0x0068; - - /* - * A&D Engineering, Inc. - */ - public static final int A_AND_D_ENGINEERING = 0x0069; - - /* - * MindTree Ltd. - */ - public static final int MINDTREE = 0x006A; - - /* - * Polar Electro OY. - */ - public static final int POLAR_ELECTRO = 0x006B; - - /* - * Beautiful Enterprise Co., Ltd. - */ - public static final int BEAUTIFUL_ENTERPRISE = 0x006C; - - /* - * BriarTek, Inc. - */ - public static final int BRIARTEK = 0x006D; - - /* - * Summit Data Communications, Inc. - */ - public static final int SUMMIT_DATA_COMMUNICATIONS = 0x006E; - - /* - * Sound ID. - */ - public static final int SOUND_ID = 0x006F; - - /* - * Monster, LLC. - */ - public static final int MONSTER = 0x0070; - - /* - * connectBlue AB. - */ - public static final int CONNECTBLUE = 0x0071; - - /* - * ShangHai Super Smart Electronics Co. Ltd. - */ - public static final int SHANGHAI_SUPER_SMART_ELECTRONICS = 0x0072; - - /* - * Group Sense Ltd. - */ - public static final int GROUP_SENSE = 0x0073; - - /* - * Zomm, LLC. - */ - public static final int ZOMM = 0x0074; - - /* - * Samsung Electronics Co. Ltd. - */ - public static final int SAMSUNG_ELECTRONICS = 0x0075; - - /* - * Creative Technology Ltd. - */ - public static final int CREATIVE_TECHNOLOGY = 0x0076; - - /* - * Laird Technologies. - */ - public static final int LAIRD_TECHNOLOGIES = 0x0077; - - /* - * Nike, Inc. - */ - public static final int NIKE = 0x0078; - - /* - * lesswire AG. - */ - public static final int LESSWIRE = 0x0079; - - /* - * MStar Semiconductor, Inc. - */ - public static final int MSTAR_SEMICONDUCTOR = 0x007A; - - /* - * Hanlynn Technologies. - */ - public static final int HANLYNN_TECHNOLOGIES = 0x007B; - - /* - * A & R Cambridge. - */ - public static final int A_AND_R_CAMBRIDGE = 0x007C; - - /* - * Seers Technology Co. Ltd. - */ - public static final int SEERS_TECHNOLOGY = 0x007D; - - /* - * Sports Tracking Technologies Ltd. - */ - public static final int SPORTS_TRACKING_TECHNOLOGIES = 0x007E; - - /* - * Autonet Mobile. - */ - public static final int AUTONET_MOBILE = 0x007F; - - /* - * DeLorme Publishing Company, Inc. - */ - public static final int DELORME_PUBLISHING_COMPANY = 0x0080; - - /* - * WuXi Vimicro. - */ - public static final int WUXI_VIMICRO = 0x0081; - - /* - * Sennheiser Communications A/S. - */ - public static final int SENNHEISER_COMMUNICATIONS = 0x0082; - - /* - * TimeKeeping Systems, Inc. - */ - public static final int TIMEKEEPING_SYSTEMS = 0x0083; - - /* - * Ludus Helsinki Ltd. - */ - public static final int LUDUS_HELSINKI = 0x0084; - - /* - * BlueRadios, Inc. - */ - public static final int BLUERADIOS = 0x0085; - - /* - * equinox AG. - */ - public static final int EQUINOX_AG = 0x0086; - - /* - * Garmin International, Inc. - */ - public static final int GARMIN_INTERNATIONAL = 0x0087; - - /* - * Ecotest. - */ - public static final int ECOTEST = 0x0088; - - /* - * GN ReSound A/S. - */ - public static final int GN_RESOUND = 0x0089; - - /* - * Jawbone. - */ - public static final int JAWBONE = 0x008A; - - /* - * Topcorn Positioning Systems, LLC. - */ - public static final int TOPCORN_POSITIONING_SYSTEMS = 0x008B; - - /* - * Qualcomm Labs, Inc. - */ - public static final int QUALCOMM_LABS = 0x008C; - - /* - * Zscan Software. - */ - public static final int ZSCAN_SOFTWARE = 0x008D; - - /* - * Quintic Corp. - */ - public static final int QUINTIC = 0x008E; - - /* - * Stollman E+V GmbH. - */ - public static final int STOLLMAN_E_PLUS_V = 0x008F; - - /* - * Funai Electric Co., Ltd. - */ - public static final int FUNAI_ELECTRIC = 0x0090; - - /* - * Advanced PANMOBIL Systems GmbH & Co. KG. - */ - public static final int ADVANCED_PANMOBIL_SYSTEMS = 0x0091; - - /* - * ThinkOptics, Inc. - */ - public static final int THINKOPTICS = 0x0092; - - /* - * Universal Electronics, Inc. - */ - public static final int UNIVERSAL_ELECTRONICS = 0x0093; - - /* - * Airoha Technology Corp. - */ - public static final int AIROHA_TECHNOLOGY = 0x0094; - - /* - * NEC Lighting, Ltd. - */ - public static final int NEC_LIGHTING = 0x0095; - - /* - * ODM Technology, Inc. - */ - public static final int ODM_TECHNOLOGY = 0x0096; - - /* - * Bluetrek Technologies Limited. - */ - public static final int BLUETREK_TECHNOLOGIES = 0x0097; - - /* - * zer01.tv GmbH. - */ - public static final int ZER01_TV = 0x0098; - - /* - * i.Tech Dynamic Global Distribution Ltd. - */ - public static final int I_TECH_DYNAMIC_GLOBAL_DISTRIBUTION = 0x0099; - - /* - * Alpwise. - */ - public static final int ALPWISE = 0x009A; - - /* - * Jiangsu Toppower Automotive Electronics Co., Ltd. - */ - public static final int JIANGSU_TOPPOWER_AUTOMOTIVE_ELECTRONICS = 0x009B; - - /* - * Colorfy, Inc. - */ - public static final int COLORFY = 0x009C; - - /* - * Geoforce Inc. - */ - public static final int GEOFORCE = 0x009D; - - /* - * Bose Corporation. - */ - public static final int BOSE = 0x009E; - - /* - * Suunto Oy. - */ - public static final int SUUNTO = 0x009F; - - /* - * Kensington Computer Products Group. - */ - public static final int KENSINGTON_COMPUTER_PRODUCTS_GROUP = 0x00A0; - - /* - * SR-Medizinelektronik. - */ - public static final int SR_MEDIZINELEKTRONIK = 0x00A1; - - /* - * Vertu Corporation Limited. - */ - public static final int VERTU = 0x00A2; - - /* - * Meta Watch Ltd. - */ - public static final int META_WATCH = 0x00A3; - - /* - * LINAK A/S. - */ - public static final int LINAK = 0x00A4; - - /* - * OTL Dynamics LLC. - */ - public static final int OTL_DYNAMICS = 0x00A5; - - /* - * Panda Ocean Inc. - */ - public static final int PANDA_OCEAN = 0x00A6; - - /* - * Visteon Corporation. - */ - public static final int VISTEON = 0x00A7; - - /* - * ARP Devices Limited. - */ - public static final int ARP_DEVICES = 0x00A8; - - /* - * Magneti Marelli S.p.A. - */ - public static final int MAGNETI_MARELLI = 0x00A9; - - /* - * CAEN RFID srl. - */ - public static final int CAEN_RFID = 0x00AA; - - /* - * Ingenieur-Systemgruppe Zahn GmbH. - */ - public static final int INGENIEUR_SYSTEMGRUPPE_ZAHN = 0x00AB; - - /* - * Green Throttle Games. - */ - public static final int GREEN_THROTTLE_GAMES = 0x00AC; - - /* - * Peter Systemtechnik GmbH. - */ - public static final int PETER_SYSTEMTECHNIK = 0x00AD; - - /* - * Omegawave Oy. - */ - public static final int OMEGAWAVE = 0x00AE; - - /* - * Cinetix. - */ - public static final int CINETIX = 0x00AF; - - /* - * Passif Semiconductor Corp. - */ - public static final int PASSIF_SEMICONDUCTOR = 0x00B0; - - /* - * Saris Cycling Group, Inc. - */ - public static final int SARIS_CYCLING_GROUP = 0x00B1; - - /* - * Bekey A/S. - */ - public static final int BEKEY = 0x00B2; - - /* - * Clarinox Technologies Pty. Ltd. - */ - public static final int CLARINOX_TECHNOLOGIES = 0x00B3; - - /* - * BDE Technology Co., Ltd. - */ - public static final int BDE_TECHNOLOGY = 0x00B4; - - /* - * Swirl Networks. - */ - public static final int SWIRL_NETWORKS = 0x00B5; - - /* - * Meso international. - */ - public static final int MESO_INTERNATIONAL = 0x00B6; - - /* - * TreLab Ltd. - */ - public static final int TRELAB = 0x00B7; - - /* - * Qualcomm Innovation Center, Inc. (QuIC). - */ - public static final int QUALCOMM_INNOVATION_CENTER = 0x00B8; - - /* - * Johnson Controls, Inc. - */ - public static final int JOHNSON_CONTROLS = 0x00B9; - - /* - * Starkey Laboratories Inc. - */ - public static final int STARKEY_LABORATORIES = 0x00BA; - - /* - * S-Power Electronics Limited. - */ - public static final int S_POWER_ELECTRONICS = 0x00BB; - - /* - * Ace Sensor Inc. - */ - public static final int ACE_SENSOR = 0x00BC; - - /* - * Aplix Corporation. - */ - public static final int APLIX = 0x00BD; - - /* - * AAMP of America. - */ - public static final int AAMP_OF_AMERICA = 0x00BE; - - /* - * Stalmart Technology Limited. - */ - public static final int STALMART_TECHNOLOGY = 0x00BF; - - /* - * AMICCOM Electronics Corporation. - */ - public static final int AMICCOM_ELECTRONICS = 0x00C0; - - /* - * Shenzhen Excelsecu Data Technology Co.,Ltd. - */ - public static final int SHENZHEN_EXCELSECU_DATA_TECHNOLOGY = 0x00C1; - - /* - * Geneq Inc. - */ - public static final int GENEQ = 0x00C2; - - /* - * adidas AG. - */ - public static final int ADIDAS = 0x00C3; - - /* - * LG Electronics. - */ - public static final int LG_ELECTRONICS = 0x00C4; - - /* - * Onset Computer Corporation. - */ - public static final int ONSET_COMPUTER = 0x00C5; - - /* - * Selfly BV. - */ - public static final int SELFLY = 0x00C6; - - /* - * Quuppa Oy. - */ - public static final int QUUPPA = 0x00C7; - - /* - * GeLo Inc. - */ - public static final int GELO = 0x00C8; - - /* - * Evluma. - */ - public static final int EVLUMA = 0x00C9; - - /* - * MC10. - */ - public static final int MC10 = 0x00CA; - - /* - * Binauric SE. - */ - public static final int BINAURIC = 0x00CB; - - /* - * Beats Electronics. - */ - public static final int BEATS_ELECTRONICS = 0x00CC; - - /* - * Microchip Technology Inc. - */ - public static final int MICROCHIP_TECHNOLOGY = 0x00CD; - - /* - * Elgato Systems GmbH. - */ - public static final int ELGATO_SYSTEMS = 0x00CE; - - /* - * ARCHOS SA. - */ - public static final int ARCHOS = 0x00CF; - - /* - * Dexcom, Inc. - */ - public static final int DEXCOM = 0x00D0; - - /* - * Polar Electro Europe B.V. - */ - public static final int POLAR_ELECTRO_EUROPE = 0x00D1; - - /* - * Dialog Semiconductor B.V. - */ - public static final int DIALOG_SEMICONDUCTOR = 0x00D2; - - /* - * Taixingbang Technology (HK) Co,. LTD. - */ - public static final int TAIXINGBANG_TECHNOLOGY = 0x00D3; - - /* - * Kawantech. - */ - public static final int KAWANTECH = 0x00D4; - - /* - * Austco Communication Systems. - */ - public static final int AUSTCO_COMMUNICATION_SYSTEMS = 0x00D5; - - /* - * Timex Group USA, Inc. - */ - public static final int TIMEX_GROUP_USA = 0x00D6; - - /* - * Qualcomm Technologies, Inc. - */ - public static final int QUALCOMM_TECHNOLOGIES = 0x00D7; - - /* - * Qualcomm Connected Experiences, Inc. - */ - public static final int QUALCOMM_CONNECTED_EXPERIENCES = 0x00D8; - - /* - * Voyetra Turtle Beach. - */ - public static final int VOYETRA_TURTLE_BEACH = 0x00D9; - - /* - * txtr GmbH. - */ - public static final int TXTR = 0x00DA; - - /* - * Biosentronics. - */ - public static final int BIOSENTRONICS = 0x00DB; - - /* - * Procter & Gamble. - */ - public static final int PROCTER_AND_GAMBLE = 0x00DC; - - /* - * Hosiden Corporation. - */ - public static final int HOSIDEN = 0x00DD; - - /* - * Muzik LLC. - */ - public static final int MUZIK = 0x00DE; - - /* - * Misfit Wearables Corp. - */ - public static final int MISFIT_WEARABLES = 0x00DF; - - /* - * Google. - */ - public static final int GOOGLE = 0x00E0; - - /* - * Danlers Ltd. - */ - public static final int DANLERS = 0x00E1; - - /* - * Semilink Inc. - */ - public static final int SEMILINK = 0x00E2; - - /* - * You can't instantiate one of these. - */ - private BluetoothAssignedNumbers() { - } - -} diff --git a/core/java/android/bluetooth/BluetoothAudioConfig.java b/core/java/android/bluetooth/BluetoothAudioConfig.java deleted file mode 100644 index 4c8b8c11fbc2..000000000000 --- a/core/java/android/bluetooth/BluetoothAudioConfig.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2009 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.bluetooth; - -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Represents the audio configuration for a Bluetooth A2DP source device. - * - * {@see BluetoothA2dpSink} - * - * {@hide} - */ -public final class BluetoothAudioConfig implements Parcelable { - - private final int mSampleRate; - private final int mChannelConfig; - private final int mAudioFormat; - - public BluetoothAudioConfig(int sampleRate, int channelConfig, int audioFormat) { - mSampleRate = sampleRate; - mChannelConfig = channelConfig; - mAudioFormat = audioFormat; - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof BluetoothAudioConfig) { - BluetoothAudioConfig bac = (BluetoothAudioConfig) o; - return (bac.mSampleRate == mSampleRate && bac.mChannelConfig == mChannelConfig - && bac.mAudioFormat == mAudioFormat); - } - return false; - } - - @Override - public int hashCode() { - return mSampleRate | (mChannelConfig << 24) | (mAudioFormat << 28); - } - - @Override - public String toString() { - return "{mSampleRate:" + mSampleRate + ",mChannelConfig:" + mChannelConfig - + ",mAudioFormat:" + mAudioFormat + "}"; - } - - @Override - public int describeContents() { - return 0; - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothAudioConfig> CREATOR = - new Parcelable.Creator<BluetoothAudioConfig>() { - public BluetoothAudioConfig createFromParcel(Parcel in) { - int sampleRate = in.readInt(); - int channelConfig = in.readInt(); - int audioFormat = in.readInt(); - return new BluetoothAudioConfig(sampleRate, channelConfig, audioFormat); - } - - public BluetoothAudioConfig[] newArray(int size) { - return new BluetoothAudioConfig[size]; - } - }; - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mSampleRate); - out.writeInt(mChannelConfig); - out.writeInt(mAudioFormat); - } - - /** - * Returns the sample rate in samples per second - * - * @return sample rate - */ - public int getSampleRate() { - return mSampleRate; - } - - /** - * Returns the channel configuration (either {@link android.media.AudioFormat#CHANNEL_IN_MONO} - * or {@link android.media.AudioFormat#CHANNEL_IN_STEREO}) - * - * @return channel configuration - */ - public int getChannelConfig() { - return mChannelConfig; - } - - /** - * Returns the channel audio format (either {@link android.media.AudioFormat#ENCODING_PCM_16BIT} - * or {@link android.media.AudioFormat#ENCODING_PCM_8BIT} - * - * @return audio format - */ - public int getAudioFormat() { - return mAudioFormat; - } -} diff --git a/core/java/android/bluetooth/BluetoothAvrcp.java b/core/java/android/bluetooth/BluetoothAvrcp.java deleted file mode 100644 index 1a4c75906482..000000000000 --- a/core/java/android/bluetooth/BluetoothAvrcp.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -/** - * This class contains constants for Bluetooth AVRCP profile. - * - * {@hide} - */ -public final class BluetoothAvrcp { - - /* - * State flags for Passthrough commands - */ - public static final int PASSTHROUGH_STATE_PRESS = 0; - public static final int PASSTHROUGH_STATE_RELEASE = 1; - - /* - * Operation IDs for Passthrough commands - */ - public static final int PASSTHROUGH_ID_SELECT = 0x00; /* select */ - public static final int PASSTHROUGH_ID_UP = 0x01; /* up */ - public static final int PASSTHROUGH_ID_DOWN = 0x02; /* down */ - public static final int PASSTHROUGH_ID_LEFT = 0x03; /* left */ - public static final int PASSTHROUGH_ID_RIGHT = 0x04; /* right */ - public static final int PASSTHROUGH_ID_RIGHT_UP = 0x05; /* right-up */ - public static final int PASSTHROUGH_ID_RIGHT_DOWN = 0x06; /* right-down */ - public static final int PASSTHROUGH_ID_LEFT_UP = 0x07; /* left-up */ - public static final int PASSTHROUGH_ID_LEFT_DOWN = 0x08; /* left-down */ - public static final int PASSTHROUGH_ID_ROOT_MENU = 0x09; /* root menu */ - public static final int PASSTHROUGH_ID_SETUP_MENU = 0x0A; /* setup menu */ - public static final int PASSTHROUGH_ID_CONT_MENU = 0x0B; /* contents menu */ - public static final int PASSTHROUGH_ID_FAV_MENU = 0x0C; /* favorite menu */ - public static final int PASSTHROUGH_ID_EXIT = 0x0D; /* exit */ - public static final int PASSTHROUGH_ID_0 = 0x20; /* 0 */ - public static final int PASSTHROUGH_ID_1 = 0x21; /* 1 */ - public static final int PASSTHROUGH_ID_2 = 0x22; /* 2 */ - public static final int PASSTHROUGH_ID_3 = 0x23; /* 3 */ - public static final int PASSTHROUGH_ID_4 = 0x24; /* 4 */ - public static final int PASSTHROUGH_ID_5 = 0x25; /* 5 */ - public static final int PASSTHROUGH_ID_6 = 0x26; /* 6 */ - public static final int PASSTHROUGH_ID_7 = 0x27; /* 7 */ - public static final int PASSTHROUGH_ID_8 = 0x28; /* 8 */ - public static final int PASSTHROUGH_ID_9 = 0x29; /* 9 */ - public static final int PASSTHROUGH_ID_DOT = 0x2A; /* dot */ - public static final int PASSTHROUGH_ID_ENTER = 0x2B; /* enter */ - public static final int PASSTHROUGH_ID_CLEAR = 0x2C; /* clear */ - public static final int PASSTHROUGH_ID_CHAN_UP = 0x30; /* channel up */ - public static final int PASSTHROUGH_ID_CHAN_DOWN = 0x31; /* channel down */ - public static final int PASSTHROUGH_ID_PREV_CHAN = 0x32; /* previous channel */ - public static final int PASSTHROUGH_ID_SOUND_SEL = 0x33; /* sound select */ - public static final int PASSTHROUGH_ID_INPUT_SEL = 0x34; /* input select */ - public static final int PASSTHROUGH_ID_DISP_INFO = 0x35; /* display information */ - public static final int PASSTHROUGH_ID_HELP = 0x36; /* help */ - public static final int PASSTHROUGH_ID_PAGE_UP = 0x37; /* page up */ - public static final int PASSTHROUGH_ID_PAGE_DOWN = 0x38; /* page down */ - public static final int PASSTHROUGH_ID_POWER = 0x40; /* power */ - public static final int PASSTHROUGH_ID_VOL_UP = 0x41; /* volume up */ - public static final int PASSTHROUGH_ID_VOL_DOWN = 0x42; /* volume down */ - public static final int PASSTHROUGH_ID_MUTE = 0x43; /* mute */ - public static final int PASSTHROUGH_ID_PLAY = 0x44; /* play */ - public static final int PASSTHROUGH_ID_STOP = 0x45; /* stop */ - public static final int PASSTHROUGH_ID_PAUSE = 0x46; /* pause */ - public static final int PASSTHROUGH_ID_RECORD = 0x47; /* record */ - public static final int PASSTHROUGH_ID_REWIND = 0x48; /* rewind */ - public static final int PASSTHROUGH_ID_FAST_FOR = 0x49; /* fast forward */ - public static final int PASSTHROUGH_ID_EJECT = 0x4A; /* eject */ - public static final int PASSTHROUGH_ID_FORWARD = 0x4B; /* forward */ - public static final int PASSTHROUGH_ID_BACKWARD = 0x4C; /* backward */ - public static final int PASSTHROUGH_ID_ANGLE = 0x50; /* angle */ - public static final int PASSTHROUGH_ID_SUBPICT = 0x51; /* subpicture */ - public static final int PASSTHROUGH_ID_F1 = 0x71; /* F1 */ - public static final int PASSTHROUGH_ID_F2 = 0x72; /* F2 */ - public static final int PASSTHROUGH_ID_F3 = 0x73; /* F3 */ - public static final int PASSTHROUGH_ID_F4 = 0x74; /* F4 */ - public static final int PASSTHROUGH_ID_F5 = 0x75; /* F5 */ - public static final int PASSTHROUGH_ID_VENDOR = 0x7E; /* vendor unique */ - public static final int PASSTHROUGH_KEYPRESSED_RELEASE = 0x80; -} diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java deleted file mode 100644 index 81fc3e11e9e1..000000000000 --- a/core/java/android/bluetooth/BluetoothAvrcpController.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.content.AttributionSource; -import android.content.Context; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently - * supports player information, playback support and track metadata. - * - * <p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothAvrcpController proxy object. - * - * {@hide} - */ -public final class BluetoothAvrcpController implements BluetoothProfile { - private static final String TAG = "BluetoothAvrcpController"; - private static final boolean DBG = false; - private static final boolean VDBG = false; - - /** - * Intent used to broadcast the change in connection state of the AVRCP Controller - * profile. - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; - - /** - * Intent used to broadcast the change in player application setting state on AVRCP AG. - * - * <p>This intent will have the following extras: - * <ul> - * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the - * most recent player setting. </li> - * </ul> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PLAYER_SETTING = - "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING"; - - public static final String EXTRA_PLAYER_SETTING = - "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothAvrcpController> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER, - "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) { - @Override - public IBluetoothAvrcpController getServiceInterface(IBinder service) { - return IBluetoothAvrcpController.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothAvrcpController proxy object for interacting with the local - * Bluetooth AVRCP service. - */ - /* package */ BluetoothAvrcpController(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - } - - /*package*/ void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothAvrcpController getService() { - return mProfileConnector.getService(); - } - - @Override - public void finalize() { - close(); - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getConnectedDevices() { - if (VDBG) log("getConnectedDevices()"); - final IBluetoothAvrcpController service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothAvrcpController service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(BluetoothDevice device) { - if (VDBG) log("getState(" + device + ")"); - final IBluetoothAvrcpController service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Gets the player application settings. - * - * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { - if (DBG) Log.d(TAG, "getPlayerSettings"); - BluetoothAvrcpPlayerSettings settings = null; - final IBluetoothAvrcpController service = getService(); - final BluetoothAvrcpPlayerSettings defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<BluetoothAvrcpPlayerSettings> recv = - new SynchronousResultReceiver(); - service.getPlayerSettings(device, mAttributionSource, recv); - settings = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Sets the player app setting for current player. - * returns true in case setting is supported by remote, false otherwise - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { - if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); - final IBluetoothAvrcpController service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setPlayerApplicationSetting(plAppSetting, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Send Group Navigation Command to Remote. - * possible keycode values: next_grp, previous_grp defined above - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { - Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " - + keyState); - final IBluetoothAvrcpController service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver recv = new SynchronousResultReceiver(); - service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); - return; - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - private boolean isEnabled() { - return mAdapter.getState() == BluetoothAdapter.STATE_ON; - } - - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java b/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java deleted file mode 100644 index 30aea1abf73c..000000000000 --- a/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2015 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.bluetooth; - -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -import java.util.HashMap; -import java.util.Map; - -/** - * Class used to identify settings associated with the player on AG. - * - * {@hide} - */ -public final class BluetoothAvrcpPlayerSettings implements Parcelable { - public static final String TAG = "BluetoothAvrcpPlayerSettings"; - - /** - * Equalizer setting. - */ - public static final int SETTING_EQUALIZER = 0x01; - - /** - * Repeat setting. - */ - public static final int SETTING_REPEAT = 0x02; - - /** - * Shuffle setting. - */ - public static final int SETTING_SHUFFLE = 0x04; - - /** - * Scan mode setting. - */ - public static final int SETTING_SCAN = 0x08; - - /** - * Invalid state. - * - * Used for returning error codes. - */ - public static final int STATE_INVALID = -1; - - /** - * OFF state. - * - * Denotes a general OFF state. Applies to all settings. - */ - public static final int STATE_OFF = 0x00; - - /** - * ON state. - * - * Applies to {@link SETTING_EQUALIZER}. - */ - public static final int STATE_ON = 0x01; - - /** - * Single track repeat. - * - * Applies only to {@link SETTING_REPEAT}. - */ - public static final int STATE_SINGLE_TRACK = 0x02; - - /** - * All track repeat/shuffle. - * - * Applies to {@link #SETTING_REPEAT}, {@link #SETTING_SHUFFLE} and {@link #SETTING_SCAN}. - */ - public static final int STATE_ALL_TRACK = 0x03; - - /** - * Group repeat/shuffle. - * - * Applies to {@link #SETTING_REPEAT}, {@link #SETTING_SHUFFLE} and {@link #SETTING_SCAN}. - */ - public static final int STATE_GROUP = 0x04; - - /** - * List of supported settings ORed. - */ - private int mSettings; - - /** - * Hash map of current capability values. - */ - private Map<Integer, Integer> mSettingsValue = new HashMap<Integer, Integer>(); - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mSettings); - out.writeInt(mSettingsValue.size()); - for (int k : mSettingsValue.keySet()) { - out.writeInt(k); - out.writeInt(mSettingsValue.get(k)); - } - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothAvrcpPlayerSettings> CREATOR = - new Parcelable.Creator<BluetoothAvrcpPlayerSettings>() { - public BluetoothAvrcpPlayerSettings createFromParcel(Parcel in) { - return new BluetoothAvrcpPlayerSettings(in); - } - - public BluetoothAvrcpPlayerSettings[] newArray(int size) { - return new BluetoothAvrcpPlayerSettings[size]; - } - }; - - private BluetoothAvrcpPlayerSettings(Parcel in) { - mSettings = in.readInt(); - int numSettings = in.readInt(); - for (int i = 0; i < numSettings; i++) { - mSettingsValue.put(in.readInt(), in.readInt()); - } - } - - /** - * Create a new player settings object. - * - * @param settings a ORed value of SETTINGS_* defined above. - */ - public BluetoothAvrcpPlayerSettings(int settings) { - mSettings = settings; - } - - /** - * Get the supported settings. - * - * @return int ORed value of supported settings. - */ - public int getSettings() { - return mSettings; - } - - /** - * Add a setting value. - * - * The setting must be part of possible settings in {@link getSettings()}. - * - * @param setting setting config. - * @param value value for the setting. - * @throws IllegalStateException if the setting is not supported. - */ - public void addSettingValue(int setting, int value) { - if ((setting & mSettings) == 0) { - Log.e(TAG, "Setting not supported: " + setting + " " + mSettings); - throw new IllegalStateException("Setting not supported: " + setting); - } - mSettingsValue.put(setting, value); - } - - /** - * Get a setting value. - * - * The setting must be part of possible settings in {@link getSettings()}. - * - * @param setting setting config. - * @return value value for the setting. - * @throws IllegalStateException if the setting is not supported. - */ - public int getSettingValue(int setting) { - if ((setting & mSettings) == 0) { - Log.e(TAG, "Setting not supported: " + setting + " " + mSettings); - throw new IllegalStateException("Setting not supported: " + setting); - } - Integer i = mSettingsValue.get(setting); - if (i == null) return -1; - return i; - } -} diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java deleted file mode 100755 index 8535b4fd2839..000000000000 --- a/core/java/android/bluetooth/BluetoothClass.java +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.Nullable; -import android.annotation.TestApi; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; -import android.os.Parcel; -import android.os.Parcelable; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; - -/** - * Represents a Bluetooth class, which describes general characteristics - * and capabilities of a device. For example, a Bluetooth class will - * specify the general device type such as a phone, a computer, or - * headset, and whether it's capable of services such as audio or telephony. - * - * <p>Every Bluetooth class is composed of zero or more service classes, and - * exactly one device class. The device class is further broken down into major - * and minor device class components. - * - * <p>{@link BluetoothClass} is useful as a hint to roughly describe a device - * (for example to show an icon in the UI), but does not reliably describe which - * Bluetooth profiles or services are actually supported by a device. Accurate - * service discovery is done through SDP requests, which are automatically - * performed when creating an RFCOMM socket with {@link - * BluetoothDevice#createRfcommSocketToServiceRecord} and {@link - * BluetoothAdapter#listenUsingRfcommWithServiceRecord}</p> - * - * <p>Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for - * a remote device. - * - * <!-- - * The Bluetooth class is a 32 bit field. The format of these bits is defined at - * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm - * (login required). This class contains that 32 bit field, and provides - * constants and methods to determine which Service Class(es) and Device Class - * are encoded in that field. - * --> - */ -public final class BluetoothClass implements Parcelable { - /** - * Legacy error value. Applications should use null instead. - * - * @hide - */ - public static final int ERROR = 0xFF000000; - - private final int mClass; - - /** @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public BluetoothClass(int classInt) { - mClass = classInt; - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof BluetoothClass) { - return mClass == ((BluetoothClass) o).mClass; - } - return false; - } - - @Override - public int hashCode() { - return mClass; - } - - @Override - public String toString() { - return Integer.toHexString(mClass); - } - - @Override - public int describeContents() { - return 0; - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothClass> CREATOR = - new Parcelable.Creator<BluetoothClass>() { - public BluetoothClass createFromParcel(Parcel in) { - return new BluetoothClass(in.readInt()); - } - - public BluetoothClass[] newArray(int size) { - return new BluetoothClass[size]; - } - }; - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mClass); - } - - /** - * Defines all service class constants. - * <p>Each {@link BluetoothClass} encodes zero or more service classes. - */ - public static final class Service { - private static final int BITMASK = 0xFFE000; - - public static final int LIMITED_DISCOVERABILITY = 0x002000; - public static final int LE_AUDIO = 0x004000; - public static final int POSITIONING = 0x010000; - public static final int NETWORKING = 0x020000; - public static final int RENDER = 0x040000; - public static final int CAPTURE = 0x080000; - public static final int OBJECT_TRANSFER = 0x100000; - public static final int AUDIO = 0x200000; - public static final int TELEPHONY = 0x400000; - public static final int INFORMATION = 0x800000; - } - - /** - * Return true if the specified service class is supported by this - * {@link BluetoothClass}. - * <p>Valid service classes are the public constants in - * {@link BluetoothClass.Service}. For example, {@link - * BluetoothClass.Service#AUDIO}. - * - * @param service valid service class - * @return true if the service class is supported - */ - public boolean hasService(int service) { - return ((mClass & Service.BITMASK & service) != 0); - } - - /** - * Defines all device class constants. - * <p>Each {@link BluetoothClass} encodes exactly one device class, with - * major and minor components. - * <p>The constants in {@link - * BluetoothClass.Device} represent a combination of major and minor - * device components (the complete device class). The constants in {@link - * BluetoothClass.Device.Major} represent only major device classes. - * <p>See {@link BluetoothClass.Service} for service class constants. - */ - public static class Device { - private static final int BITMASK = 0x1FFC; - - /** - * Defines all major device class constants. - * <p>See {@link BluetoothClass.Device} for minor classes. - */ - public static class Major { - private static final int BITMASK = 0x1F00; - - public static final int MISC = 0x0000; - public static final int COMPUTER = 0x0100; - public static final int PHONE = 0x0200; - public static final int NETWORKING = 0x0300; - public static final int AUDIO_VIDEO = 0x0400; - public static final int PERIPHERAL = 0x0500; - public static final int IMAGING = 0x0600; - public static final int WEARABLE = 0x0700; - public static final int TOY = 0x0800; - public static final int HEALTH = 0x0900; - public static final int UNCATEGORIZED = 0x1F00; - } - - // Devices in the COMPUTER major class - public static final int COMPUTER_UNCATEGORIZED = 0x0100; - public static final int COMPUTER_DESKTOP = 0x0104; - public static final int COMPUTER_SERVER = 0x0108; - public static final int COMPUTER_LAPTOP = 0x010C; - public static final int COMPUTER_HANDHELD_PC_PDA = 0x0110; - public static final int COMPUTER_PALM_SIZE_PC_PDA = 0x0114; - public static final int COMPUTER_WEARABLE = 0x0118; - - // Devices in the PHONE major class - public static final int PHONE_UNCATEGORIZED = 0x0200; - public static final int PHONE_CELLULAR = 0x0204; - public static final int PHONE_CORDLESS = 0x0208; - public static final int PHONE_SMART = 0x020C; - public static final int PHONE_MODEM_OR_GATEWAY = 0x0210; - public static final int PHONE_ISDN = 0x0214; - - // Minor classes for the AUDIO_VIDEO major class - public static final int AUDIO_VIDEO_UNCATEGORIZED = 0x0400; - public static final int AUDIO_VIDEO_WEARABLE_HEADSET = 0x0404; - public static final int AUDIO_VIDEO_HANDSFREE = 0x0408; - //public static final int AUDIO_VIDEO_RESERVED = 0x040C; - public static final int AUDIO_VIDEO_MICROPHONE = 0x0410; - public static final int AUDIO_VIDEO_LOUDSPEAKER = 0x0414; - public static final int AUDIO_VIDEO_HEADPHONES = 0x0418; - public static final int AUDIO_VIDEO_PORTABLE_AUDIO = 0x041C; - public static final int AUDIO_VIDEO_CAR_AUDIO = 0x0420; - public static final int AUDIO_VIDEO_SET_TOP_BOX = 0x0424; - public static final int AUDIO_VIDEO_HIFI_AUDIO = 0x0428; - public static final int AUDIO_VIDEO_VCR = 0x042C; - public static final int AUDIO_VIDEO_VIDEO_CAMERA = 0x0430; - public static final int AUDIO_VIDEO_CAMCORDER = 0x0434; - public static final int AUDIO_VIDEO_VIDEO_MONITOR = 0x0438; - public static final int AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER = 0x043C; - public static final int AUDIO_VIDEO_VIDEO_CONFERENCING = 0x0440; - //public static final int AUDIO_VIDEO_RESERVED = 0x0444; - public static final int AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x0448; - - // Devices in the WEARABLE major class - public static final int WEARABLE_UNCATEGORIZED = 0x0700; - public static final int WEARABLE_WRIST_WATCH = 0x0704; - public static final int WEARABLE_PAGER = 0x0708; - public static final int WEARABLE_JACKET = 0x070C; - public static final int WEARABLE_HELMET = 0x0710; - public static final int WEARABLE_GLASSES = 0x0714; - - // Devices in the TOY major class - public static final int TOY_UNCATEGORIZED = 0x0800; - public static final int TOY_ROBOT = 0x0804; - public static final int TOY_VEHICLE = 0x0808; - public static final int TOY_DOLL_ACTION_FIGURE = 0x080C; - public static final int TOY_CONTROLLER = 0x0810; - public static final int TOY_GAME = 0x0814; - - // Devices in the HEALTH major class - public static final int HEALTH_UNCATEGORIZED = 0x0900; - public static final int HEALTH_BLOOD_PRESSURE = 0x0904; - public static final int HEALTH_THERMOMETER = 0x0908; - public static final int HEALTH_WEIGHING = 0x090C; - public static final int HEALTH_GLUCOSE = 0x0910; - public static final int HEALTH_PULSE_OXIMETER = 0x0914; - public static final int HEALTH_PULSE_RATE = 0x0918; - public static final int HEALTH_DATA_DISPLAY = 0x091C; - - // Devices in PERIPHERAL major class - /** - * @hide - */ - public static final int PERIPHERAL_NON_KEYBOARD_NON_POINTING = 0x0500; - /** - * @hide - */ - public static final int PERIPHERAL_KEYBOARD = 0x0540; - /** - * @hide - */ - public static final int PERIPHERAL_POINTING = 0x0580; - /** - * @hide - */ - public static final int PERIPHERAL_KEYBOARD_POINTING = 0x05C0; - } - - /** - * Return the major device class component of this {@link BluetoothClass}. - * <p>Values returned from this function can be compared with the - * public constants in {@link BluetoothClass.Device.Major} to determine - * which major class is encoded in this Bluetooth class. - * - * @return major device class component - */ - public int getMajorDeviceClass() { - return (mClass & Device.Major.BITMASK); - } - - /** - * Return the (major and minor) device class component of this - * {@link BluetoothClass}. - * <p>Values returned from this function can be compared with the - * public constants in {@link BluetoothClass.Device} to determine which - * device class is encoded in this Bluetooth class. - * - * @return device class component - */ - public int getDeviceClass() { - return (mClass & Device.BITMASK); - } - - /** - * Return the Bluetooth Class of Device (CoD) value including the - * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and - * minor device fields. - * - * <p>This value is an integer representation of Bluetooth CoD as in - * Bluetooth specification. - * - * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a> - * - * @hide - */ - @TestApi - public int getClassOfDevice() { - return mClass; - } - - /** - * Return the Bluetooth Class of Device (CoD) value including the - * {@link BluetoothClass.Service}, {@link BluetoothClass.Device.Major} and - * minor device fields. - * - * <p>This value is a byte array representation of Bluetooth CoD as in - * Bluetooth specification. - * - * <p>Bluetooth COD information is 3 bytes, but stored as an int. Hence the - * MSB is useless and needs to be thrown away. The lower 3 bytes are - * converted into a byte array MSB to LSB. Hence, using BIG_ENDIAN. - * - * @see <a href="Bluetooth CoD">https://www.bluetooth.com/specifications/assigned-numbers/baseband</a> - * - * @hide - */ - public byte[] getClassOfDeviceBytes() { - byte[] bytes = ByteBuffer.allocate(4) - .order(ByteOrder.BIG_ENDIAN) - .putInt(mClass) - .array(); - - // Discard the top byte - return Arrays.copyOfRange(bytes, 1, bytes.length); - } - - /** @hide */ - @UnsupportedAppUsage - public static final int PROFILE_HEADSET = 0; - /** @hide */ - @UnsupportedAppUsage - public static final int PROFILE_A2DP = 1; - /** @hide */ - public static final int PROFILE_OPP = 2; - /** @hide */ - public static final int PROFILE_HID = 3; - /** @hide */ - public static final int PROFILE_PANU = 4; - /** @hide */ - public static final int PROFILE_NAP = 5; - /** @hide */ - public static final int PROFILE_A2DP_SINK = 6; - - /** - * Check class bits for possible bluetooth profile support. - * This is a simple heuristic that tries to guess if a device with the - * given class bits might support specified profile. It is not accurate for all - * devices. It tries to err on the side of false positives. - * - * @param profile The profile to be checked - * @return True if this device might support specified profile. - * @hide - */ - @UnsupportedAppUsage - public boolean doesClassMatch(int profile) { - if (profile == PROFILE_A2DP) { - if (hasService(Service.RENDER)) { - return true; - } - // By the A2DP spec, sinks must indicate the RENDER service. - // However we found some that do not (Chordette). So lets also - // match on some other class bits. - switch (getDeviceClass()) { - case Device.AUDIO_VIDEO_HIFI_AUDIO: - case Device.AUDIO_VIDEO_HEADPHONES: - case Device.AUDIO_VIDEO_LOUDSPEAKER: - case Device.AUDIO_VIDEO_CAR_AUDIO: - return true; - default: - return false; - } - } else if (profile == PROFILE_A2DP_SINK) { - if (hasService(Service.CAPTURE)) { - return true; - } - // By the A2DP spec, srcs must indicate the CAPTURE service. - // However if some device that do not, we try to - // match on some other class bits. - switch (getDeviceClass()) { - case Device.AUDIO_VIDEO_HIFI_AUDIO: - case Device.AUDIO_VIDEO_SET_TOP_BOX: - case Device.AUDIO_VIDEO_VCR: - return true; - default: - return false; - } - } else if (profile == PROFILE_HEADSET) { - // The render service class is required by the spec for HFP, so is a - // pretty good signal - if (hasService(Service.RENDER)) { - return true; - } - // Just in case they forgot the render service class - switch (getDeviceClass()) { - case Device.AUDIO_VIDEO_HANDSFREE: - case Device.AUDIO_VIDEO_WEARABLE_HEADSET: - case Device.AUDIO_VIDEO_CAR_AUDIO: - return true; - default: - return false; - } - } else if (profile == PROFILE_OPP) { - if (hasService(Service.OBJECT_TRANSFER)) { - return true; - } - - switch (getDeviceClass()) { - case Device.COMPUTER_UNCATEGORIZED: - case Device.COMPUTER_DESKTOP: - case Device.COMPUTER_SERVER: - case Device.COMPUTER_LAPTOP: - case Device.COMPUTER_HANDHELD_PC_PDA: - case Device.COMPUTER_PALM_SIZE_PC_PDA: - case Device.COMPUTER_WEARABLE: - case Device.PHONE_UNCATEGORIZED: - case Device.PHONE_CELLULAR: - case Device.PHONE_CORDLESS: - case Device.PHONE_SMART: - case Device.PHONE_MODEM_OR_GATEWAY: - case Device.PHONE_ISDN: - return true; - default: - return false; - } - } else if (profile == PROFILE_HID) { - return getMajorDeviceClass() == Device.Major.PERIPHERAL; - } else if (profile == PROFILE_PANU || profile == PROFILE_NAP) { - // No good way to distinguish between the two, based on class bits. - if (hasService(Service.NETWORKING)) { - return true; - } - return getMajorDeviceClass() == Device.Major.NETWORKING; - } else { - return false; - } - } -} diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java deleted file mode 100644 index 9a4151adffc7..000000000000 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ /dev/null @@ -1,807 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Parcel; -import android.os.Parcelable; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * Represents the codec configuration for a Bluetooth A2DP source device. - * <p>Contains the source codec type, the codec priority, the codec sample - * rate, the codec bits per sample, and the codec channel mode. - * <p>The source codec type values are the same as those supported by the - * device hardware. - * - * {@see BluetoothA2dp} - */ -public final class BluetoothCodecConfig implements Parcelable { - /** @hide */ - @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = { - SOURCE_CODEC_TYPE_SBC, - SOURCE_CODEC_TYPE_AAC, - SOURCE_CODEC_TYPE_APTX, - SOURCE_CODEC_TYPE_APTX_HD, - SOURCE_CODEC_TYPE_LDAC, - SOURCE_CODEC_TYPE_INVALID - }) - @Retention(RetentionPolicy.SOURCE) - public @interface SourceCodecType {} - - /** - * Source codec type SBC. This is the mandatory source codec - * type. - */ - public static final int SOURCE_CODEC_TYPE_SBC = 0; - - /** - * Source codec type AAC. - */ - public static final int SOURCE_CODEC_TYPE_AAC = 1; - - /** - * Source codec type APTX. - */ - public static final int SOURCE_CODEC_TYPE_APTX = 2; - - /** - * Source codec type APTX HD. - */ - public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; - - /** - * Source codec type LDAC. - */ - public static final int SOURCE_CODEC_TYPE_LDAC = 4; - - /** - * Source codec type invalid. This is the default value used for codec - * type. - */ - public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; - - /** - * Represents the count of valid source codec types. Can be accessed via - * {@link #getMaxCodecType}. - */ - private static final int SOURCE_CODEC_TYPE_MAX = 5; - - /** @hide */ - @IntDef(prefix = "CODEC_PRIORITY_", value = { - CODEC_PRIORITY_DISABLED, - CODEC_PRIORITY_DEFAULT, - CODEC_PRIORITY_HIGHEST - }) - @Retention(RetentionPolicy.SOURCE) - public @interface CodecPriority {} - - /** - * Codec priority disabled. - * Used to indicate that this codec is disabled and should not be used. - */ - public static final int CODEC_PRIORITY_DISABLED = -1; - - /** - * Codec priority default. - * Default value used for codec priority. - */ - public static final int CODEC_PRIORITY_DEFAULT = 0; - - /** - * Codec priority highest. - * Used to indicate the highest priority a codec can have. - */ - public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; - - /** @hide */ - @IntDef(prefix = "SAMPLE_RATE_", value = { - SAMPLE_RATE_NONE, - SAMPLE_RATE_44100, - SAMPLE_RATE_48000, - SAMPLE_RATE_88200, - SAMPLE_RATE_96000, - SAMPLE_RATE_176400, - SAMPLE_RATE_192000 - }) - @Retention(RetentionPolicy.SOURCE) - public @interface SampleRate {} - - /** - * Codec sample rate 0 Hz. Default value used for - * codec sample rate. - */ - public static final int SAMPLE_RATE_NONE = 0; - - /** - * Codec sample rate 44100 Hz. - */ - public static final int SAMPLE_RATE_44100 = 0x1 << 0; - - /** - * Codec sample rate 48000 Hz. - */ - public static final int SAMPLE_RATE_48000 = 0x1 << 1; - - /** - * Codec sample rate 88200 Hz. - */ - public static final int SAMPLE_RATE_88200 = 0x1 << 2; - - /** - * Codec sample rate 96000 Hz. - */ - public static final int SAMPLE_RATE_96000 = 0x1 << 3; - - /** - * Codec sample rate 176400 Hz. - */ - public static final int SAMPLE_RATE_176400 = 0x1 << 4; - - /** - * Codec sample rate 192000 Hz. - */ - public static final int SAMPLE_RATE_192000 = 0x1 << 5; - - /** @hide */ - @IntDef(prefix = "BITS_PER_SAMPLE_", value = { - BITS_PER_SAMPLE_NONE, - BITS_PER_SAMPLE_16, - BITS_PER_SAMPLE_24, - BITS_PER_SAMPLE_32 - }) - @Retention(RetentionPolicy.SOURCE) - public @interface BitsPerSample {} - - /** - * Codec bits per sample 0. Default value of the codec - * bits per sample. - */ - public static final int BITS_PER_SAMPLE_NONE = 0; - - /** - * Codec bits per sample 16. - */ - public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; - - /** - * Codec bits per sample 24. - */ - public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; - - /** - * Codec bits per sample 32. - */ - public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; - - /** @hide */ - @IntDef(prefix = "CHANNEL_MODE_", value = { - CHANNEL_MODE_NONE, - CHANNEL_MODE_MONO, - CHANNEL_MODE_STEREO - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ChannelMode {} - - /** - * Codec channel mode NONE. Default value of the - * codec channel mode. - */ - public static final int CHANNEL_MODE_NONE = 0; - - /** - * Codec channel mode MONO. - */ - public static final int CHANNEL_MODE_MONO = 0x1 << 0; - - /** - * Codec channel mode STEREO. - */ - public static final int CHANNEL_MODE_STEREO = 0x1 << 1; - - private final @SourceCodecType int mCodecType; - private @CodecPriority int mCodecPriority; - private final @SampleRate int mSampleRate; - private final @BitsPerSample int mBitsPerSample; - private final @ChannelMode int mChannelMode; - private final long mCodecSpecific1; - private final long mCodecSpecific2; - private final long mCodecSpecific3; - private final long mCodecSpecific4; - - /** - * Creates a new BluetoothCodecConfig. - * - * @param codecType the source codec type - * @param codecPriority the priority of this codec - * @param sampleRate the codec sample rate - * @param bitsPerSample the bits per sample of this codec - * @param channelMode the channel mode of this codec - * @param codecSpecific1 the specific value 1 - * @param codecSpecific2 the specific value 2 - * @param codecSpecific3 the specific value 3 - * @param codecSpecific4 the specific value 4 - * values to 0. - * @hide - */ - @UnsupportedAppUsage - public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority, - @SampleRate int sampleRate, @BitsPerSample int bitsPerSample, - @ChannelMode int channelMode, long codecSpecific1, - long codecSpecific2, long codecSpecific3, - long codecSpecific4) { - mCodecType = codecType; - mCodecPriority = codecPriority; - mSampleRate = sampleRate; - mBitsPerSample = bitsPerSample; - mChannelMode = channelMode; - mCodecSpecific1 = codecSpecific1; - mCodecSpecific2 = codecSpecific2; - mCodecSpecific3 = codecSpecific3; - mCodecSpecific4 = codecSpecific4; - } - - /** - * Creates a new BluetoothCodecConfig. - * <p> By default, the codec priority will be set - * to {@link BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to - * {@link BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to - * {@link BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to - * {@link BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific - * values to 0. - * - * @param codecType the source codec type - */ - public BluetoothCodecConfig(@SourceCodecType int codecType) { - this(codecType, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_NONE, - BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, - BluetoothCodecConfig.CHANNEL_MODE_NONE, 0, 0, 0, 0); - } - - private BluetoothCodecConfig(Parcel in) { - mCodecType = in.readInt(); - mCodecPriority = in.readInt(); - mSampleRate = in.readInt(); - mBitsPerSample = in.readInt(); - mChannelMode = in.readInt(); - mCodecSpecific1 = in.readLong(); - mCodecSpecific2 = in.readLong(); - mCodecSpecific3 = in.readLong(); - mCodecSpecific4 = in.readLong(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof BluetoothCodecConfig) { - BluetoothCodecConfig other = (BluetoothCodecConfig) o; - return (other.mCodecType == mCodecType - && other.mCodecPriority == mCodecPriority - && other.mSampleRate == mSampleRate - && other.mBitsPerSample == mBitsPerSample - && other.mChannelMode == mChannelMode - && other.mCodecSpecific1 == mCodecSpecific1 - && other.mCodecSpecific2 == mCodecSpecific2 - && other.mCodecSpecific3 == mCodecSpecific3 - && other.mCodecSpecific4 == mCodecSpecific4); - } - return false; - } - - /** - * Returns a hash representation of this BluetoothCodecConfig - * based on all the config values. - */ - @Override - public int hashCode() { - return Objects.hash(mCodecType, mCodecPriority, mSampleRate, - mBitsPerSample, mChannelMode, mCodecSpecific1, - mCodecSpecific2, mCodecSpecific3, mCodecSpecific4); - } - - /** - * Adds capability string to an existing string. - * - * @param prevStr the previous string with the capabilities. Can be a {@code null} pointer - * @param capStr the capability string to append to prevStr argument - * @return the result string in the form "prevStr|capStr" - */ - private static String appendCapabilityToString(@Nullable String prevStr, - @NonNull String capStr) { - if (prevStr == null) { - return capStr; - } - return prevStr + "|" + capStr; - } - - /** - * Returns a {@link String} that describes each BluetoothCodecConfig parameter - * current value. - */ - @Override - public String toString() { - String sampleRateStr = null; - if (mSampleRate == SAMPLE_RATE_NONE) { - sampleRateStr = appendCapabilityToString(sampleRateStr, "NONE"); - } - if ((mSampleRate & SAMPLE_RATE_44100) != 0) { - sampleRateStr = appendCapabilityToString(sampleRateStr, "44100"); - } - if ((mSampleRate & SAMPLE_RATE_48000) != 0) { - sampleRateStr = appendCapabilityToString(sampleRateStr, "48000"); - } - if ((mSampleRate & SAMPLE_RATE_88200) != 0) { - sampleRateStr = appendCapabilityToString(sampleRateStr, "88200"); - } - if ((mSampleRate & SAMPLE_RATE_96000) != 0) { - sampleRateStr = appendCapabilityToString(sampleRateStr, "96000"); - } - if ((mSampleRate & SAMPLE_RATE_176400) != 0) { - sampleRateStr = appendCapabilityToString(sampleRateStr, "176400"); - } - if ((mSampleRate & SAMPLE_RATE_192000) != 0) { - sampleRateStr = appendCapabilityToString(sampleRateStr, "192000"); - } - - String bitsPerSampleStr = null; - if (mBitsPerSample == BITS_PER_SAMPLE_NONE) { - bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "NONE"); - } - if ((mBitsPerSample & BITS_PER_SAMPLE_16) != 0) { - bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "16"); - } - if ((mBitsPerSample & BITS_PER_SAMPLE_24) != 0) { - bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "24"); - } - if ((mBitsPerSample & BITS_PER_SAMPLE_32) != 0) { - bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "32"); - } - - String channelModeStr = null; - if (mChannelMode == CHANNEL_MODE_NONE) { - channelModeStr = appendCapabilityToString(channelModeStr, "NONE"); - } - if ((mChannelMode & CHANNEL_MODE_MONO) != 0) { - channelModeStr = appendCapabilityToString(channelModeStr, "MONO"); - } - if ((mChannelMode & CHANNEL_MODE_STEREO) != 0) { - channelModeStr = appendCapabilityToString(channelModeStr, "STEREO"); - } - - return "{codecName:" + getCodecName() - + ",mCodecType:" + mCodecType - + ",mCodecPriority:" + mCodecPriority - + ",mSampleRate:" + String.format("0x%x", mSampleRate) - + "(" + sampleRateStr + ")" - + ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) - + "(" + bitsPerSampleStr + ")" - + ",mChannelMode:" + String.format("0x%x", mChannelMode) - + "(" + channelModeStr + ")" - + ",mCodecSpecific1:" + mCodecSpecific1 - + ",mCodecSpecific2:" + mCodecSpecific2 - + ",mCodecSpecific3:" + mCodecSpecific3 - + ",mCodecSpecific4:" + mCodecSpecific4 + "}"; - } - - /** - * @return 0 - * @hide - */ - @Override - public int describeContents() { - return 0; - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothCodecConfig> CREATOR = - new Parcelable.Creator<BluetoothCodecConfig>() { - public BluetoothCodecConfig createFromParcel(Parcel in) { - return new BluetoothCodecConfig(in); - } - - public BluetoothCodecConfig[] newArray(int size) { - return new BluetoothCodecConfig[size]; - } - }; - - /** - * Flattens the object to a parcel - * - * @param out The Parcel in which the object should be written - * @param flags Additional flags about how the object should be written - * - * @hide - */ - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mCodecType); - out.writeInt(mCodecPriority); - out.writeInt(mSampleRate); - out.writeInt(mBitsPerSample); - out.writeInt(mChannelMode); - out.writeLong(mCodecSpecific1); - out.writeLong(mCodecSpecific2); - out.writeLong(mCodecSpecific3); - out.writeLong(mCodecSpecific4); - } - - /** - * Returns the codec name converted to {@link String}. - * @hide - */ - public @NonNull String getCodecName() { - switch (mCodecType) { - case SOURCE_CODEC_TYPE_SBC: - return "SBC"; - case SOURCE_CODEC_TYPE_AAC: - return "AAC"; - case SOURCE_CODEC_TYPE_APTX: - return "aptX"; - case SOURCE_CODEC_TYPE_APTX_HD: - return "aptX HD"; - case SOURCE_CODEC_TYPE_LDAC: - return "LDAC"; - case SOURCE_CODEC_TYPE_INVALID: - return "INVALID CODEC"; - default: - break; - } - return "UNKNOWN CODEC(" + mCodecType + ")"; - } - - /** - * Returns the source codec type of this config. - */ - public @SourceCodecType int getCodecType() { - return mCodecType; - } - - /** - * Returns the valid codec types count. - */ - public static int getMaxCodecType() { - return SOURCE_CODEC_TYPE_MAX; - } - - /** - * Checks whether the codec is mandatory. - * <p> The actual mandatory codec type for Android Bluetooth audio is SBC. - * See {@link #SOURCE_CODEC_TYPE_SBC}. - * - * @return {@code true} if the codec is mandatory, {@code false} otherwise - * @hide - */ - public boolean isMandatoryCodec() { - return mCodecType == SOURCE_CODEC_TYPE_SBC; - } - - /** - * Returns the codec selection priority. - * <p>The codec selection priority is relative to other codecs: larger value - * means higher priority. - */ - public @CodecPriority int getCodecPriority() { - return mCodecPriority; - } - - /** - * Sets the codec selection priority. - * <p>The codec selection priority is relative to other codecs: larger value - * means higher priority. - * - * @param codecPriority the priority this codec should have - * @hide - */ - public void setCodecPriority(@CodecPriority int codecPriority) { - mCodecPriority = codecPriority; - } - - /** - * Returns the codec sample rate. The value can be a bitmask with all - * supported sample rates. - */ - public @SampleRate int getSampleRate() { - return mSampleRate; - } - - /** - * Returns the codec bits per sample. The value can be a bitmask with all - * bits per sample supported. - */ - public @BitsPerSample int getBitsPerSample() { - return mBitsPerSample; - } - - /** - * Returns the codec channel mode. The value can be a bitmask with all - * supported channel modes. - */ - public @ChannelMode int getChannelMode() { - return mChannelMode; - } - - /** - * Returns the codec specific value1. - */ - public long getCodecSpecific1() { - return mCodecSpecific1; - } - - /** - * Returns the codec specific value2. - */ - public long getCodecSpecific2() { - return mCodecSpecific2; - } - - /** - * Returns the codec specific value3. - */ - public long getCodecSpecific3() { - return mCodecSpecific3; - } - - /** - * Returns the codec specific value4. - */ - public long getCodecSpecific4() { - return mCodecSpecific4; - } - - /** - * Checks whether a value set presented by a bitmask has zero or single bit - * - * @param valueSet the value set presented by a bitmask - * @return {@code true} if the valueSet contains zero or single bit, {@code false} otherwise - * @hide - */ - private static boolean hasSingleBit(int valueSet) { - return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0); - } - - /** - * Returns whether the object contains none or single sample rate. - * @hide - */ - public boolean hasSingleSampleRate() { - return hasSingleBit(mSampleRate); - } - - /** - * Returns whether the object contains none or single bits per sample. - * @hide - */ - public boolean hasSingleBitsPerSample() { - return hasSingleBit(mBitsPerSample); - } - - /** - * Returns whether the object contains none or single channel mode. - * @hide - */ - public boolean hasSingleChannelMode() { - return hasSingleBit(mChannelMode); - } - - /** - * Checks whether the audio feeding parameters are the same. - * - * @param other the codec config to compare against - * @return {@code true} if the audio feeding parameters are same, {@code false} otherwise - * @hide - */ - public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) { - return (other != null && other.mSampleRate == mSampleRate - && other.mBitsPerSample == mBitsPerSample - && other.mChannelMode == mChannelMode); - } - - /** - * Checks whether another codec config has the similar feeding parameters. - * Any parameters with NONE value will be considered to be a wildcard matching. - * - * @param other the codec config to compare against - * @return {@code true} if the audio feeding parameters are similar, {@code false} otherwise - * @hide - */ - public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) { - if (other == null || mCodecType != other.mCodecType) { - return false; - } - int sampleRate = other.mSampleRate; - if (mSampleRate == SAMPLE_RATE_NONE - || sampleRate == SAMPLE_RATE_NONE) { - sampleRate = mSampleRate; - } - int bitsPerSample = other.mBitsPerSample; - if (mBitsPerSample == BITS_PER_SAMPLE_NONE - || bitsPerSample == BITS_PER_SAMPLE_NONE) { - bitsPerSample = mBitsPerSample; - } - int channelMode = other.mChannelMode; - if (mChannelMode == CHANNEL_MODE_NONE - || channelMode == CHANNEL_MODE_NONE) { - channelMode = mChannelMode; - } - return sameAudioFeedingParameters(new BluetoothCodecConfig( - mCodecType, /* priority */ 0, sampleRate, bitsPerSample, channelMode, - /* specific1 */ 0, /* specific2 */ 0, /* specific3 */ 0, - /* specific4 */ 0)); - } - - /** - * Checks whether the codec specific parameters are the same. - * <p> Currently, only AAC VBR and LDAC Playback Quality on CodecSpecific1 - * are compared. - * - * @param other the codec config to compare against - * @return {@code true} if the codec specific parameters are the same, {@code false} otherwise - * @hide - */ - public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) { - if (other == null && mCodecType != other.mCodecType) { - return false; - } - switch (mCodecType) { - case SOURCE_CODEC_TYPE_AAC: - case SOURCE_CODEC_TYPE_LDAC: - if (mCodecSpecific1 != other.mCodecSpecific1) { - return false; - } - default: - return true; - } - } - - /** - * Builder for {@link BluetoothCodecConfig}. - * <p> By default, the codec type will be set to - * {@link BluetoothCodecConfig#SOURCE_CODEC_TYPE_INVALID}, the codec priority - * to {@link BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to - * {@link BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to - * {@link BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to - * {@link BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific - * values to 0. - */ - public static final class Builder { - private int mCodecType = BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID; - private int mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; - private int mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE; - private int mBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE; - private int mChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE; - private long mCodecSpecific1 = 0; - private long mCodecSpecific2 = 0; - private long mCodecSpecific3 = 0; - private long mCodecSpecific4 = 0; - - /** - * Set codec type for Bluetooth codec config. - * - * @param codecType of this codec - * @return the same Builder instance - */ - public @NonNull Builder setCodecType(@SourceCodecType int codecType) { - mCodecType = codecType; - return this; - } - - /** - * Set codec priority for Bluetooth codec config. - * - * @param codecPriority of this codec - * @return the same Builder instance - */ - public @NonNull Builder setCodecPriority(@CodecPriority int codecPriority) { - mCodecPriority = codecPriority; - return this; - } - - /** - * Set sample rate for Bluetooth codec config. - * - * @param sampleRate of this codec - * @return the same Builder instance - */ - public @NonNull Builder setSampleRate(@SampleRate int sampleRate) { - mSampleRate = sampleRate; - return this; - } - - /** - * Set the bits per sample for Bluetooth codec config. - * - * @param bitsPerSample of this codec - * @return the same Builder instance - */ - public @NonNull Builder setBitsPerSample(@BitsPerSample int bitsPerSample) { - mBitsPerSample = bitsPerSample; - return this; - } - - /** - * Set the channel mode for Bluetooth codec config. - * - * @param channelMode of this codec - * @return the same Builder instance - */ - public @NonNull Builder setChannelMode(@ChannelMode int channelMode) { - mChannelMode = channelMode; - return this; - } - - /** - * Set the first codec specific values for Bluetooth codec config. - * - * @param codecSpecific1 codec specific value or 0 if default - * @return the same Builder instance - */ - public @NonNull Builder setCodecSpecific1(long codecSpecific1) { - mCodecSpecific1 = codecSpecific1; - return this; - } - - /** - * Set the second codec specific values for Bluetooth codec config. - * - * @param codecSpecific2 codec specific value or 0 if default - * @return the same Builder instance - */ - public @NonNull Builder setCodecSpecific2(long codecSpecific2) { - mCodecSpecific2 = codecSpecific2; - return this; - } - - /** - * Set the third codec specific values for Bluetooth codec config. - * - * @param codecSpecific3 codec specific value or 0 if default - * @return the same Builder instance - */ - public @NonNull Builder setCodecSpecific3(long codecSpecific3) { - mCodecSpecific3 = codecSpecific3; - return this; - } - - /** - * Set the fourth codec specific values for Bluetooth codec config. - * - * @param codecSpecific4 codec specific value or 0 if default - * @return the same Builder instance - */ - public @NonNull Builder setCodecSpecific4(long codecSpecific4) { - mCodecSpecific4 = codecSpecific4; - return this; - } - - /** - * Build {@link BluetoothCodecConfig}. - * @return new BluetoothCodecConfig built - */ - public @NonNull BluetoothCodecConfig build() { - return new BluetoothCodecConfig(mCodecType, mCodecPriority, - mSampleRate, mBitsPerSample, - mChannelMode, mCodecSpecific1, - mCodecSpecific2, mCodecSpecific3, - mCodecSpecific4); - } - } -} diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java deleted file mode 100644 index 02606feb3b3f..000000000000 --- a/core/java/android/bluetooth/BluetoothCodecStatus.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * Represents the codec status (configuration and capability) for a Bluetooth - * A2DP source device. - * - * {@see BluetoothA2dp} - */ -public final class BluetoothCodecStatus implements Parcelable { - /** - * Extra for the codec configuration intents of the individual profiles. - * - * This extra represents the current codec status of the A2DP - * profile. - */ - public static final String EXTRA_CODEC_STATUS = - "android.bluetooth.extra.CODEC_STATUS"; - - private final @Nullable BluetoothCodecConfig mCodecConfig; - private final @Nullable List<BluetoothCodecConfig> mCodecsLocalCapabilities; - private final @Nullable List<BluetoothCodecConfig> mCodecsSelectableCapabilities; - - public BluetoothCodecStatus(@Nullable BluetoothCodecConfig codecConfig, - @Nullable List<BluetoothCodecConfig> codecsLocalCapabilities, - @Nullable List<BluetoothCodecConfig> codecsSelectableCapabilities) { - mCodecConfig = codecConfig; - mCodecsLocalCapabilities = codecsLocalCapabilities; - mCodecsSelectableCapabilities = codecsSelectableCapabilities; - } - - private BluetoothCodecStatus(Parcel in) { - mCodecConfig = in.readTypedObject(BluetoothCodecConfig.CREATOR); - mCodecsLocalCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR); - mCodecsSelectableCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR); - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof BluetoothCodecStatus) { - BluetoothCodecStatus other = (BluetoothCodecStatus) o; - return (Objects.equals(other.mCodecConfig, mCodecConfig) - && sameCapabilities(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities) - && sameCapabilities(other.mCodecsSelectableCapabilities, - mCodecsSelectableCapabilities)); - } - return false; - } - - /** - * Checks whether two lists of capabilities contain same capabilities. - * The order of the capabilities in each list is ignored. - * - * @param c1 the first list of capabilities to compare - * @param c2 the second list of capabilities to compare - * @return {@code true} if both lists contain same capabilities - */ - private static boolean sameCapabilities(@Nullable List<BluetoothCodecConfig> c1, - @Nullable List<BluetoothCodecConfig> c2) { - if (c1 == null) { - return (c2 == null); - } - if (c2 == null) { - return false; - } - if (c1.size() != c2.size()) { - return false; - } - return c1.containsAll(c2); - } - - /** - * Checks whether the codec config matches the selectable capabilities. - * Any parameters of the codec config with NONE value will be considered a wildcard matching. - * - * @param codecConfig the codec config to compare against - * @return {@code true} if the codec config matches, {@code false} otherwise - */ - public boolean isCodecConfigSelectable(@Nullable BluetoothCodecConfig codecConfig) { - if (codecConfig == null || !codecConfig.hasSingleSampleRate() - || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) { - return false; - } - for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) { - if (codecConfig.getCodecType() != selectableConfig.getCodecType()) { - continue; - } - int sampleRate = codecConfig.getSampleRate(); - if ((sampleRate & selectableConfig.getSampleRate()) == 0 - && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) { - continue; - } - int bitsPerSample = codecConfig.getBitsPerSample(); - if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0 - && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { - continue; - } - int channelMode = codecConfig.getChannelMode(); - if ((channelMode & selectableConfig.getChannelMode()) == 0 - && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) { - continue; - } - return true; - } - return false; - } - - /** - * Returns a hash based on the codec config and local capabilities. - */ - @Override - public int hashCode() { - return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, - mCodecsLocalCapabilities); - } - - /** - * Returns a {@link String} that describes each BluetoothCodecStatus parameter - * current value. - */ - @Override - public String toString() { - return "{mCodecConfig:" + mCodecConfig - + ",mCodecsLocalCapabilities:" + mCodecsLocalCapabilities - + ",mCodecsSelectableCapabilities:" + mCodecsSelectableCapabilities - + "}"; - } - - /** - * @return 0 - * @hide - */ - @Override - public int describeContents() { - return 0; - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothCodecStatus> CREATOR = - new Parcelable.Creator<BluetoothCodecStatus>() { - public BluetoothCodecStatus createFromParcel(Parcel in) { - return new BluetoothCodecStatus(in); - } - - public BluetoothCodecStatus[] newArray(int size) { - return new BluetoothCodecStatus[size]; - } - }; - - /** - * Flattens the object to a parcel. - * - * @param out The Parcel in which the object should be written - * @param flags Additional flags about how the object should be written - */ - @Override - public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeTypedObject(mCodecConfig, 0); - out.writeTypedList(mCodecsLocalCapabilities); - out.writeTypedList(mCodecsSelectableCapabilities); - } - - /** - * Returns the current codec configuration. - */ - public @Nullable BluetoothCodecConfig getCodecConfig() { - return mCodecConfig; - } - - /** - * Returns the codecs local capabilities. - */ - public @NonNull List<BluetoothCodecConfig> getCodecsLocalCapabilities() { - return (mCodecsLocalCapabilities == null) - ? Collections.emptyList() : mCodecsLocalCapabilities; - } - - /** - * Returns the codecs selectable capabilities. - */ - public @NonNull List<BluetoothCodecConfig> getCodecsSelectableCapabilities() { - return (mCodecsSelectableCapabilities == null) - ? Collections.emptyList() : mCodecsSelectableCapabilities; - } -} diff --git a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java deleted file mode 100644 index ba57ec472a6e..000000000000 --- a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java +++ /dev/null @@ -1,555 +0,0 @@ -/* - * 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 static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.Manifest; -import android.annotation.CallbackExecutor; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SystemApi; -import android.content.AttributionSource; -import android.content.Context; -import android.os.IBinder; -import android.os.ParcelUuid; -import android.os.RemoteException; -import android.util.CloseGuard; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeoutException; - -/** - * This class provides the public APIs to control the Bluetooth CSIP set coordinator. - * - * <p>BluetoothCsipSetCoordinator is a proxy object for controlling the Bluetooth VC - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothCsipSetCoordinator proxy object. - * - */ -public final class BluetoothCsipSetCoordinator implements BluetoothProfile, AutoCloseable { - private static final String TAG = "BluetoothCsipSetCoordinator"; - private static final boolean DBG = false; - private static final boolean VDBG = false; - - private CloseGuard mCloseGuard; - - /** - * @hide - */ - @SystemApi - public interface ClientLockCallback { - /** - * @hide - */ - @SystemApi void onGroupLockSet(int groupId, int opStatus, boolean isLocked); - } - - private static class BluetoothCsipSetCoordinatorLockCallbackDelegate - extends IBluetoothCsipSetCoordinatorLockCallback.Stub { - private final ClientLockCallback mCallback; - private final Executor mExecutor; - - BluetoothCsipSetCoordinatorLockCallbackDelegate( - Executor executor, ClientLockCallback callback) { - mExecutor = executor; - mCallback = callback; - } - - @Override - public void onGroupLockSet(int groupId, int opStatus, boolean isLocked) { - mExecutor.execute(() -> mCallback.onGroupLockSet(groupId, opStatus, isLocked)); - } - }; - - /** - * Intent used to broadcast the change in connection state of the CSIS - * Client. - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED = - "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED"; - - /** - * Intent used to expose broadcast receiving device. - * - * <p>This intent will have 2 extras: - * <ul> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Broadcast receiver device. </li> - * <li> {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li> - * <li> {@link #EXTRA_CSIS_GROUP_SIZE} - Group size. </li> - * <li> {@link #EXTRA_CSIS_GROUP_TYPE_UUID} - Group type UUID. </li> - * </ul> - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CSIS_DEVICE_AVAILABLE = - "android.bluetooth.action.CSIS_DEVICE_AVAILABLE"; - - /** - * Used as an extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. - * Contains the group id. - * - * @hide - */ - public static final String EXTRA_CSIS_GROUP_ID = "android.bluetooth.extra.CSIS_GROUP_ID"; - - /** - * Group size as int extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. - * - * @hide - */ - public static final String EXTRA_CSIS_GROUP_SIZE = "android.bluetooth.extra.CSIS_GROUP_SIZE"; - - /** - * Group type uuid extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent. - * - * @hide - */ - public static final String EXTRA_CSIS_GROUP_TYPE_UUID = - "android.bluetooth.extra.CSIS_GROUP_TYPE_UUID"; - - /** - * Intent used to broadcast information about identified set member - * ready to connect. - * - * <p>This intent will have one extra: - * <ul> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can - * be null if no device is active. </li> - * <li> {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li> - * </ul> - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE = - "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE"; - - /** - * This represents an invalid group ID. - * - * @hide - */ - public static final int GROUP_ID_INVALID = IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID; - - /** - * Indicating that group was locked with success. - * - * @hide - */ - public static final int GROUP_LOCK_SUCCESS = 0; - - /** - * Indicating that group locked failed due to invalid group ID. - * - * @hide - */ - public static final int GROUP_LOCK_FAILED_INVALID_GROUP = 1; - - /** - * Indicating that group locked failed due to empty group. - * - * @hide - */ - public static final int GROUP_LOCK_FAILED_GROUP_EMPTY = 2; - - /** - * Indicating that group locked failed due to group members being disconnected. - * - * @hide - */ - public static final int GROUP_LOCK_FAILED_GROUP_NOT_CONNECTED = 3; - - /** - * Indicating that group locked failed due to group member being already locked. - * - * @hide - */ - public static final int GROUP_LOCK_FAILED_LOCKED_BY_OTHER = 4; - - /** - * Indicating that group locked failed due to other reason. - * - * @hide - */ - public static final int GROUP_LOCK_FAILED_OTHER_REASON = 5; - - /** - * Indicating that group member in locked state was lost. - * - * @hide - */ - public static final int LOCKED_GROUP_MEMBER_LOST = 6; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothCsipSetCoordinator> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.CSIP_SET_COORDINATOR, TAG, - IBluetoothCsipSetCoordinator.class.getName()) { - @Override - public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) { - return IBluetoothCsipSetCoordinator.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothCsipSetCoordinator proxy object for interacting with the local - * Bluetooth CSIS service. - */ - /*package*/ BluetoothCsipSetCoordinator(Context context, ServiceListener listener, BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - mCloseGuard = new CloseGuard(); - mCloseGuard.open("close"); - } - - /** - * @hide - */ - protected void finalize() { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } - close(); - } - - /** - * @hide - */ - public void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothCsipSetCoordinator getService() { - return mProfileConnector.getService(); - } - - /** - * Lock the set. - * @param groupId group ID to lock, - * @param executor callback executor, - * @param cb callback to report lock and unlock events - stays valid until the app unlocks - * using the returned lock identifier or the lock timeouts on the remote side, - * as per CSIS specification, - * @return unique lock identifier used for unlocking or null if lock has failed. - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public - @Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor, - @Nullable ClientLockCallback cb) { - if (VDBG) log("groupLockSet()"); - final IBluetoothCsipSetCoordinator service = getService(); - final UUID defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - IBluetoothCsipSetCoordinatorLockCallback delegate = null; - if ((executor != null) && (cb != null)) { - delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb); - } - try { - final SynchronousResultReceiver<ParcelUuid> recv = new SynchronousResultReceiver(); - service.groupLock(groupId, delegate, mAttributionSource, recv); - final ParcelUuid ret = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); - return ret == null ? defaultValue : ret.getUuid(); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Unlock the set. - * @param lockUuid unique lock identifier - * @return true if unlocked, false on error - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean groupUnlock(@NonNull UUID lockUuid) { - if (VDBG) log("groupLockSet()"); - if (lockUuid == null) { - return false; - } - final IBluetoothCsipSetCoordinator service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver recv = new SynchronousResultReceiver(); - service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); - return true; - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get device's groups. - * @param device the active device - * @return Map of groups ids and related UUIDs - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) { - if (VDBG) log("getGroupUuidMapByDevice()"); - final IBluetoothCsipSetCoordinator service = getService(); - final Map defaultValue = new HashMap<>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Map> recv = new SynchronousResultReceiver(); - service.getGroupUuidMapByDevice(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get group id for the given UUID - * @param uuid - * @return list of group IDs - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public @NonNull List<Integer> getAllGroupIds(@Nullable ParcelUuid uuid) { - if (VDBG) log("getAllGroupIds()"); - final IBluetoothCsipSetCoordinator service = getService(); - final List<Integer> defaultValue = new ArrayList<>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<Integer>> recv = - new SynchronousResultReceiver(); - service.getAllGroupIds(uuid, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - public @NonNull List<BluetoothDevice> getConnectedDevices() { - if (VDBG) log("getConnectedDevices()"); - final IBluetoothCsipSetCoordinator service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - public - @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[] states) { - if (VDBG) log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")"); - final IBluetoothCsipSetCoordinator service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - public - @BluetoothProfile.BtProfileState int getConnectionState(@Nullable BluetoothDevice device) { - if (VDBG) log("getState(" + device + ")"); - final IBluetoothCsipSetCoordinator service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setConnectionPolicy( - @Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothCsipSetCoordinator service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - final IBluetoothCsipSetCoordinator service = getService(); - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - private boolean isEnabled() { - return mAdapter.getState() == BluetoothAdapter.STATE_ON; - } - - private static boolean isValidDevice(@Nullable BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java deleted file mode 100644 index 93f026860856..000000000000 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ /dev/null @@ -1,2830 +0,0 @@ -/* - * Copyright (C) 2009 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.bluetooth; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -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.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresBluetoothLocationPermission; -import android.bluetooth.annotations.RequiresBluetoothScanPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.companion.AssociationRequest; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.Context; -import android.os.Build; -import android.os.Handler; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; -import android.os.Process; -import android.os.RemoteException; -import android.util.Log; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.UUID; - -/** - * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you - * create a connection with the respective device or query information about - * it, such as the name, address, class, and bonding state. - * - * <p>This class is really just a thin wrapper for a Bluetooth hardware - * address. Objects of this class are immutable. Operations on this class - * are performed on the remote Bluetooth hardware address, using the - * {@link BluetoothAdapter} that was used to create this {@link - * BluetoothDevice}. - * - * <p>To get a {@link BluetoothDevice}, use - * {@link BluetoothAdapter#getRemoteDevice(String) - * BluetoothAdapter.getRemoteDevice(String)} to create one representing a device - * of a known MAC address (which you can get through device discovery with - * {@link BluetoothAdapter}) or get one from the set of bonded devices - * returned by {@link BluetoothAdapter#getBondedDevices() - * BluetoothAdapter.getBondedDevices()}. You can then open a - * {@link BluetoothSocket} for communication with the remote device, using - * {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using - * {@link #createL2capChannel(int)} over Bluetooth LE. - * - * <div class="special reference"> - * <h3>Developer Guides</h3> - * <p> - * For more information about using Bluetooth, read the <a href= - * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer - * guide. - * </p> - * </div> - * - * {@see BluetoothAdapter} - * {@see BluetoothSocket} - */ -public final class BluetoothDevice implements Parcelable, Attributable { - private static final String TAG = "BluetoothDevice"; - private static final boolean DBG = false; - - /** - * Connection state bitmask as returned by getConnectionState. - */ - private static final int CONNECTION_STATE_DISCONNECTED = 0; - private static final int CONNECTION_STATE_CONNECTED = 1; - private static final int CONNECTION_STATE_ENCRYPTED_BREDR = 2; - private static final int CONNECTION_STATE_ENCRYPTED_LE = 4; - - /** - * Sentinel error value for this class. Guaranteed to not equal any other - * integer constant in this class. Provided as a convenience for functions - * that require a sentinel error value, for example: - * <p><code>Intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, - * BluetoothDevice.ERROR)</code> - */ - public static final int ERROR = Integer.MIN_VALUE; - - /** - * Broadcast Action: Remote device discovered. - * <p>Sent when a remote device is found during discovery. - * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link - * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or - * {@link #EXTRA_RSSI} and/or {@link #EXTRA_IS_COORDINATED_SET_MEMBER} if they are available. - */ - // TODO: Change API to not broadcast RSSI if not available (incoming connection) - @RequiresLegacyBluetoothPermission - @RequiresBluetoothScanPermission - @RequiresBluetoothLocationPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_FOUND = - "android.bluetooth.device.action.FOUND"; - - /** - * Broadcast Action: Bluetooth class of a remote device has changed. - * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link - * #EXTRA_CLASS}. - * {@see BluetoothClass} - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CLASS_CHANGED = - "android.bluetooth.device.action.CLASS_CHANGED"; - - /** - * Broadcast Action: Indicates a low level (ACL) connection has been - * established with a remote device. - * <p>Always contains the extra field {@link #EXTRA_DEVICE}. - * <p>ACL connections are managed automatically by the Android Bluetooth - * stack. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_ACL_CONNECTED = - "android.bluetooth.device.action.ACL_CONNECTED"; - - /** - * Broadcast Action: Indicates that a low level (ACL) disconnection has - * been requested for a remote device, and it will soon be disconnected. - * <p>This is useful for graceful disconnection. Applications should use - * this intent as a hint to immediately terminate higher level connections - * (RFCOMM, L2CAP, or profile connections) to the remote device. - * <p>Always contains the extra field {@link #EXTRA_DEVICE}. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_ACL_DISCONNECT_REQUESTED = - "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED"; - - /** - * Broadcast Action: Indicates a low level (ACL) disconnection from a - * remote device. - * <p>Always contains the extra field {@link #EXTRA_DEVICE}. - * <p>ACL connections are managed automatically by the Android Bluetooth - * stack. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_ACL_DISCONNECTED = - "android.bluetooth.device.action.ACL_DISCONNECTED"; - - /** - * Broadcast Action: Indicates the friendly name of a remote device has - * been retrieved for the first time, or changed since the last retrieval. - * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link - * #EXTRA_NAME}. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_NAME_CHANGED = - "android.bluetooth.device.action.NAME_CHANGED"; - - /** - * Broadcast Action: Indicates the alias of a remote device has been - * changed. - * <p>Always contains the extra field {@link #EXTRA_DEVICE}. - */ - @SuppressLint("ActionValue") - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_ALIAS_CHANGED = - "android.bluetooth.device.action.ALIAS_CHANGED"; - - /** - * Broadcast Action: Indicates a change in the bond state of a remote - * device. For example, if a device is bonded (paired). - * <p>Always contains the extra fields {@link #EXTRA_DEVICE}, {@link - * #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}. - */ - // Note: When EXTRA_BOND_STATE is BOND_NONE then this will also - // contain a hidden extra field EXTRA_REASON with the result code. - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_BOND_STATE_CHANGED = - "android.bluetooth.device.action.BOND_STATE_CHANGED"; - - /** - * Broadcast Action: Indicates the battery level of a remote device has - * been retrieved for the first time, or changed since the last retrieval - * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link - * #EXTRA_BATTERY_LEVEL}. - * - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_BATTERY_LEVEL_CHANGED = - "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED"; - - /** - * Used as an Integer extra field in {@link #ACTION_BATTERY_LEVEL_CHANGED} - * intent. It contains the most recently retrieved battery level information - * ranging from 0% to 100% for a remote device, {@link #BATTERY_LEVEL_UNKNOWN} - * when the valid is unknown or there is an error - * - * @hide - */ - public static final String EXTRA_BATTERY_LEVEL = - "android.bluetooth.device.extra.BATTERY_LEVEL"; - - /** - * Used as the unknown value for {@link #EXTRA_BATTERY_LEVEL} and {@link #getBatteryLevel()} - * - * @hide - */ - public static final int BATTERY_LEVEL_UNKNOWN = -1; - - /** - * Used as an error value for {@link #getBatteryLevel()} to represent bluetooth is off - * - * @hide - */ - public static final int BATTERY_LEVEL_BLUETOOTH_OFF = -100; - - /** - * Used as a Parcelable {@link BluetoothDevice} extra field in every intent - * broadcast by this class. It contains the {@link BluetoothDevice} that - * the intent applies to. - */ - public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE"; - - /** - * Used as a String extra field in {@link #ACTION_NAME_CHANGED} and {@link - * #ACTION_FOUND} intents. It contains the friendly Bluetooth name. - */ - public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME"; - - /** - * Used as an optional short extra field in {@link #ACTION_FOUND} intents. - * Contains the RSSI value of the remote device as reported by the - * Bluetooth hardware. - */ - public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI"; - - /** - * Used as an bool extra field in {@link #ACTION_FOUND} intents. - * It contains the information if device is discovered as member of a coordinated set or not. - * Pairing with device that belongs to a set would trigger pairing with the rest of set members. - * See Bluetooth CSIP specification for more details. - */ - public static final String EXTRA_IS_COORDINATED_SET_MEMBER = - "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER"; - - /** - * Used as a Parcelable {@link BluetoothClass} extra field in {@link - * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents. - */ - public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS"; - - /** - * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. - * Contains the bond state of the remote device. - * <p>Possible values are: - * {@link #BOND_NONE}, - * {@link #BOND_BONDING}, - * {@link #BOND_BONDED}. - */ - public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE"; - /** - * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. - * Contains the previous bond state of the remote device. - * <p>Possible values are: - * {@link #BOND_NONE}, - * {@link #BOND_BONDING}, - * {@link #BOND_BONDED}. - */ - public static final String EXTRA_PREVIOUS_BOND_STATE = - "android.bluetooth.device.extra.PREVIOUS_BOND_STATE"; - /** - * Indicates the remote device is not bonded (paired). - * <p>There is no shared link key with the remote device, so communication - * (if it is allowed at all) will be unauthenticated and unencrypted. - */ - public static final int BOND_NONE = 10; - /** - * Indicates bonding (pairing) is in progress with the remote device. - */ - public static final int BOND_BONDING = 11; - /** - * Indicates the remote device is bonded (paired). - * <p>A shared link keys exists locally for the remote device, so - * communication can be authenticated and encrypted. - * <p><i>Being bonded (paired) with a remote device does not necessarily - * mean the device is currently connected. It just means that the pending - * procedure was completed at some earlier time, and the link key is still - * stored locally, ready to use on the next connection. - * </i> - */ - public static final int BOND_BONDED = 12; - - /** - * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} - * intents for unbond reason. - * - * @hide - */ - @UnsupportedAppUsage - public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON"; - - /** - * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} - * intents to indicate pairing method used. Possible values are: - * {@link #PAIRING_VARIANT_PIN}, - * {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION}, - */ - public static final String EXTRA_PAIRING_VARIANT = - "android.bluetooth.device.extra.PAIRING_VARIANT"; - - /** - * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} - * intents as the value of passkey. - */ - public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY"; - - /** - * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST} - * intents as the value of passkey. - * @hide - */ - public static final String EXTRA_PAIRING_INITIATOR = - "android.bluetooth.device.extra.PAIRING_INITIATOR"; - - /** - * Bluetooth pairing initiator, Foreground App - * @hide - */ - public static final int EXTRA_PAIRING_INITIATOR_FOREGROUND = 1; - - /** - * Bluetooth pairing initiator, Background - * @hide - */ - public static final int EXTRA_PAIRING_INITIATOR_BACKGROUND = 2; - - /** - * Bluetooth device type, Unknown - */ - public static final int DEVICE_TYPE_UNKNOWN = 0; - - /** - * Bluetooth device type, Classic - BR/EDR devices - */ - public static final int DEVICE_TYPE_CLASSIC = 1; - - /** - * Bluetooth device type, Low Energy - LE-only - */ - public static final int DEVICE_TYPE_LE = 2; - - /** - * Bluetooth device type, Dual Mode - BR/EDR/LE - */ - public static final int DEVICE_TYPE_DUAL = 3; - - - /** @hide */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final String ACTION_SDP_RECORD = - "android.bluetooth.device.action.SDP_RECORD"; - - /** @hide */ - @IntDef(prefix = "METADATA_", value = { - METADATA_MANUFACTURER_NAME, - METADATA_MODEL_NAME, - METADATA_SOFTWARE_VERSION, - METADATA_HARDWARE_VERSION, - METADATA_COMPANION_APP, - METADATA_MAIN_ICON, - METADATA_IS_UNTETHERED_HEADSET, - METADATA_UNTETHERED_LEFT_ICON, - METADATA_UNTETHERED_RIGHT_ICON, - METADATA_UNTETHERED_CASE_ICON, - METADATA_UNTETHERED_LEFT_BATTERY, - METADATA_UNTETHERED_RIGHT_BATTERY, - METADATA_UNTETHERED_CASE_BATTERY, - METADATA_UNTETHERED_LEFT_CHARGING, - METADATA_UNTETHERED_RIGHT_CHARGING, - METADATA_UNTETHERED_CASE_CHARGING, - METADATA_ENHANCED_SETTINGS_UI_URI, - METADATA_DEVICE_TYPE, - METADATA_MAIN_BATTERY, - METADATA_MAIN_CHARGING, - METADATA_MAIN_LOW_BATTERY_THRESHOLD, - METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD, - METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD, - METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD}) - @Retention(RetentionPolicy.SOURCE) - public @interface MetadataKey{} - - /** - * Maximum length of a metadata entry, this is to avoid exploding Bluetooth - * disk usage - * @hide - */ - @SystemApi - public static final int METADATA_MAX_LENGTH = 2048; - - /** - * Manufacturer name of this Bluetooth device - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_MANUFACTURER_NAME = 0; - - /** - * Model name of this Bluetooth device - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_MODEL_NAME = 1; - - /** - * Software version of this Bluetooth device - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_SOFTWARE_VERSION = 2; - - /** - * Hardware version of this Bluetooth device - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_HARDWARE_VERSION = 3; - - /** - * Package name of the companion app, if any - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_COMPANION_APP = 4; - - /** - * URI to the main icon shown on the settings UI - * Data type should be {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_MAIN_ICON = 5; - - /** - * Whether this device is an untethered headset with left, right and case - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_IS_UNTETHERED_HEADSET = 6; - - /** - * URI to icon of the left headset - * Data type should be {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_LEFT_ICON = 7; - - /** - * URI to icon of the right headset - * Data type should be {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_RIGHT_ICON = 8; - - /** - * URI to icon of the headset charging case - * Data type should be {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_CASE_ICON = 9; - - /** - * Battery level of left headset - * Data type should be {@String} 0-100 as {@link Byte} array, otherwise - * as invalid. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10; - - /** - * Battery level of rigth headset - * Data type should be {@String} 0-100 as {@link Byte} array, otherwise - * as invalid. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11; - - /** - * Battery level of the headset charging case - * Data type should be {@String} 0-100 as {@link Byte} array, otherwise - * as invalid. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_CASE_BATTERY = 12; - - /** - * Whether the left headset is charging - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13; - - /** - * Whether the right headset is charging - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14; - - /** - * Whether the headset charging case is charging - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_CASE_CHARGING = 15; - - /** - * URI to the enhanced settings UI slice - * Data type should be {@String} as {@link Byte} array, null means - * the UI does not exist. - * @hide - */ - @SystemApi - public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; - - /** - * Type of the Bluetooth device, must be within the list of - * BluetoothDevice.DEVICE_TYPE_* - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_DEVICE_TYPE = 17; - - /** - * Battery level of the Bluetooth device, use when the Bluetooth device - * does not support HFP battery indicator. - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_MAIN_BATTERY = 18; - - /** - * Whether the device is charging. - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_MAIN_CHARGING = 19; - - /** - * The battery threshold of the Bluetooth device to show low battery icon. - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_MAIN_LOW_BATTERY_THRESHOLD = 20; - - /** - * The battery threshold of the left headset to show low battery icon. - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD = 21; - - /** - * The battery threshold of the right headset to show low battery icon. - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD = 22; - - /** - * The battery threshold of the case to show low battery icon. - * Data type should be {@String} as {@link Byte} array. - * @hide - */ - @SystemApi - public static final int METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD = 23; - - /** - * Device type which is used in METADATA_DEVICE_TYPE - * Indicates this Bluetooth device is a standard Bluetooth accessory or - * not listed in METADATA_DEVICE_TYPE_*. - * @hide - */ - @SystemApi - public static final String DEVICE_TYPE_DEFAULT = "Default"; - - /** - * Device type which is used in METADATA_DEVICE_TYPE - * Indicates this Bluetooth device is a watch. - * @hide - */ - @SystemApi - public static final String DEVICE_TYPE_WATCH = "Watch"; - - /** - * Device type which is used in METADATA_DEVICE_TYPE - * Indicates this Bluetooth device is an untethered headset. - * @hide - */ - @SystemApi - public static final String DEVICE_TYPE_UNTETHERED_HEADSET = "Untethered Headset"; - - /** - * Broadcast Action: This intent is used to broadcast the {@link UUID} - * wrapped as a {@link android.os.ParcelUuid} of the remote device after it - * has been fetched. This intent is sent only when the UUIDs of the remote - * device are requested to be fetched using Service Discovery Protocol - * <p> Always contains the extra field {@link #EXTRA_DEVICE} - * <p> Always contains the extra field {@link #EXTRA_UUID} - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_UUID = - "android.bluetooth.device.action.UUID"; - - /** @hide */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_MAS_INSTANCE = - "android.bluetooth.device.action.MAS_INSTANCE"; - - /** - * Broadcast Action: Indicates a failure to retrieve the name of a remote - * device. - * <p>Always contains the extra field {@link #EXTRA_DEVICE}. - * - * @hide - */ - //TODO: is this actually useful? - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_NAME_FAILED = - "android.bluetooth.device.action.NAME_FAILED"; - - /** - * Broadcast Action: This intent is used to broadcast PAIRING REQUEST - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PAIRING_REQUEST = - "android.bluetooth.device.action.PAIRING_REQUEST"; - /** @hide */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage - public static final String ACTION_PAIRING_CANCEL = - "android.bluetooth.device.action.PAIRING_CANCEL"; - - /** @hide */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_ACCESS_REQUEST = - "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST"; - - /** @hide */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_ACCESS_REPLY = - "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY"; - - /** @hide */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_ACCESS_CANCEL = - "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL"; - - /** - * Intent to broadcast silence mode changed. - * Alway contains the extra field {@link #EXTRA_DEVICE} - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @SystemApi - public static final String ACTION_SILENCE_MODE_CHANGED = - "android.bluetooth.device.action.SILENCE_MODE_CHANGED"; - - /** - * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent. - * - * @hide - */ - public static final String EXTRA_ACCESS_REQUEST_TYPE = - "android.bluetooth.device.extra.ACCESS_REQUEST_TYPE"; - - /** @hide */ - public static final int REQUEST_TYPE_PROFILE_CONNECTION = 1; - - /** @hide */ - public static final int REQUEST_TYPE_PHONEBOOK_ACCESS = 2; - - /** @hide */ - public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3; - - /** @hide */ - public static final int REQUEST_TYPE_SIM_ACCESS = 4; - - /** - * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents, - * Contains package name to return reply intent to. - * - * @hide - */ - public static final String EXTRA_PACKAGE_NAME = "android.bluetooth.device.extra.PACKAGE_NAME"; - - /** - * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents, - * Contains class name to return reply intent to. - * - * @hide - */ - public static final String EXTRA_CLASS_NAME = "android.bluetooth.device.extra.CLASS_NAME"; - - /** - * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent. - * - * @hide - */ - public static final String EXTRA_CONNECTION_ACCESS_RESULT = - "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT"; - - /** @hide */ - public static final int CONNECTION_ACCESS_YES = 1; - - /** @hide */ - public static final int CONNECTION_ACCESS_NO = 2; - - /** - * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intents, - * Contains boolean to indicate if the allowed response is once-for-all so that - * next request will be granted without asking user again. - * - * @hide - */ - public static final String EXTRA_ALWAYS_ALLOWED = - "android.bluetooth.device.extra.ALWAYS_ALLOWED"; - - /** - * A bond attempt succeeded - * - * @hide - */ - public static final int BOND_SUCCESS = 0; - - /** - * A bond attempt failed because pins did not match, or remote device did - * not respond to pin request in time - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int UNBOND_REASON_AUTH_FAILED = 1; - - /** - * A bond attempt failed because the other side explicitly rejected - * bonding - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int UNBOND_REASON_AUTH_REJECTED = 2; - - /** - * A bond attempt failed because we canceled the bonding process - * - * @hide - */ - public static final int UNBOND_REASON_AUTH_CANCELED = 3; - - /** - * A bond attempt failed because we could not contact the remote device - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; - - /** - * A bond attempt failed because a discovery is in progress - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; - - /** - * A bond attempt failed because of authentication timeout - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; - - /** - * A bond attempt failed because of repeated attempts - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; - - /** - * A bond attempt failed because we received an Authentication Cancel - * by remote end - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; - - /** - * An existing bond was explicitly revoked - * - * @hide - */ - public static final int UNBOND_REASON_REMOVED = 9; - - /** - * The user will be prompted to enter a pin or - * an app will enter a pin for user. - */ - public static final int PAIRING_VARIANT_PIN = 0; - - /** - * The user will be prompted to enter a passkey - * - * @hide - */ - public static final int PAIRING_VARIANT_PASSKEY = 1; - - /** - * The user will be prompted to confirm the passkey displayed on the screen or - * an app will confirm the passkey for the user. - */ - public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; - - /** - * The user will be prompted to accept or deny the incoming pairing request - * - * @hide - */ - public static final int PAIRING_VARIANT_CONSENT = 3; - - /** - * The user will be prompted to enter the passkey displayed on remote device - * This is used for Bluetooth 2.1 pairing. - * - * @hide - */ - public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; - - /** - * The user will be prompted to enter the PIN displayed on remote device. - * This is used for Bluetooth 2.0 pairing. - * - * @hide - */ - public static final int PAIRING_VARIANT_DISPLAY_PIN = 5; - - /** - * The user will be prompted to accept or deny the OOB pairing request - * - * @hide - */ - public static final int PAIRING_VARIANT_OOB_CONSENT = 6; - - /** - * The user will be prompted to enter a 16 digit pin or - * an app will enter a 16 digit pin for user. - * - * @hide - */ - public static final int PAIRING_VARIANT_PIN_16_DIGITS = 7; - - /** - * Used as an extra field in {@link #ACTION_UUID} intents, - * Contains the {@link android.os.ParcelUuid}s of the remote device which - * is a parcelable version of {@link UUID}. - */ - public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; - - /** @hide */ - public static final String EXTRA_SDP_RECORD = - "android.bluetooth.device.extra.SDP_RECORD"; - - /** @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final String EXTRA_SDP_SEARCH_STATUS = - "android.bluetooth.device.extra.SDP_SEARCH_STATUS"; - - /** @hide */ - @IntDef(prefix = "ACCESS_", value = {ACCESS_UNKNOWN, - ACCESS_ALLOWED, ACCESS_REJECTED}) - @Retention(RetentionPolicy.SOURCE) - public @interface AccessPermission{} - - /** - * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, - * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. - * - * @hide - */ - @SystemApi - public static final int ACCESS_UNKNOWN = 0; - - /** - * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, - * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. - * - * @hide - */ - @SystemApi - public static final int ACCESS_ALLOWED = 1; - - /** - * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission}, - * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}. - * - * @hide - */ - @SystemApi - public static final int ACCESS_REJECTED = 2; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - prefix = { "TRANSPORT_" }, - value = { - /** Allow host to automatically select a transport (dual-mode only) */ - TRANSPORT_AUTO, - /** Use Classic or BR/EDR transport.*/ - TRANSPORT_BREDR, - /** Use Low Energy transport.*/ - TRANSPORT_LE, - } - ) - public @interface Transport {} - - /** - * No preference of physical transport for GATT connections to remote dual-mode devices - */ - public static final int TRANSPORT_AUTO = 0; - - /** - * Prefer BR/EDR transport for GATT connections to remote dual-mode devices - */ - public static final int TRANSPORT_BREDR = 1; - - /** - * Prefer LE transport for GATT connections to remote dual-mode devices - */ - public static final int TRANSPORT_LE = 2; - - /** - * Bluetooth LE 1M PHY. Used to refer to LE 1M Physical Channel for advertising, scanning or - * connection. - */ - public static final int PHY_LE_1M = 1; - - /** - * Bluetooth LE 2M PHY. Used to refer to LE 2M Physical Channel for advertising, scanning or - * connection. - */ - public static final int PHY_LE_2M = 2; - - /** - * Bluetooth LE Coded PHY. Used to refer to LE Coded Physical Channel for advertising, scanning - * or connection. - */ - public static final int PHY_LE_CODED = 3; - - /** - * Bluetooth LE 1M PHY mask. Used to specify LE 1M Physical Channel as one of many available - * options in a bitmask. - */ - public static final int PHY_LE_1M_MASK = 1; - - /** - * Bluetooth LE 2M PHY mask. Used to specify LE 2M Physical Channel as one of many available - * options in a bitmask. - */ - public static final int PHY_LE_2M_MASK = 2; - - /** - * Bluetooth LE Coded PHY mask. Used to specify LE Coded Physical Channel as one of many - * available options in a bitmask. - */ - public static final int PHY_LE_CODED_MASK = 4; - - /** - * No preferred coding when transmitting on the LE Coded PHY. - */ - public static final int PHY_OPTION_NO_PREFERRED = 0; - - /** - * Prefer the S=2 coding to be used when transmitting on the LE Coded PHY. - */ - public static final int PHY_OPTION_S2 = 1; - - /** - * Prefer the S=8 coding to be used when transmitting on the LE Coded PHY. - */ - public static final int PHY_OPTION_S8 = 2; - - - /** @hide */ - public static final String EXTRA_MAS_INSTANCE = - "android.bluetooth.device.extra.MAS_INSTANCE"; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - prefix = { "ADDRESS_TYPE_" }, - value = { - /** Hardware MAC Address */ - ADDRESS_TYPE_PUBLIC, - /** Address is either resolvable, non-resolvable or static.*/ - ADDRESS_TYPE_RANDOM, - } - ) - public @interface AddressType {} - - /** Hardware MAC Address of the device */ - public static final int ADDRESS_TYPE_PUBLIC = 0; - /** Address is either resolvable, non-resolvable or static. */ - public static final int ADDRESS_TYPE_RANDOM = 1; - - private static final String NULL_MAC_ADDRESS = "00:00:00:00:00:00"; - - /** - * Lazy initialization. Guaranteed final after first object constructed, or - * getService() called. - * TODO: Unify implementation of sService amongst BluetoothFoo API's - */ - private static volatile IBluetooth sService; - - private final String mAddress; - @AddressType private final int mAddressType; - - private AttributionSource mAttributionSource; - - /*package*/ - @UnsupportedAppUsage - static IBluetooth getService() { - synchronized (BluetoothDevice.class) { - if (sService == null) { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - sService = adapter.getBluetoothService(sStateChangeCallback); - } - } - return sService; - } - - static IBluetoothManagerCallback sStateChangeCallback = new IBluetoothManagerCallback.Stub() { - - public void onBluetoothServiceUp(IBluetooth bluetoothService) - throws RemoteException { - synchronized (BluetoothDevice.class) { - if (sService == null) { - sService = bluetoothService; - } - } - } - - public void onBluetoothServiceDown() - throws RemoteException { - synchronized (BluetoothDevice.class) { - sService = null; - } - } - - public void onBrEdrDown() { - if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state"); - } - - public void onOobData(@Transport int transport, OobData oobData) { - if (DBG) Log.d(TAG, "onOobData: got data"); - } - }; - - /** - * Create a new BluetoothDevice - * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB", - * and is validated in this constructor. - * - * @param address valid Bluetooth MAC address - * @param attributionSource attribution for permission-protected calls - * @throws RuntimeException Bluetooth is not available on this platform - * @throws IllegalArgumentException address is invalid - * @hide - */ - @UnsupportedAppUsage - /*package*/ BluetoothDevice(String address) { - getService(); // ensures sService is initialized - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - throw new IllegalArgumentException(address + " is not a valid Bluetooth address"); - } - - mAddress = address; - mAddressType = ADDRESS_TYPE_PUBLIC; - mAttributionSource = AttributionSource.myAttributionSource(); - } - - /** {@hide} */ - public void setAttributionSource(@NonNull AttributionSource attributionSource) { - mAttributionSource = attributionSource; - } - - /** {@hide} */ - public void prepareToEnterProcess(@NonNull AttributionSource attributionSource) { - setAttributionSource(attributionSource); - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof BluetoothDevice) { - return mAddress.equals(((BluetoothDevice) o).getAddress()); - } - return false; - } - - @Override - public int hashCode() { - return mAddress.hashCode(); - } - - /** - * Returns a string representation of this BluetoothDevice. - * <p>Currently this is the Bluetooth hardware address, for example - * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress} - * if you explicitly require the Bluetooth hardware address in case the - * {@link #toString} representation changes in the future. - * - * @return string representation of this BluetoothDevice - */ - @Override - public String toString() { - return mAddress; - } - - @Override - public int describeContents() { - return 0; - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothDevice> CREATOR = - new Parcelable.Creator<BluetoothDevice>() { - public BluetoothDevice createFromParcel(Parcel in) { - return new BluetoothDevice(in.readString()); - } - - public BluetoothDevice[] newArray(int size) { - return new BluetoothDevice[size]; - } - }; - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(mAddress); - } - - /** - * Returns the hardware address of this BluetoothDevice. - * <p> For example, "00:11:22:AA:BB:CC". - * - * @return Bluetooth hardware address as string - */ - public String getAddress() { - if (DBG) Log.d(TAG, "mAddress: " + mAddress); - return mAddress; - } - - /** - * Returns the anonymized hardware address of this BluetoothDevice. The first three octets - * will be suppressed for anonymization. - * <p> For example, "XX:XX:XX:AA:BB:CC". - * - * @return Anonymized bluetooth hardware address as string - * @hide - */ - public String getAnonymizedAddress() { - return "XX:XX:XX" + getAddress().substring(8); - } - - /** - * Get the friendly Bluetooth name of the remote device. - * - * <p>The local adapter will automatically retrieve remote names when - * performing a device scan, and will cache them. This method just returns - * the name for this device from the cache. - * - * @return the Bluetooth name, or null if there was a problem. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public String getName() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot get Remote Device name"); - return null; - } - try { - String name = service.getRemoteName(this, mAttributionSource); - if (name != null) { - // remove whitespace characters from the name - return name - .replace('\t', ' ') - .replace('\n', ' ') - .replace('\r', ' '); - } - return null; - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return null; - } - - /** - * Get the Bluetooth device type of the remote device. - * - * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} {@link - * #DEVICE_TYPE_DUAL}. {@link #DEVICE_TYPE_UNKNOWN} if it's not available - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getType() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot get Remote Device type"); - return DEVICE_TYPE_UNKNOWN; - } - try { - return service.getRemoteType(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return DEVICE_TYPE_UNKNOWN; - } - - /** - * Get the locally modifiable name (alias) of the remote Bluetooth device. - * - * @return the Bluetooth alias, the friendly device name if no alias, or - * null if there was a problem - */ - @Nullable - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public String getAlias() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot get Remote Device Alias"); - return null; - } - try { - String alias = service.getRemoteAliasWithAttribution(this, mAttributionSource); - if (alias == null) { - return getName(); - } - return alias - .replace('\t', ' ') - .replace('\n', ' ') - .replace('\r', ' '); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return null; - } - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - BluetoothStatusCodes.SUCCESS, - BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, - BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, - BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, - BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED - }) - public @interface SetAliasReturnValues{} - - /** - * Sets the locally modifiable name (alias) of the remote Bluetooth device. This method - * overwrites the previously stored alias. The new alias is saved in local - * storage so that the change is preserved over power cycles. - * - * <p>This method requires the calling app to be associated with Companion Device Manager (see - * {@link android.companion.CompanionDeviceManager#associate(AssociationRequest, - * android.companion.CompanionDeviceManager.Callback, Handler)}) and have the - * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission. Alternatively, if the - * caller has the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can - * bypass the Companion Device Manager association requirement as well as other permission - * requirements. - * - * @param alias is the new locally modifiable name for the remote Bluetooth device which must - * be the empty string. If null, we clear the alias. - * @return whether the alias was successfully changed - * @throws IllegalArgumentException if the alias is the empty string - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @SetAliasReturnValues int setAlias(@Nullable String alias) { - if (alias != null && alias.isEmpty()) { - throw new IllegalArgumentException("alias cannot be the empty string"); - } - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot set Remote Device name"); - return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; - } - try { - return service.setRemoteAlias(this, alias, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get the most recent identified battery level of this Bluetooth device - * - * @return Battery level in percents from 0 to 100, {@link #BATTERY_LEVEL_BLUETOOTH_OFF} if - * Bluetooth is disabled or {@link #BATTERY_LEVEL_UNKNOWN} if device is disconnected, or does - * not have any battery reporting service, or return value is invalid - * @hide - */ - @UnsupportedAppUsage - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getBatteryLevel() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level"); - return BATTERY_LEVEL_BLUETOOTH_OFF; - } - try { - return service.getBatteryLevel(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return BATTERY_LEVEL_UNKNOWN; - } - - /** - * Start the bonding (pairing) process with the remote device. - * <p>This is an asynchronous call, it will return immediately. Register - * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when - * the bonding process completes, and its result. - * <p>Android system services will handle the necessary user interactions - * to confirm and complete the bonding process. - * - * @return false on immediate error, true if bonding will begin - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean createBond() { - return createBond(TRANSPORT_AUTO); - } - - /** - * Start the bonding (pairing) process with the remote device using the - * specified transport. - * - * <p>This is an asynchronous call, it will return immediately. Register - * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when - * the bonding process completes, and its result. - * <p>Android system services will handle the necessary user interactions - * to confirm and complete the bonding process. - * - * @param transport The transport to use for the pairing procedure. - * @return false on immediate error, true if bonding will begin - * @throws IllegalArgumentException if an invalid transport was specified - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean createBond(int transport) { - return createBondInternal(transport, null, null); - } - - /** - * Start the bonding (pairing) process with the remote device using the - * Out Of Band mechanism. - * - * <p>This is an asynchronous call, it will return immediately. Register - * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when - * the bonding process completes, and its result. - * - * <p>Android system services will handle the necessary user interactions - * to confirm and complete the bonding process. - * - * <p>There are two possible versions of OOB Data. This data can come in as - * P192 or P256. This is a reference to the cryptography used to generate the key. - * The caller may pass one or both. If both types of data are passed, then the - * P256 data will be preferred, and thus used. - * - * @param transport - Transport to use - * @param remoteP192Data - Out Of Band data (P192) or null - * @param remoteP256Data - Out Of Band data (P256) or null - * @return false on immediate error, true if bonding will begin - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data, - @Nullable OobData remoteP256Data) { - if (remoteP192Data == null && remoteP256Data == null) { - throw new IllegalArgumentException( - "One or both arguments for the OOB data types are required to not be null." - + " Please use createBond() instead if you do not have OOB data to pass."); - } - return createBondInternal(transport, remoteP192Data, remoteP256Data); - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data, - @Nullable OobData remoteP256Data) { - final IBluetooth service = sService; - if (service == null) { - Log.w(TAG, "BT not enabled, createBondOutOfBand failed"); - return false; - } - if (NULL_MAC_ADDRESS.equals(mAddress)) { - Log.e(TAG, "Unable to create bond, invalid address " + mAddress); - return false; - } - try { - return service.createBond( - this, transport, remoteP192Data, remoteP256Data, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Gets whether bonding was initiated locally - * - * @return true if bonding is initiated locally, false otherwise - * - * @hide - */ - @UnsupportedAppUsage - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isBondingInitiatedLocally() { - final IBluetooth service = sService; - if (service == null) { - Log.w(TAG, "BT not enabled, isBondingInitiatedLocally failed"); - return false; - } - try { - return service.isBondingInitiatedLocally(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Cancel an in-progress bonding request started with {@link #createBond}. - * - * @return true on success, false on error - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean cancelBondProcess() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot cancel Remote Device bond"); - return false; - } - try { - Log.i(TAG, "cancelBondProcess() for device " + getAddress() - + " called by pid: " + Process.myPid() - + " tid: " + Process.myTid()); - return service.cancelBondProcess(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Remove bond (pairing) with the remote device. - * <p>Delete the link key associated with the remote device, and - * immediately terminate connections to that device that require - * authentication and encryption. - * - * @return true on success, false on error - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean removeBond() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot remove Remote Device bond"); - return false; - } - try { - Log.i(TAG, "removeBond() for device " + getAddress() - + " called by pid: " + Process.myPid() - + " tid: " + Process.myTid()); - return service.removeBond(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - private static final String BLUETOOTH_BONDING_CACHE_PROPERTY = - "cache_key.bluetooth.get_bond_state"; - private final PropertyInvalidatedCache<BluetoothDevice, Integer> mBluetoothBondCache = - new PropertyInvalidatedCache<BluetoothDevice, Integer>( - 8, BLUETOOTH_BONDING_CACHE_PROPERTY) { - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - protected Integer recompute(BluetoothDevice query) { - try { - return sService.getBondState(query, mAttributionSource); - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); - } - } - }; - - /** @hide */ - public void disableBluetoothGetBondStateCache() { - mBluetoothBondCache.disableLocal(); - } - - /** @hide */ - public static void invalidateBluetoothGetBondStateCache() { - PropertyInvalidatedCache.invalidateCache(BLUETOOTH_BONDING_CACHE_PROPERTY); - } - - /** - * Get the bond state of the remote device. - * <p>Possible values for the bond state are: - * {@link #BOND_NONE}, - * {@link #BOND_BONDING}, - * {@link #BOND_BONDED}. - * - * @return the bond state - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public int getBondState() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot get bond state"); - return BOND_NONE; - } - try { - return mBluetoothBondCache.query(this); - } catch (RuntimeException e) { - if (e.getCause() instanceof RemoteException) { - Log.e(TAG, "", e); - } else { - throw e; - } - } - return BOND_NONE; - } - - /** - * Checks whether this bluetooth device is associated with CDM and meets the criteria to skip - * the bluetooth pairing dialog because it has been already consented by the CDM prompt. - * - * @return true if we can bond without the dialog, false otherwise - * - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean canBondWithoutDialog() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot check if we can skip pairing dialog"); - return false; - } - try { - if (DBG) Log.d(TAG, "canBondWithoutDialog, device: " + this); - return service.canBondWithoutDialog(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - BluetoothStatusCodes.SUCCESS, - BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, - BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED, - BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, - BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED - }) - public @interface ConnectionReturnValues{} - - /** - * Connects all user enabled and supported bluetooth profiles between the local and remote - * device. If no profiles are user enabled (e.g. first connection), we connect all supported - * profiles. If the device is not already connected, this will page the device before initiating - * profile connections. Connection is asynchronous and you should listen to each profile's - * broadcast intent ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful. - * For example, to verify a2dp is connected, you would listen for - * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED} - * - * @return whether the messages were successfully sent to try to connect all profiles - * @throws IllegalArgumentException if the device address is invalid - * - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public @ConnectionReturnValues int connect() { - if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) { - throw new IllegalArgumentException("device cannot have an invalid address"); - } - - try { - if (sService == null) { - Log.e(TAG, "BT not enabled. Cannot connect to remote device."); - return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; - } - return sService.connectAllEnabledProfiles(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - throw e.rethrowFromSystemServer(); - } - } - - /** - * Disconnects all connected bluetooth profiles between the local and remote device. - * Disconnection is asynchronous and you should listen to each profile's broadcast intent - * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example, - * to verify a2dp is disconnected, you would listen for - * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED} - * - * @return whether the messages were successfully sent to try to disconnect all profiles - * @throws IllegalArgumentException if the device address is invalid - * - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @ConnectionReturnValues int disconnect() { - if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) { - throw new IllegalArgumentException("device cannot have an invalid address"); - } - - try { - if (sService == null) { - Log.e(TAG, "BT not enabled. Cannot disconnect from remote device."); - return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; - } - return sService.disconnectAllEnabledProfiles(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - throw e.rethrowFromSystemServer(); - } - } - - /** - * Returns whether there is an open connection to this device. - * - * @return True if there is at least one open connection to this device. - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isConnected() { - final IBluetooth service = sService; - if (service == null) { - // BT is not enabled, we cannot be connected. - return false; - } - try { - return service.getConnectionStateWithAttribution(this, mAttributionSource) - != CONNECTION_STATE_DISCONNECTED; - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - } - - /** - * Returns whether there is an open connection to this device - * that has been encrypted. - * - * @return True if there is at least one encrypted connection to this device. - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isEncrypted() { - final IBluetooth service = sService; - if (service == null) { - // BT is not enabled, we cannot be connected. - return false; - } - try { - return service.getConnectionStateWithAttribution(this, mAttributionSource) - > CONNECTION_STATE_CONNECTED; - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - } - - /** - * Get the Bluetooth class of the remote device. - * - * @return Bluetooth class object, or null on error - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothClass getBluetoothClass() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class"); - return null; - } - try { - int classInt = service.getRemoteClass(this, mAttributionSource); - if (classInt == BluetoothClass.ERROR) return null; - return new BluetoothClass(classInt); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return null; - } - - /** - * Returns the supported features (UUIDs) of the remote device. - * - * <p>This method does not start a service discovery procedure to retrieve the UUIDs - * from the remote device. Instead, the local cached copy of the service - * UUIDs are returned. - * <p>Use {@link #fetchUuidsWithSdp} if fresh UUIDs are desired. - * - * @return the supported features (UUIDs) of the remote device, or null on error - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public ParcelUuid[] getUuids() { - final IBluetooth service = sService; - if (service == null || !isBluetoothEnabled()) { - Log.e(TAG, "BT not enabled. Cannot get remote device Uuids"); - return null; - } - try { - return service.getRemoteUuids(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return null; - } - - /** - * Perform a service discovery on the remote device to get the UUIDs supported. - * - * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent, - * with the UUIDs supported by the remote end. If there is an error - * in getting the SDP records or if the process takes a long time, or the device is bonding and - * we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the UUIDs that is - * currently present in the cache. Clients should use the {@link #getUuids} to get UUIDs - * if service discovery is not to be performed. If there is an ongoing bonding process, - * service discovery or device inquiry, the request will be queued. - * - * @return False if the check fails, True if the process of initiating an ACL connection - * to the remote device was started or cached UUIDs will be broadcast. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean fetchUuidsWithSdp() { - return fetchUuidsWithSdp(TRANSPORT_AUTO); - } - - /** - * Perform a service discovery on the remote device to get the UUIDs supported with the - * specific transport. - * - * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent, - * with the UUIDs supported by the remote end. If there is an error - * in getting the SDP or GATT records or if the process takes a long time, or the device - * is bonding and we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the - * UUIDs that is currently present in the cache. Clients should use the {@link #getUuids} - * to get UUIDs if service discovery is not to be performed. If there is an ongoing bonding - * process, service discovery or device inquiry, the request will be queued. - * - * @param transport - provide type of transport (e.g. LE or Classic). - * @return False if the check fails, True if the process of initiating an ACL connection - * to the remote device was started or cached UUIDs will be broadcast with the specific - * transport. - * - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean fetchUuidsWithSdp(@Transport int transport) { - final IBluetooth service = sService; - if (service == null || !isBluetoothEnabled()) { - Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp"); - return false; - } - try { - return service.fetchRemoteUuidsWithAttribution(this, transport, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Perform a service discovery on the remote device to get the SDP records associated - * with the specified UUID. - * - * <p>This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent, - * with the SDP records found on the remote end. If there is an error - * in getting the SDP records or if the process takes a long time, - * {@link #ACTION_SDP_RECORD} intent is sent with an status value in - * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0. - * Detailed status error codes can be found by members of the Bluetooth package in - * the AbstractionLayer class. - * <p>The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}. - * The object type will match one of the SdpXxxRecord types, depending on the UUID searched - * for. - * - * @return False if the check fails, True if the process - * of initiating an ACL connection to the remote device - * was started. - */ - /** @hide */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean sdpSearch(ParcelUuid uuid) { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot query remote device sdp records"); - return false; - } - try { - return service.sdpSearch(this, uuid, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} - * - * @return true pin has been set false for error - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setPin(byte[] pin) { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot set Remote Device pin"); - return false; - } - try { - return service.setPin(this, true, pin.length, pin, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} - * - * @return true pin has been set false for error - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setPin(@NonNull String pin) { - byte[] pinBytes = convertPinToBytes(pin); - if (pinBytes == null) { - return false; - } - return setPin(pinBytes); - } - - /** - * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. - * - * @return true confirmation has been sent out false for error - */ - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setPairingConfirmation(boolean confirm) { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); - return false; - } - try { - return service.setPairingConfirmation(this, confirm, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Cancels pairing to this device - * - * @return true if pairing cancelled successfully, false otherwise - * - * @hide - */ - @UnsupportedAppUsage - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean cancelPairing() { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "BT not enabled. Cannot cancel pairing"); - return false; - } - try { - return service.cancelBondProcess(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - boolean isBluetoothEnabled() { - boolean ret = false; - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter != null && adapter.isEnabled()) { - ret = true; - } - return ret; - } - - /** - * Gets whether the phonebook access is allowed for this bluetooth device - * - * @return Whether the phonebook access is allowed to this device. Can be {@link - * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}. - * @hide - */ - @UnsupportedAppUsage - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @AccessPermission int getPhonebookAccessPermission() { - final IBluetooth service = sService; - if (service == null) { - return ACCESS_UNKNOWN; - } - try { - return service.getPhonebookAccessPermission(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return ACCESS_UNKNOWN; - } - - /** - * Sets whether the {@link BluetoothDevice} enters silence mode. Audio will not - * be routed to the {@link BluetoothDevice} if set to {@code true}. - * - * When the {@link BluetoothDevice} enters silence mode, and the {@link BluetoothDevice} - * is an active device (for A2DP or HFP), the active device for that profile - * will be set to null. - * If the {@link BluetoothDevice} exits silence mode while the A2DP or HFP - * active device is null, the {@link BluetoothDevice} will be set as the - * active device for that profile. - * If the {@link BluetoothDevice} is disconnected, it exits silence mode. - * If the {@link BluetoothDevice} is set as the active device for A2DP or - * HFP, while silence mode is enabled, then the device will exit silence mode. - * If the {@link BluetoothDevice} is in silence mode, AVRCP position change - * event and HFP AG indicators will be disabled. - * If the {@link BluetoothDevice} is not connected with A2DP or HFP, it cannot - * enter silence mode. - * - * @param silence true to enter silence mode, false to exit - * @return true on success, false on error. - * @throws IllegalStateException if Bluetooth is not turned ON. - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setSilenceMode(boolean silence) { - final IBluetooth service = sService; - if (service == null) { - throw new IllegalStateException("Bluetooth is not turned ON"); - } - try { - return service.setSilenceMode(this, silence, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "setSilenceMode fail", e); - return false; - } - } - - /** - * Check whether the {@link BluetoothDevice} is in silence mode - * - * @return true on device in silence mode, otherwise false. - * @throws IllegalStateException if Bluetooth is not turned ON. - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean isInSilenceMode() { - final IBluetooth service = sService; - if (service == null) { - throw new IllegalStateException("Bluetooth is not turned ON"); - } - try { - return service.getSilenceMode(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "isInSilenceMode fail", e); - return false; - } - } - - /** - * Sets whether the phonebook access is allowed to this device. - * - * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link - * #ACCESS_REJECTED}. - * @return Whether the value has been successfully set. - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setPhonebookAccessPermission(@AccessPermission int value) { - final IBluetooth service = sService; - if (service == null) { - return false; - } - try { - return service.setPhonebookAccessPermission(this, value, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Gets whether message access is allowed to this bluetooth device - * - * @return Whether the message access is allowed to this device. - * @hide - */ - @UnsupportedAppUsage - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @AccessPermission int getMessageAccessPermission() { - final IBluetooth service = sService; - if (service == null) { - return ACCESS_UNKNOWN; - } - try { - return service.getMessageAccessPermission(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return ACCESS_UNKNOWN; - } - - /** - * Sets whether the message access is allowed to this device. - * - * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded, - * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if - * the permission is not being granted. - * @return Whether the value has been successfully set. - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setMessageAccessPermission(@AccessPermission int value) { - // Validates param value is one of the accepted constants - if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) { - throw new IllegalArgumentException(value + "is not a valid AccessPermission value"); - } - final IBluetooth service = sService; - if (service == null) { - return false; - } - try { - return service.setMessageAccessPermission(this, value, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Gets whether sim access is allowed for this bluetooth device - * - * @return Whether the Sim access is allowed to this device. - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @AccessPermission int getSimAccessPermission() { - final IBluetooth service = sService; - if (service == null) { - return ACCESS_UNKNOWN; - } - try { - return service.getSimAccessPermission(this, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return ACCESS_UNKNOWN; - } - - /** - * Sets whether the Sim access is allowed to this device. - * - * @param value Can be {@link #ACCESS_UNKNOWN} if the device is unbonded, - * {@link #ACCESS_ALLOWED} if the permission is being granted, or {@link #ACCESS_REJECTED} if - * the permission is not being granted. - * @return Whether the value has been successfully set. - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setSimAccessPermission(int value) { - final IBluetooth service = sService; - if (service == null) { - return false; - } - try { - return service.setSimAccessPermission(this, value, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return false; - } - - /** - * Create an RFCOMM {@link BluetoothSocket} ready to start a secure - * outgoing connection to this remote device on given channel. - * <p>The remote device will be authenticated and communication on this - * socket will be encrypted. - * <p> Use this socket only if an authenticated socket link is possible. - * Authentication refers to the authentication of the link key to - * prevent person-in-the-middle type of attacks. - * For example, for Bluetooth 2.1 devices, if any of the devices does not - * have an input and output capability or just has the ability to - * display a numeric key, a secure socket connection is not possible. - * In such a case, use {@link createInsecureRfcommSocket}. - * For more details, refer to the Security Model section 5.2 (vol 3) of - * Bluetooth Core Specification version 2.1 + EDR. - * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing - * connection. - * <p>Valid RFCOMM channels are in range 1 to 30. - * - * @param channel RFCOMM channel to connect to - * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions - * @hide - */ - @UnsupportedAppUsage - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public BluetoothSocket createRfcommSocket(int channel) throws IOException { - if (!isBluetoothEnabled()) { - Log.e(TAG, "Bluetooth is not enabled"); - throw new IOException(); - } - return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel, - null); - } - - /** - * Create an L2cap {@link BluetoothSocket} ready to start a secure - * outgoing connection to this remote device on given channel. - * <p>The remote device will be authenticated and communication on this - * socket will be encrypted. - * <p> Use this socket only if an authenticated socket link is possible. - * Authentication refers to the authentication of the link key to - * prevent person-in-the-middle type of attacks. - * For example, for Bluetooth 2.1 devices, if any of the devices does not - * have an input and output capability or just has the ability to - * display a numeric key, a secure socket connection is not possible. - * In such a case, use {@link createInsecureRfcommSocket}. - * For more details, refer to the Security Model section 5.2 (vol 3) of - * Bluetooth Core Specification version 2.1 + EDR. - * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing - * connection. - * <p>Valid L2CAP PSM channels are in range 1 to 2^16. - * - * @param channel L2cap PSM/channel to connect to - * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public BluetoothSocket createL2capSocket(int channel) throws IOException { - return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, true, true, this, channel, - null); - } - - /** - * Create an L2cap {@link BluetoothSocket} ready to start an insecure - * outgoing connection to this remote device on given channel. - * <p>The remote device will be not authenticated and communication on this - * socket will not be encrypted. - * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing - * connection. - * <p>Valid L2CAP PSM channels are in range 1 to 2^16. - * - * @param channel L2cap PSM/channel to connect to - * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException { - return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel, - null); - } - - /** - * Create an RFCOMM {@link BluetoothSocket} ready to start a secure - * outgoing connection to this remote device using SDP lookup of uuid. - * <p>This is designed to be used with {@link - * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer - * Bluetooth applications. - * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing - * connection. This will also perform an SDP lookup of the given uuid to - * determine which channel to connect to. - * <p>The remote device will be authenticated and communication on this - * socket will be encrypted. - * <p> Use this socket only if an authenticated socket link is possible. - * Authentication refers to the authentication of the link key to - * prevent person-in-the-middle type of attacks. - * For example, for Bluetooth 2.1 devices, if any of the devices does not - * have an input and output capability or just has the ability to - * display a numeric key, a secure socket connection is not possible. - * In such a case, use {@link #createInsecureRfcommSocketToServiceRecord}. - * For more details, refer to the Security Model section 5.2 (vol 3) of - * Bluetooth Core Specification version 2.1 + EDR. - * <p>Hint: If you are connecting to a Bluetooth serial board then try - * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. - * However if you are connecting to an Android peer then please generate - * your own unique UUID. - * - * @param uuid service record uuid to lookup RFCOMM channel - * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { - if (!isBluetoothEnabled()) { - Log.e(TAG, "Bluetooth is not enabled"); - throw new IOException(); - } - - return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1, - new ParcelUuid(uuid)); - } - - /** - * Create an RFCOMM {@link BluetoothSocket} socket ready to start an insecure - * outgoing connection to this remote device using SDP lookup of uuid. - * <p> The communication channel will not have an authenticated link key - * i.e it will be subject to person-in-the-middle attacks. For Bluetooth 2.1 - * devices, the link key will be encrypted, as encryption is mandatory. - * For legacy devices (pre Bluetooth 2.1 devices) the link key will - * be not be encrypted. Use {@link #createRfcommSocketToServiceRecord} if an - * encrypted and authenticated communication channel is desired. - * <p>This is designed to be used with {@link - * BluetoothAdapter#listenUsingInsecureRfcommWithServiceRecord} for peer-peer - * Bluetooth applications. - * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing - * connection. This will also perform an SDP lookup of the given uuid to - * determine which channel to connect to. - * <p>The remote device will be authenticated and communication on this - * socket will be encrypted. - * <p>Hint: If you are connecting to a Bluetooth serial board then try - * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. - * However if you are connecting to an Android peer then please generate - * your own unique UUID. - * - * @param uuid service record uuid to lookup RFCOMM channel - * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { - if (!isBluetoothEnabled()) { - Log.e(TAG, "Bluetooth is not enabled"); - throw new IOException(); - } - return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1, - new ParcelUuid(uuid)); - } - - /** - * Construct an insecure RFCOMM socket ready to start an outgoing - * connection. - * Call #connect on the returned #BluetoothSocket to begin the connection. - * The remote device will not be authenticated and communication on this - * socket will not be encrypted. - * - * @param port remote port - * @return An RFCOMM BluetoothSocket - * @throws IOException On error, for example Bluetooth not available, or insufficient - * permissions. - * @hide - */ - @UnsupportedAppUsage(publicAlternatives = "Use " - + "{@link #createInsecureRfcommSocketToServiceRecord} instead.") - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { - if (!isBluetoothEnabled()) { - Log.e(TAG, "Bluetooth is not enabled"); - throw new IOException(); - } - return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port, - null); - } - - /** - * Construct a SCO socket ready to start an outgoing connection. - * Call #connect on the returned #BluetoothSocket to begin the connection. - * - * @return a SCO BluetoothSocket - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions. - * @hide - */ - @UnsupportedAppUsage - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public BluetoothSocket createScoSocket() throws IOException { - if (!isBluetoothEnabled()) { - Log.e(TAG, "Bluetooth is not enabled"); - throw new IOException(); - } - return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null); - } - - /** - * Check that a pin is valid and convert to byte array. - * - * Bluetooth pin's are 1 to 16 bytes of UTF-8 characters. - * - * @param pin pin as java String - * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin. - * @hide - */ - @UnsupportedAppUsage - public static byte[] convertPinToBytes(String pin) { - if (pin == null) { - return null; - } - byte[] pinBytes; - try { - pinBytes = pin.getBytes("UTF-8"); - } catch (UnsupportedEncodingException uee) { - Log.e(TAG, "UTF-8 not supported?!?"); // this should not happen - return null; - } - if (pinBytes.length <= 0 || pinBytes.length > 16) { - return null; - } - return pinBytes; - } - - /** - * Connect to GATT Server hosted by this device. Caller acts as GATT client. - * The callback is used to deliver results to Caller, such as connection status as well - * as any further GATT client operations. - * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct - * GATT client operations. - * - * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) or to - * automatically connect as soon as the remote device becomes available (true). - * @throws IllegalArgumentException if callback is null - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback) { - return (connectGatt(context, autoConnect, callback, TRANSPORT_AUTO)); - } - - /** - * Connect to GATT Server hosted by this device. Caller acts as GATT client. - * The callback is used to deliver results to Caller, such as connection status as well - * as any further GATT client operations. - * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct - * GATT client operations. - * - * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) or to - * automatically connect as soon as the remote device becomes available (true). - * @param transport preferred transport for GATT connections to remote dual-mode devices {@link - * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link - * BluetoothDevice#TRANSPORT_LE} - * @throws IllegalArgumentException if callback is null - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback, int transport) { - return (connectGatt(context, autoConnect, callback, transport, PHY_LE_1M_MASK)); - } - - /** - * Connect to GATT Server hosted by this device. Caller acts as GATT client. - * The callback is used to deliver results to Caller, such as connection status as well - * as any further GATT client operations. - * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct - * GATT client operations. - * - * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) or to - * automatically connect as soon as the remote device becomes available (true). - * @param transport preferred transport for GATT connections to remote dual-mode devices {@link - * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link - * BluetoothDevice#TRANSPORT_LE} - * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link - * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link - * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect} - * is set to true. - * @throws NullPointerException if callback is null - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback, int transport, int phy) { - return connectGatt(context, autoConnect, callback, transport, phy, null); - } - - /** - * Connect to GATT Server hosted by this device. Caller acts as GATT client. - * The callback is used to deliver results to Caller, such as connection status as well - * as any further GATT client operations. - * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct - * GATT client operations. - * - * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) or to - * automatically connect as soon as the remote device becomes available (true). - * @param transport preferred transport for GATT connections to remote dual-mode devices {@link - * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link - * BluetoothDevice#TRANSPORT_LE} - * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link - * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link - * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect} - * is set to true. - * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on - * an un-specified background thread. - * @throws NullPointerException if callback is null - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback, int transport, int phy, - Handler handler) { - return connectGatt(context, autoConnect, callback, transport, false, phy, handler); - } - - /** - * Connect to GATT Server hosted by this device. Caller acts as GATT client. - * The callback is used to deliver results to Caller, such as connection status as well - * as any further GATT client operations. - * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct - * GATT client operations. - * - * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param autoConnect Whether to directly connect to the remote device (false) or to - * automatically connect as soon as the remote device becomes available (true). - * @param transport preferred transport for GATT connections to remote dual-mode devices {@link - * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link - * BluetoothDevice#TRANSPORT_LE} - * @param opportunistic Whether this GATT client is opportunistic. An opportunistic GATT client - * does not hold a GATT connection. It automatically disconnects when no other GATT connections - * are active for the remote device. - * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link - * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link - * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect} - * is set to true. - * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on - * an un-specified background thread. - * @return A BluetoothGatt instance. You can use BluetoothGatt to conduct GATT client - * operations. - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothGatt connectGatt(Context context, boolean autoConnect, - BluetoothGattCallback callback, int transport, - boolean opportunistic, int phy, Handler handler) { - if (callback == null) { - throw new NullPointerException("callback is null"); - } - - // TODO(Bluetooth) check whether platform support BLE - // Do the check here or in GattServer? - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager managerService = adapter.getBluetoothManager(); - try { - IBluetoothGatt iGatt = managerService.getBluetoothGatt(); - if (iGatt == null) { - // BLE is not supported - return null; - } - BluetoothGatt gatt = new BluetoothGatt( - iGatt, this, transport, opportunistic, phy, mAttributionSource); - gatt.connect(autoConnect, callback, handler); - return gatt; - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - return null; - } - - /** - * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can - * be used to start a secure outgoing connection to the remote device with the same dynamic - * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only. - * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capChannel()} for - * peer-peer Bluetooth applications. - * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection. - * <p>Application using this API is responsible for obtaining PSM value from remote device. - * <p>The remote device will be authenticated and communication on this socket will be - * encrypted. - * <p> Use this socket if an authenticated socket link is possible. Authentication refers - * to the authentication of the link key to prevent person-in-the-middle type of attacks. - * - * @param psm dynamic PSM value from remote device - * @return a CoC #BluetoothSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public @NonNull BluetoothSocket createL2capChannel(int psm) throws IOException { - if (!isBluetoothEnabled()) { - Log.e(TAG, "createL2capChannel: Bluetooth is not enabled"); - throw new IOException(); - } - if (DBG) Log.d(TAG, "createL2capChannel: psm=" + psm); - return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm, - null); - } - - /** - * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can - * be used to start a secure outgoing connection to the remote device with the same dynamic - * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only. - * <p>This is designed to be used with {@link - * BluetoothAdapter#listenUsingInsecureL2capChannel()} for peer-peer Bluetooth applications. - * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection. - * <p>Application using this API is responsible for obtaining PSM value from remote device. - * <p> The communication channel may not have an authenticated link key, i.e. it may be subject - * to person-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and - * authenticated communication channel is possible. - * - * @param psm dynamic PSM value from remote device - * @return a CoC #BluetoothSocket ready for an outgoing connection - * @throws IOException on error, for example Bluetooth not available, or insufficient - * permissions - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public @NonNull BluetoothSocket createInsecureL2capChannel(int psm) throws IOException { - if (!isBluetoothEnabled()) { - Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled"); - throw new IOException(); - } - if (DBG) { - Log.d(TAG, "createInsecureL2capChannel: psm=" + psm); - } - return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm, - null); - } - - /** - * Set a keyed metadata of this {@link BluetoothDevice} to a - * {@link String} value. - * Only bonded devices's metadata will be persisted across Bluetooth - * restart. - * Metadata will be removed when the device's bond state is moved to - * {@link #BOND_NONE}. - * - * @param key must be within the list of BluetoothDevice.METADATA_* - * @param value a byte array data to set for key. Must be less than - * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length - * @return true on success, false on error - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata"); - return false; - } - if (value.length > METADATA_MAX_LENGTH) { - throw new IllegalArgumentException("value length is " + value.length - + ", should not over " + METADATA_MAX_LENGTH); - } - try { - return service.setMetadata(this, key, value, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "setMetadata fail", e); - return false; - } - } - - /** - * Get a keyed metadata for this {@link BluetoothDevice} as {@link String} - * - * @param key must be within the list of BluetoothDevice.METADATA_* - * @return Metadata of the key as byte array, null on error or not found - * @hide - */ - @SystemApi - @Nullable - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public byte[] getMetadata(@MetadataKey int key) { - final IBluetooth service = sService; - if (service == null) { - Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata"); - return null; - } - try { - return service.getMetadata(this, key, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "getMetadata fail", e); - return null; - } - } - - /** - * Get the maxinum metadata key ID. - * - * @return the last supported metadata key - * @hide - */ - public static @MetadataKey int getMaxMetadataKey() { - return METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD; - } -} diff --git a/core/java/android/bluetooth/BluetoothDevicePicker.java b/core/java/android/bluetooth/BluetoothDevicePicker.java deleted file mode 100644 index 26e46573dd95..000000000000 --- a/core/java/android/bluetooth/BluetoothDevicePicker.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2009 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.bluetooth; - -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; - -/** - * A helper to show a system "Device Picker" activity to the user. - * - * @hide - */ -public interface BluetoothDevicePicker { - public static final String EXTRA_NEED_AUTH = - "android.bluetooth.devicepicker.extra.NEED_AUTH"; - public static final String EXTRA_FILTER_TYPE = - "android.bluetooth.devicepicker.extra.FILTER_TYPE"; - public static final String EXTRA_LAUNCH_PACKAGE = - "android.bluetooth.devicepicker.extra.LAUNCH_PACKAGE"; - public static final String EXTRA_LAUNCH_CLASS = - "android.bluetooth.devicepicker.extra.DEVICE_PICKER_LAUNCH_CLASS"; - - /** - * Broadcast when one BT device is selected from BT device picker screen. - * Selected {@link BluetoothDevice} is returned in extra data named - * {@link BluetoothDevice#EXTRA_DEVICE}. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_DEVICE_SELECTED = - "android.bluetooth.devicepicker.action.DEVICE_SELECTED"; - - /** - * Broadcast when someone want to select one BT device from devices list. - * This intent contains below extra data: - * - {@link #EXTRA_NEED_AUTH} (boolean): if need authentication - * - {@link #EXTRA_FILTER_TYPE} (int): what kinds of device should be - * listed - * - {@link #EXTRA_LAUNCH_PACKAGE} (string): where(which package) this - * intent come from - * - {@link #EXTRA_LAUNCH_CLASS} (string): where(which class) this intent - * come from - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_LAUNCH = - "android.bluetooth.devicepicker.action.LAUNCH"; - - /** Ask device picker to show all kinds of BT devices */ - public static final int FILTER_TYPE_ALL = 0; - /** Ask device picker to show BT devices that support AUDIO profiles */ - public static final int FILTER_TYPE_AUDIO = 1; - /** Ask device picker to show BT devices that support Object Transfer */ - public static final int FILTER_TYPE_TRANSFER = 2; - /** - * Ask device picker to show BT devices that support - * Personal Area Networking User (PANU) profile - */ - public static final int FILTER_TYPE_PANU = 3; - /** Ask device picker to show BT devices that support Network Access Point (NAP) profile */ - public static final int FILTER_TYPE_NAP = 4; -} diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java deleted file mode 100644 index b531829d2940..000000000000 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ /dev/null @@ -1,1848 +0,0 @@ -/* - * Copyright (C) 2013 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.bluetooth; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.RequiresNoPermission; -import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.os.Build; -import android.os.Handler; -import android.os.ParcelUuid; -import android.os.RemoteException; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -/** - * Public API for the Bluetooth GATT Profile. - * - * <p>This class provides Bluetooth GATT functionality to enable communication - * with Bluetooth Smart or Smart Ready devices. - * - * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback} - * and call {@link BluetoothDevice#connectGatt} to get a instance of this class. - * GATT capable devices can be discovered using the Bluetooth device discovery or BLE - * scan process. - */ -public final class BluetoothGatt implements BluetoothProfile { - private static final String TAG = "BluetoothGatt"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - @UnsupportedAppUsage - private IBluetoothGatt mService; - @UnsupportedAppUsage - private volatile BluetoothGattCallback mCallback; - private Handler mHandler; - @UnsupportedAppUsage - private int mClientIf; - private BluetoothDevice mDevice; - @UnsupportedAppUsage - private boolean mAutoConnect; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private int mAuthRetryState; - private int mConnState; - private final Object mStateLock = new Object(); - private final Object mDeviceBusyLock = new Object(); - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private Boolean mDeviceBusy = false; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private int mTransport; - private int mPhy; - private boolean mOpportunistic; - private final AttributionSource mAttributionSource; - - private static final int AUTH_RETRY_STATE_IDLE = 0; - private static final int AUTH_RETRY_STATE_NO_MITM = 1; - private static final int AUTH_RETRY_STATE_MITM = 2; - - private static final int CONN_STATE_IDLE = 0; - private static final int CONN_STATE_CONNECTING = 1; - private static final int CONN_STATE_CONNECTED = 2; - private static final int CONN_STATE_DISCONNECTING = 3; - private static final int CONN_STATE_CLOSED = 4; - - private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5; - private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 10; // milliseconds - - private List<BluetoothGattService> mServices; - - /** A GATT operation completed successfully */ - public static final int GATT_SUCCESS = 0; - - /** GATT read operation is not permitted */ - public static final int GATT_READ_NOT_PERMITTED = 0x2; - - /** GATT write operation is not permitted */ - public static final int GATT_WRITE_NOT_PERMITTED = 0x3; - - /** Insufficient authentication for a given operation */ - public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5; - - /** The given request is not supported */ - public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6; - - /** Insufficient encryption for a given operation */ - public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf; - - /** A read or write operation was requested with an invalid offset */ - public static final int GATT_INVALID_OFFSET = 0x7; - - /** Insufficient authorization for a given operation */ - public static final int GATT_INSUFFICIENT_AUTHORIZATION = 0x8; - - /** A write operation exceeds the maximum length of the attribute */ - public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; - - /** A remote device connection is congested. */ - public static final int GATT_CONNECTION_CONGESTED = 0x8f; - - /** A GATT operation failed, errors other than the above */ - public static final int GATT_FAILURE = 0x101; - - /** - * Connection parameter update - Use the connection parameters recommended by the - * Bluetooth SIG. This is the default value if no connection parameter update - * is requested. - */ - public static final int CONNECTION_PRIORITY_BALANCED = 0; - - /** - * Connection parameter update - Request a high priority, low latency connection. - * An application should only request high priority connection parameters to transfer large - * amounts of data over LE quickly. Once the transfer is complete, the application should - * request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters to reduce - * energy use. - */ - public static final int CONNECTION_PRIORITY_HIGH = 1; - - /** Connection parameter update - Request low power, reduced data rate connection parameters. */ - public static final int CONNECTION_PRIORITY_LOW_POWER = 2; - - /** - * No authentication required. - * - * @hide - */ - /*package*/ static final int AUTHENTICATION_NONE = 0; - - /** - * Authentication requested; no person-in-the-middle protection required. - * - * @hide - */ - /*package*/ static final int AUTHENTICATION_NO_MITM = 1; - - /** - * Authentication with person-in-the-middle protection requested. - * - * @hide - */ - /*package*/ static final int AUTHENTICATION_MITM = 2; - - /** - * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. - */ - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final IBluetoothGattCallback mBluetoothGattCallback = - new IBluetoothGattCallback.Stub() { - /** - * Application interface registered - app is ready to go - * @hide - */ - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - public void onClientRegistered(int status, int clientIf) { - if (DBG) { - Log.d(TAG, "onClientRegistered() - status=" + status - + " clientIf=" + clientIf); - } - if (VDBG) { - synchronized (mStateLock) { - if (mConnState != CONN_STATE_CONNECTING) { - Log.e(TAG, "Bad connection state: " + mConnState); - } - } - } - mClientIf = clientIf; - if (status != GATT_SUCCESS) { - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onConnectionStateChange(BluetoothGatt.this, - GATT_FAILURE, - BluetoothProfile.STATE_DISCONNECTED); - } - } - }); - - synchronized (mStateLock) { - mConnState = CONN_STATE_IDLE; - } - return; - } - try { - mService.clientConnect(mClientIf, mDevice.getAddress(), - !mAutoConnect, mTransport, mOpportunistic, - mPhy, mAttributionSource); // autoConnect is inverse of "isDirect" - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - /** - * Phy update callback - * @hide - */ - @Override - public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { - if (DBG) { - Log.d(TAG, "onPhyUpdate() - status=" + status - + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); - } - if (!address.equals(mDevice.getAddress())) { - return; - } - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status); - } - } - }); - } - - /** - * Phy read callback - * @hide - */ - @Override - public void onPhyRead(String address, int txPhy, int rxPhy, int status) { - if (DBG) { - Log.d(TAG, "onPhyRead() - status=" + status - + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy); - } - if (!address.equals(mDevice.getAddress())) { - return; - } - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status); - } - } - }); - } - - /** - * Client connection state changed - * @hide - */ - @Override - public void onClientConnectionState(int status, int clientIf, - boolean connected, String address) { - if (DBG) { - Log.d(TAG, "onClientConnectionState() - status=" + status - + " clientIf=" + clientIf + " device=" + address); - } - if (!address.equals(mDevice.getAddress())) { - return; - } - int profileState = connected ? BluetoothProfile.STATE_CONNECTED : - BluetoothProfile.STATE_DISCONNECTED; - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onConnectionStateChange(BluetoothGatt.this, status, - profileState); - } - } - }); - - synchronized (mStateLock) { - if (connected) { - mConnState = CONN_STATE_CONNECTED; - } else { - mConnState = CONN_STATE_IDLE; - } - } - - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - } - - /** - * Remote search has been completed. - * The internal object structure should now reflect the state - * of the remote device database. Let the application know that - * we are done at this point. - * @hide - */ - @Override - public void onSearchComplete(String address, List<BluetoothGattService> services, - int status) { - if (DBG) { - Log.d(TAG, - "onSearchComplete() = Device=" + address + " Status=" + status); - } - if (!address.equals(mDevice.getAddress())) { - return; - } - - for (BluetoothGattService s : services) { - //services we receive don't have device set properly. - s.setDevice(mDevice); - } - - mServices.addAll(services); - - // Fix references to included services, as they doesn't point to right objects. - for (BluetoothGattService fixedService : mServices) { - ArrayList<BluetoothGattService> includedServices = - new ArrayList(fixedService.getIncludedServices()); - fixedService.getIncludedServices().clear(); - - for (BluetoothGattService brokenRef : includedServices) { - BluetoothGattService includedService = getService(mDevice, - brokenRef.getUuid(), brokenRef.getInstanceId()); - if (includedService != null) { - fixedService.addIncludedService(includedService); - } else { - Log.e(TAG, "Broken GATT database: can't find included service."); - } - } - } - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onServicesDiscovered(BluetoothGatt.this, status); - } - } - }); - } - - /** - * Remote characteristic has been read. - * Updates the internal value. - * @hide - */ - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - public void onCharacteristicRead(String address, int status, int handle, - byte[] value) { - if (VDBG) { - Log.d(TAG, "onCharacteristicRead() - Device=" + address - + " handle=" + handle + " Status=" + status); - } - - if (!address.equals(mDevice.getAddress())) { - return; - } - - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - - if ((status == GATT_INSUFFICIENT_AUTHENTICATION - || status == GATT_INSUFFICIENT_ENCRYPTION) - && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { - try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) - ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.readCharacteristic( - mClientIf, address, handle, authReq, mAttributionSource); - mAuthRetryState++; - return; - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - mAuthRetryState = AUTH_RETRY_STATE_IDLE; - - BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, - handle); - if (characteristic == null) { - Log.w(TAG, "onCharacteristicRead() failed to find characteristic!"); - return; - } - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - if (status == 0) characteristic.setValue(value); - callback.onCharacteristicRead(BluetoothGatt.this, characteristic, - value, status); - // Keep calling deprecated callback to maintain app compatibility - callback.onCharacteristicRead(BluetoothGatt.this, characteristic, - status); - } - } - }); - } - - /** - * Characteristic has been written to the remote device. - * Let the app know how we did... - * @hide - */ - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - public void onCharacteristicWrite(String address, int status, int handle, - byte[] value) { - if (VDBG) { - Log.d(TAG, "onCharacteristicWrite() - Device=" + address - + " handle=" + handle + " Status=" + status); - } - - if (!address.equals(mDevice.getAddress())) { - return; - } - - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - - BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, - handle); - if (characteristic == null) return; - - if ((status == GATT_INSUFFICIENT_AUTHENTICATION - || status == GATT_INSUFFICIENT_ENCRYPTION) - && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { - try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) - ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN; - for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { - requestStatus = mService.writeCharacteristic(mClientIf, address, - handle, characteristic.getWriteType(), authReq, - value, mAttributionSource); - if (requestStatus - != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) { - break; - } - try { - Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT); - } catch (InterruptedException e) { - } - } - mAuthRetryState++; - return; - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - mAuthRetryState = AUTH_RETRY_STATE_IDLE; - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onCharacteristicWrite(BluetoothGatt.this, characteristic, - status); - } - } - }); - } - - /** - * Remote characteristic has been updated. - * Updates the internal value. - * @hide - */ - @Override - public void onNotify(String address, int handle, byte[] value) { - if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle); - - if (!address.equals(mDevice.getAddress())) { - return; - } - - BluetoothGattCharacteristic characteristic = getCharacteristicById(mDevice, - handle); - if (characteristic == null) return; - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - characteristic.setValue(value); - callback.onCharacteristicChanged(BluetoothGatt.this, - characteristic, value); - // Keep calling deprecated callback to maintain app compatibility - callback.onCharacteristicChanged(BluetoothGatt.this, - characteristic); - } - } - }); - } - - /** - * Descriptor has been read. - * @hide - */ - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - public void onDescriptorRead(String address, int status, int handle, byte[] value) { - if (VDBG) { - Log.d(TAG, - "onDescriptorRead() - Device=" + address + " handle=" + handle); - } - - if (!address.equals(mDevice.getAddress())) { - return; - } - - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - - BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); - if (descriptor == null) return; - - - if ((status == GATT_INSUFFICIENT_AUTHENTICATION - || status == GATT_INSUFFICIENT_ENCRYPTION) - && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { - try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) - ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.readDescriptor( - mClientIf, address, handle, authReq, mAttributionSource); - mAuthRetryState++; - return; - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - mAuthRetryState = AUTH_RETRY_STATE_IDLE; - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - if (status == 0) descriptor.setValue(value); - callback.onDescriptorRead(BluetoothGatt.this, descriptor, status, - value); - // Keep calling deprecated callback to maintain app compatibility - callback.onDescriptorRead(BluetoothGatt.this, descriptor, status); - } - } - }); - } - - /** - * Descriptor write operation complete. - * @hide - */ - @Override - @SuppressLint("AndroidFrameworkRequiresPermission") - public void onDescriptorWrite(String address, int status, int handle, - byte[] value) { - if (VDBG) { - Log.d(TAG, - "onDescriptorWrite() - Device=" + address + " handle=" + handle); - } - - if (!address.equals(mDevice.getAddress())) { - return; - } - - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - - BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); - if (descriptor == null) return; - - if ((status == GATT_INSUFFICIENT_AUTHENTICATION - || status == GATT_INSUFFICIENT_ENCRYPTION) - && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { - try { - final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) - ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - mService.writeDescriptor(mClientIf, address, handle, - authReq, value, mAttributionSource); - mAuthRetryState++; - return; - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - mAuthRetryState = AUTH_RETRY_STATE_IDLE; - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status); - } - } - }); - } - - /** - * Prepared write transaction completed (or aborted) - * @hide - */ - @Override - public void onExecuteWrite(String address, int status) { - if (VDBG) { - Log.d(TAG, "onExecuteWrite() - Device=" + address - + " status=" + status); - } - if (!address.equals(mDevice.getAddress())) { - return; - } - - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onReliableWriteCompleted(BluetoothGatt.this, status); - } - } - }); - } - - /** - * Remote device RSSI has been read - * @hide - */ - @Override - public void onReadRemoteRssi(String address, int rssi, int status) { - if (VDBG) { - Log.d(TAG, "onReadRemoteRssi() - Device=" + address - + " rssi=" + rssi + " status=" + status); - } - if (!address.equals(mDevice.getAddress())) { - return; - } - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); - } - } - }); - } - - /** - * Callback invoked when the MTU for a given connection changes - * @hide - */ - @Override - public void onConfigureMTU(String address, int mtu, int status) { - if (DBG) { - Log.d(TAG, "onConfigureMTU() - Device=" + address - + " mtu=" + mtu + " status=" + status); - } - if (!address.equals(mDevice.getAddress())) { - return; - } - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onMtuChanged(BluetoothGatt.this, mtu, status); - } - } - }); - } - - /** - * Callback invoked when the given connection is updated - * @hide - */ - @Override - public void onConnectionUpdated(String address, int interval, int latency, - int timeout, int status) { - if (DBG) { - Log.d(TAG, "onConnectionUpdated() - Device=" + address - + " interval=" + interval + " latency=" + latency - + " timeout=" + timeout + " status=" + status); - } - if (!address.equals(mDevice.getAddress())) { - return; - } - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onConnectionUpdated(BluetoothGatt.this, interval, latency, - timeout, status); - } - } - }); - } - - /** - * Callback invoked when service changed event is received - * @hide - */ - @Override - public void onServiceChanged(String address) { - if (DBG) { - Log.d(TAG, "onServiceChanged() - Device=" + address); - } - - if (!address.equals(mDevice.getAddress())) { - return; - } - - runOrQueueCallback(new Runnable() { - @Override - public void run() { - final BluetoothGattCallback callback = mCallback; - if (callback != null) { - callback.onServiceChanged(BluetoothGatt.this); - } - } - }); - } - }; - - /* package */ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, int transport, - boolean opportunistic, int phy, AttributionSource attributionSource) { - mService = iGatt; - mDevice = device; - mTransport = transport; - mPhy = phy; - mOpportunistic = opportunistic; - mAttributionSource = attributionSource; - mServices = new ArrayList<BluetoothGattService>(); - - mConnState = CONN_STATE_IDLE; - mAuthRetryState = AUTH_RETRY_STATE_IDLE; - } - - /** - * Close this Bluetooth GATT client. - * - * Application should call this method as early as possible after it is done with - * this GATT client. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void close() { - if (DBG) Log.d(TAG, "close()"); - - unregisterApp(); - mConnState = CONN_STATE_CLOSED; - mAuthRetryState = AUTH_RETRY_STATE_IDLE; - } - - /** - * Returns a service by UUID, instance and type. - * - * @hide - */ - /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, - int instanceId) { - for (BluetoothGattService svc : mServices) { - if (svc.getDevice().equals(device) - && svc.getInstanceId() == instanceId - && svc.getUuid().equals(uuid)) { - return svc; - } - } - return null; - } - - - /** - * Returns a characteristic with id equal to instanceId. - * - * @hide - */ - /*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, - int instanceId) { - for (BluetoothGattService svc : mServices) { - for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { - if (charac.getInstanceId() == instanceId) { - return charac; - } - } - } - return null; - } - - /** - * Returns a descriptor with id equal to instanceId. - * - * @hide - */ - /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) { - for (BluetoothGattService svc : mServices) { - for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { - for (BluetoothGattDescriptor desc : charac.getDescriptors()) { - if (desc.getInstanceId() == instanceId) { - return desc; - } - } - } - } - return null; - } - - /** - * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable - * immediately if no Handler was provided. - */ - private void runOrQueueCallback(final Runnable cb) { - if (mHandler == null) { - try { - cb.run(); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } - } else { - mHandler.post(cb); - } - } - - /** - * Register an application callback to start using GATT. - * - * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} - * is used to notify success or failure if the function returns true. - * - * @param callback GATT callback handler that will receive asynchronous callbacks. - * @return If true, the callback will be called to notify success or failure, false on immediate - * error - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private boolean registerApp(BluetoothGattCallback callback, Handler handler) { - return registerApp(callback, handler, false); - } - - /** - * Register an application callback to start using GATT. - * - * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} - * is used to notify success or failure if the function returns true. - * - * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param eatt_support indicate to allow for eatt support - * @return If true, the callback will be called to notify success or failure, false on immediate - * error - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private boolean registerApp(BluetoothGattCallback callback, Handler handler, - boolean eatt_support) { - if (DBG) Log.d(TAG, "registerApp()"); - if (mService == null) return false; - - mCallback = callback; - mHandler = handler; - UUID uuid = UUID.randomUUID(); - if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); - - try { - mService.registerClient( - new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * Unregister the current application and callbacks. - */ - @UnsupportedAppUsage - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private void unregisterApp() { - if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); - if (mService == null || mClientIf == 0) return; - - try { - mCallback = null; - mService.unregisterClient(mClientIf, mAttributionSource); - mClientIf = 0; - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - /** - * Initiate a connection to a Bluetooth GATT capable device. - * - * <p>The connection may not be established right away, but will be - * completed when the remote device is available. A - * {@link BluetoothGattCallback#onConnectionStateChange} callback will be - * invoked when the connection state changes as a result of this function. - * - * <p>The autoConnect parameter determines whether to actively connect to - * the remote device, or rather passively scan and finalize the connection - * when the remote device is in range/available. Generally, the first ever - * connection to a device should be direct (autoConnect set to false) and - * subsequent connections to known devices should be invoked with the - * autoConnect parameter set to true. - * - * @param device Remote device to connect to - * @param autoConnect Whether to directly connect to the remote device (false) or to - * automatically connect as soon as the remote device becomes available (true). - * @return true, if the connection attempt was initiated successfully - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback, - Handler handler) { - if (DBG) { - Log.d(TAG, - "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); - } - synchronized (mStateLock) { - if (mConnState != CONN_STATE_IDLE) { - throw new IllegalStateException("Not idle"); - } - mConnState = CONN_STATE_CONNECTING; - } - - mAutoConnect = autoConnect; - - if (!registerApp(callback, handler)) { - synchronized (mStateLock) { - mConnState = CONN_STATE_IDLE; - } - Log.e(TAG, "Failed to register callback"); - return false; - } - - // The connection will continue in the onClientRegistered callback - return true; - } - - /** - * Disconnects an established connection, or cancels a connection attempt - * currently in progress. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void disconnect() { - if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress()); - if (mService == null || mClientIf == 0) return; - - try { - mService.clientDisconnect(mClientIf, mDevice.getAddress(), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - /** - * Connect back to remote device. - * - * <p>This method is used to re-connect to a remote device after the - * connection has been dropped. If the device is not in range, the - * re-connection will be triggered once the device is back in range. - * - * @return true, if the connection attempt was initiated successfully - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean connect() { - try { - // autoConnect is inverse of "isDirect" - mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport, - mOpportunistic, mPhy, mAttributionSource); - return true; - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - } - - /** - * Set the preferred connection PHY for this app. Please note that this is just a - * recommendation, whether the PHY change will happen depends on other applications preferences, - * local and remote controller capabilities. Controller can override these settings. - * <p> - * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even - * if no PHY change happens. It is also triggered when remote device updates the PHY. - * - * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link - * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link - * BluetoothDevice#PHY_LE_CODED_MASK}. - * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link - * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link - * BluetoothDevice#PHY_LE_CODED_MASK}. - * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one - * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or - * {@link BluetoothDevice#PHY_OPTION_S8} - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) { - try { - mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy, - phyOptions, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - /** - * Read the current transmitter PHY and receiver PHY of the connection. The values are returned - * in {@link BluetoothGattCallback#onPhyRead} - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void readPhy() { - try { - mService.clientReadPhy(mClientIf, mDevice.getAddress(), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - /** - * Return the remote bluetooth device this GATT client targets to - * - * @return remote bluetooth device - */ - @RequiresNoPermission - public BluetoothDevice getDevice() { - return mDevice; - } - - /** - * Discovers services offered by a remote device as well as their - * characteristics and descriptors. - * - * <p>This is an asynchronous operation. Once service discovery is completed, - * the {@link BluetoothGattCallback#onServicesDiscovered} callback is - * triggered. If the discovery was successful, the remote services can be - * retrieved using the {@link #getServices} function. - * - * @return true, if the remote service discovery has been started - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean discoverServices() { - if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress()); - if (mService == null || mClientIf == 0) return false; - - mServices.clear(); - - try { - mService.discoverServices(mClientIf, mDevice.getAddress(), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * Discovers a service by UUID. This is exposed only for passing PTS tests. - * It should never be used by real applications. The service is not searched - * for characteristics and descriptors, or returned in any callback. - * - * @return true, if the remote service discovery has been started - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean discoverServiceByUuid(UUID uuid) { - if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress()); - if (mService == null || mClientIf == 0) return false; - - mServices.clear(); - - try { - mService.discoverServiceByUuid( - mClientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - return true; - } - - /** - * Returns a list of GATT services offered by the remote device. - * - * <p>This function requires that service discovery has been completed - * for the given device. - * - * @return List of services on the remote device. Returns an empty list if service discovery has - * not yet been performed. - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public List<BluetoothGattService> getServices() { - List<BluetoothGattService> result = - new ArrayList<BluetoothGattService>(); - - for (BluetoothGattService service : mServices) { - if (service.getDevice().equals(mDevice)) { - result.add(service); - } - } - - return result; - } - - /** - * Returns a {@link BluetoothGattService}, if the requested UUID is - * supported by the remote device. - * - * <p>This function requires that service discovery has been completed - * for the given device. - * - * <p>If multiple instances of the same service (as identified by UUID) - * exist, the first instance of the service is returned. - * - * @param uuid UUID of the requested service - * @return BluetoothGattService if supported, or null if the requested service is not offered by - * the remote device. - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public BluetoothGattService getService(UUID uuid) { - for (BluetoothGattService service : mServices) { - if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) { - return service; - } - } - - return null; - } - - /** - * Reads the requested characteristic from the associated remote device. - * - * <p>This is an asynchronous operation. The result of the read operation - * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, - * BluetoothGattCharacteristic, byte[], int)} callback. - * - * @param characteristic Characteristic to read from the remote device - * @return true, if the read operation was initiated successfully - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { - if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) { - return false; - } - - if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); - if (mService == null || mClientIf == 0) return false; - - BluetoothGattService service = characteristic.getService(); - if (service == null) return false; - - BluetoothDevice device = service.getDevice(); - if (device == null) return false; - - synchronized (mDeviceBusyLock) { - if (mDeviceBusy) return false; - mDeviceBusy = true; - } - - try { - mService.readCharacteristic(mClientIf, device.getAddress(), - characteristic.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - return false; - } - - return true; - } - - /** - * Reads the characteristic using its UUID from the associated remote device. - * - * <p>This is an asynchronous operation. The result of the read operation - * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, - * BluetoothGattCharacteristic, byte[], int)} callback. - * - * @param uuid UUID of characteristic to read from the remote device - * @return true, if the read operation was initiated successfully - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) { - if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid); - if (mService == null || mClientIf == 0) return false; - - synchronized (mDeviceBusyLock) { - if (mDeviceBusy) return false; - mDeviceBusy = true; - } - - try { - mService.readUsingCharacteristicUuid(mClientIf, mDevice.getAddress(), - new ParcelUuid(uuid), startHandle, endHandle, AUTHENTICATION_NONE, - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - return false; - } - - return true; - } - - - /** - * Writes a given characteristic and its values to the associated remote device. - * - * <p>Once the write operation has been completed, the - * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, - * reporting the result of the operation. - * - * @param characteristic Characteristic to write on the remote device - * @return true, if the write operation was initiated successfully - * @throws IllegalArgumentException if characteristic or its value are null - * - * @deprecated Use {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], - * int)} as this is not memory safe. - */ - @Deprecated - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { - try { - return writeCharacteristic(characteristic, characteristic.getValue(), - characteristic.getWriteType()) == BluetoothStatusCodes.SUCCESS; - } catch (Exception e) { - return false; - } - } - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - BluetoothStatusCodes.SUCCESS, - BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, - BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION, - BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED, - BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, - BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED, - BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY, - BluetoothStatusCodes.ERROR_UNKNOWN - }) - public @interface WriteOperationReturnValues{} - - /** - * Writes a given characteristic and its values to the associated remote device. - * - * <p>Once the write operation has been completed, the - * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, - * reporting the result of the operation. - * - * @param characteristic Characteristic to write on the remote device - * @return whether the characteristic was successfully written to - * @throws IllegalArgumentException if characteristic or value are null - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @WriteOperationReturnValues - public int writeCharacteristic(@NonNull BluetoothGattCharacteristic characteristic, - @NonNull byte[] value, int writeType) { - if (characteristic == null) { - throw new IllegalArgumentException("characteristic must not be null"); - } - if (value == null) { - throw new IllegalArgumentException("value must not be null"); - } - if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); - if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 - && (characteristic.getProperties() - & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { - return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED; - } - if (mService == null || mClientIf == 0) { - return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; - } - - BluetoothGattService service = characteristic.getService(); - if (service == null) { - throw new IllegalArgumentException("Characteristic must have a non-null service"); - } - - BluetoothDevice device = service.getDevice(); - if (device == null) { - throw new IllegalArgumentException("Service must have a non-null device"); - } - - synchronized (mDeviceBusyLock) { - if (mDeviceBusy) { - return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY; - } - mDeviceBusy = true; - } - - int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN; - try { - for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { - requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(), - characteristic.getInstanceId(), writeType, AUTHENTICATION_NONE, value, - mAttributionSource); - if (requestStatus != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) { - break; - } - try { - Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT); - } catch (InterruptedException e) { - } - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - throw e.rethrowFromSystemServer(); - } - - return requestStatus; - } - - /** - * Reads the value for a given descriptor from the associated remote device. - * - * <p>Once the read operation has been completed, the - * {@link BluetoothGattCallback#onDescriptorRead} callback is - * triggered, signaling the result of the operation. - * - * @param descriptor Descriptor value to read from the remote device - * @return true, if the read operation was initiated successfully - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean readDescriptor(BluetoothGattDescriptor descriptor) { - if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); - if (mService == null || mClientIf == 0) return false; - - BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); - if (characteristic == null) return false; - - BluetoothGattService service = characteristic.getService(); - if (service == null) return false; - - BluetoothDevice device = service.getDevice(); - if (device == null) return false; - - synchronized (mDeviceBusyLock) { - if (mDeviceBusy) return false; - mDeviceBusy = true; - } - - try { - mService.readDescriptor(mClientIf, device.getAddress(), - descriptor.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - return false; - } - - return true; - } - - /** - * Write the value of a given descriptor to the associated remote device. - * - * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the - * result of the write operation. - * - * @param descriptor Descriptor to write to the associated remote device - * @return true, if the write operation was initiated successfully - * @throws IllegalArgumentException if descriptor or its value are null - * - * @deprecated Use {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} as - * this is not memory safe. - */ - @Deprecated - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { - try { - return writeDescriptor(descriptor, descriptor.getValue()) - == BluetoothStatusCodes.SUCCESS; - } catch (Exception e) { - return false; - } - } - - /** - * Write the value of a given descriptor to the associated remote device. - * - * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the - * result of the write operation. - * - * @param descriptor Descriptor to write to the associated remote device - * @return true, if the write operation was initiated successfully - * @throws IllegalArgumentException if descriptor or value are null - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @WriteOperationReturnValues - public int writeDescriptor(@NonNull BluetoothGattDescriptor descriptor, - @NonNull byte[] value) { - if (descriptor == null) { - throw new IllegalArgumentException("descriptor must not be null"); - } - if (value == null) { - throw new IllegalArgumentException("value must not be null"); - } - if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); - if (mService == null || mClientIf == 0) { - return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; - } - - BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); - if (characteristic == null) { - throw new IllegalArgumentException("Descriptor must have a non-null characteristic"); - } - - BluetoothGattService service = characteristic.getService(); - if (service == null) { - throw new IllegalArgumentException("Characteristic must have a non-null service"); - } - - BluetoothDevice device = service.getDevice(); - if (device == null) { - throw new IllegalArgumentException("Service must have a non-null device"); - } - - synchronized (mDeviceBusyLock) { - if (mDeviceBusy) return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY; - mDeviceBusy = true; - } - - try { - return mService.writeDescriptor(mClientIf, device.getAddress(), - descriptor.getInstanceId(), AUTHENTICATION_NONE, value, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - e.rethrowFromSystemServer(); - } - return BluetoothStatusCodes.ERROR_UNKNOWN; - } - - /** - * Initiates a reliable write transaction for a given remote device. - * - * <p>Once a reliable write transaction has been initiated, all calls - * to {@link #writeCharacteristic} are sent to the remote device for - * verification and queued up for atomic execution. The application will - * receive a {@link BluetoothGattCallback#onCharacteristicWrite} callback in response to every - * {@link #writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} call and is - * responsible for verifying if the value has been transmitted accurately. - * - * <p>After all characteristics have been queued up and verified, - * {@link #executeReliableWrite} will execute all writes. If a characteristic - * was not written correctly, calling {@link #abortReliableWrite} will - * cancel the current transaction without committing any values on the - * remote device. - * - * @return true, if the reliable write transaction has been initiated - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean beginReliableWrite() { - if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress()); - if (mService == null || mClientIf == 0) return false; - - try { - mService.beginReliableWrite(mClientIf, mDevice.getAddress(), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * Executes a reliable write transaction for a given remote device. - * - * <p>This function will commit all queued up characteristic write - * operations for a given remote device. - * - * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is - * invoked to indicate whether the transaction has been executed correctly. - * - * @return true, if the request to execute the transaction has been sent - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean executeReliableWrite() { - if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); - if (mService == null || mClientIf == 0) return false; - - synchronized (mDeviceBusyLock) { - if (mDeviceBusy) return false; - mDeviceBusy = true; - } - - try { - mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - synchronized (mDeviceBusyLock) { - mDeviceBusy = false; - } - return false; - } - - return true; - } - - /** - * Cancels a reliable write transaction for a given device. - * - * <p>Calling this function will discard all queued characteristic write - * operations for a given remote device. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void abortReliableWrite() { - if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress()); - if (mService == null || mClientIf == 0) return; - - try { - mService.endReliableWrite(mClientIf, mDevice.getAddress(), false, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - /** - * @deprecated Use {@link #abortReliableWrite()} - */ - @Deprecated - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void abortReliableWrite(BluetoothDevice mDevice) { - abortReliableWrite(); - } - - /** - * Enable or disable notifications/indications for a given characteristic. - * - * <p>Once notifications are enabled for a characteristic, a - * {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt, - * BluetoothGattCharacteristic, byte[])} callback will be triggered if the remote device - * indicates that the given characteristic has changed. - * - * @param characteristic The characteristic for which to enable notifications - * @param enable Set to true to enable notifications/indications - * @return true, if the requested notification status was set successfully - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, - boolean enable) { - if (DBG) { - Log.d(TAG, "setCharacteristicNotification() - uuid: " + characteristic.getUuid() - + " enable: " + enable); - } - if (mService == null || mClientIf == 0) return false; - - BluetoothGattService service = characteristic.getService(); - if (service == null) return false; - - BluetoothDevice device = service.getDevice(); - if (device == null) return false; - - try { - mService.registerForNotification(mClientIf, device.getAddress(), - characteristic.getInstanceId(), enable, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * Clears the internal cache and forces a refresh of the services from the - * remote device. - * - * @hide - */ - @UnsupportedAppUsage - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean refresh() { - if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress()); - if (mService == null || mClientIf == 0) return false; - - try { - mService.refreshDevice(mClientIf, mDevice.getAddress(), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * Read the RSSI for a connected remote device. - * - * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be - * invoked when the RSSI value has been read. - * - * @return true, if the RSSI value has been requested successfully - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean readRemoteRssi() { - if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress()); - if (mService == null || mClientIf == 0) return false; - - try { - mService.readRemoteRssi(mClientIf, mDevice.getAddress(), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * Request an MTU size used for a given connection. - * - * <p>When performing a write request operation (write without response), - * the data sent is truncated to the MTU size. This function may be used - * to request a larger MTU size to be able to send more data at once. - * - * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate - * whether this operation was successful. - * - * @return true, if the new MTU value has been requested successfully - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean requestMtu(int mtu) { - if (DBG) { - Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress() - + " mtu: " + mtu); - } - if (mService == null || mClientIf == 0) return false; - - try { - mService.configureMTU(mClientIf, mDevice.getAddress(), mtu, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * Request a connection parameter update. - * - * <p>This function will send a connection parameter update request to the - * remote device. - * - * @param connectionPriority Request a specific connection priority. Must be one of {@link - * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH} - * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}. - * @throws IllegalArgumentException If the parameters are outside of their specified range. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean requestConnectionPriority(int connectionPriority) { - if (connectionPriority < CONNECTION_PRIORITY_BALANCED - || connectionPriority > CONNECTION_PRIORITY_LOW_POWER) { - throw new IllegalArgumentException("connectionPriority not within valid range"); - } - - if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority); - if (mService == null || mClientIf == 0) return false; - - try { - mService.connectionParameterUpdate( - mClientIf, mDevice.getAddress(), connectionPriority, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * Request an LE connection parameter update. - * - * <p>This function will send an LE connection parameters update request to the remote device. - * - * @return true, if the request is send to the Bluetooth stack. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval, - int slaveLatency, int supervisionTimeout, - int minConnectionEventLen, int maxConnectionEventLen) { - if (DBG) { - Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval - + ")" + (1.25 * minConnectionInterval) - + "msec, max=(" + maxConnectionInterval + ")" - + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency - + ", timeout=" + supervisionTimeout + "msec" + ", min_ce=" - + minConnectionEventLen + ", max_ce=" + maxConnectionEventLen); - } - if (mService == null || mClientIf == 0) return false; - - try { - mService.leConnectionUpdate(mClientIf, mDevice.getAddress(), - minConnectionInterval, maxConnectionInterval, - slaveLatency, supervisionTimeout, - minConnectionEventLen, maxConnectionEventLen, - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} - * with {@link BluetoothProfile#GATT} as argument - * @throws UnsupportedOperationException - */ - @Override - @RequiresNoPermission - @Deprecated - public int getConnectionState(BluetoothDevice device) { - throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); - } - - /** - * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} - * with {@link BluetoothProfile#GATT} as argument - * - * @throws UnsupportedOperationException - */ - @Override - @RequiresNoPermission - @Deprecated - public List<BluetoothDevice> getConnectedDevices() { - throw new UnsupportedOperationException( - "Use BluetoothManager#getConnectedDevices instead."); - } - - /** - * @deprecated Not supported - please use - * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} - * with {@link BluetoothProfile#GATT} as first argument - * - * @throws UnsupportedOperationException - */ - @Override - @RequiresNoPermission - @Deprecated - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - throw new UnsupportedOperationException( - "Use BluetoothManager#getDevicesMatchingConnectionStates instead."); - } -} diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java deleted file mode 100644 index d0a5a1e729fe..000000000000 --- a/core/java/android/bluetooth/BluetoothGattCallback.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.NonNull; - -/** - * This abstract class is used to implement {@link BluetoothGatt} callbacks. - */ -public abstract class BluetoothGattCallback { - - /** - * Callback triggered as result of {@link BluetoothGatt#setPreferredPhy}, or as a result of - * remote device changing the PHY. - * - * @param gatt GATT client - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link - * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link - * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param status Status of the PHY update operation. {@link BluetoothGatt#GATT_SUCCESS} if the - * operation succeeds. - */ - public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { - } - - /** - * Callback triggered as result of {@link BluetoothGatt#readPhy} - * - * @param gatt GATT client - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link - * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link - * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}. - * @param status Status of the PHY read operation. {@link BluetoothGatt#GATT_SUCCESS} if the - * operation succeeds. - */ - public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { - } - - /** - * Callback indicating when GATT client has connected/disconnected to/from a remote - * GATT server. - * - * @param gatt GATT client - * @param status Status of the connect or disconnect operation. {@link - * BluetoothGatt#GATT_SUCCESS} if the operation succeeds. - * @param newState Returns the new connection state. Can be one of {@link - * BluetoothProfile#STATE_DISCONNECTED} or {@link BluetoothProfile#STATE_CONNECTED} - */ - public void onConnectionStateChange(BluetoothGatt gatt, int status, - int newState) { - } - - /** - * Callback invoked when the list of remote services, characteristics and descriptors - * for the remote device have been updated, ie new services have been discovered. - * - * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices} - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device has been explored - * successfully. - */ - public void onServicesDiscovered(BluetoothGatt gatt, int status) { - } - - /** - * Callback reporting the result of a characteristic read operation. - * - * @param gatt GATT client invoked - * {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} - * @param characteristic Characteristic that was read from the associated remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed - * successfully. - * @deprecated Use {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, - * BluetoothGattCharacteristic, byte[], int)} as it is memory safe - */ - @Deprecated - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, - int status) { - } - - /** - * Callback reporting the result of a characteristic read operation. - * - * @param gatt GATT client invoked - * {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} - * @param characteristic Characteristic that was read from the associated remote device. - * @param value the value of the characteristic - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed - * successfully. - */ - public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull - BluetoothGattCharacteristic characteristic, @NonNull byte[] value, int status) { - } - - /** - * Callback indicating the result of a characteristic write operation. - * - * <p>If this callback is invoked while a reliable write transaction is - * in progress, the value of the characteristic represents the value - * reported by the remote device. An application should compare this - * value to the desired value to be written. If the values don't match, - * the application must abort the reliable write transaction. - * - * @param gatt GATT client that invoked - * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, - * byte[], int)} - * @param characteristic Characteristic that was written to the associated remote device. - * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if - * the - * operation succeeds. - */ - public void onCharacteristicWrite(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { - } - - /** - * Callback triggered as a result of a remote characteristic notification. - * - * @param gatt GATT client the characteristic is associated with - * @param characteristic Characteristic that has been updated as a result of a remote - * notification event. - * @deprecated Use {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt, - * BluetoothGattCharacteristic, byte[])} as it is memory safe by providing the characteristic - * value at the time of notification. - */ - @Deprecated - public void onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { - } - - /** - * Callback triggered as a result of a remote characteristic notification. Note that the value - * within the characteristic object may have changed since receiving the remote characteristic - * notification, so check the parameter value for the value at the time of notification. - * - * @param gatt GATT client the characteristic is associated with - * @param characteristic Characteristic that has been updated as a result of a remote - * notification event. - * @param value notified characteristic value - */ - public void onCharacteristicChanged(@NonNull BluetoothGatt gatt, - @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) { - } - - /** - * Callback reporting the result of a descriptor read operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} - * @param descriptor Descriptor that was read from the associated remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed - * successfully - * @deprecated Use {@link BluetoothGattCallback#onDescriptorRead(BluetoothGatt, - * BluetoothGattDescriptor, int, byte[])} as it is memory safe by providing the descriptor - * value at the time it was read. - */ - @Deprecated - public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, - int status) { - } - - /** - * Callback reporting the result of a descriptor read operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} - * @param descriptor Descriptor that was read from the associated remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed - * successfully - * @param value the descriptor value at the time of the read operation - */ - public void onDescriptorRead(@NonNull BluetoothGatt gatt, - @NonNull BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) { - } - - /** - * Callback indicating the result of a descriptor write operation. - * - * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} - * @param descriptor Descriptor that was writte to the associated remote device. - * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the - * operation succeeds. - */ - public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, - int status) { - } - - /** - * Callback invoked when a reliable write transaction has been completed. - * - * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite} - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write transaction was - * executed successfully - */ - public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { - } - - /** - * Callback reporting the RSSI for a remote device connection. - * - * This callback is triggered in response to the - * {@link BluetoothGatt#readRemoteRssi} function. - * - * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi} - * @param rssi The RSSI value for the remote device - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully - */ - public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { - } - - /** - * Callback indicating the MTU for a given device connection has changed. - * - * This callback is triggered in response to the - * {@link BluetoothGatt#requestMtu} function, or in response to a connection - * event. - * - * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu} - * @param mtu The new MTU size - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully - */ - public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { - } - - /** - * Callback indicating the connection parameters were updated. - * - * @param gatt GATT client involved - * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from - * 6 (7.5ms) to 3200 (4000ms). - * @param latency Worker latency for the connection in number of connection events. Valid range - * is from 0 to 499 - * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10 - * (0.1s) to 3200 (32s) - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the connection has been updated - * successfully - * @hide - */ - public void onConnectionUpdated(BluetoothGatt gatt, int interval, int latency, int timeout, - int status) { - } - - /** - * Callback indicating service changed event is received - * - * <p>Receiving this event means that the GATT database is out of sync with - * the remote device. {@link BluetoothGatt#discoverServices} should be - * called to re-discover the services. - * - * @param gatt GATT client involved - */ - public void onServiceChanged(@NonNull BluetoothGatt gatt) { - } -} diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java deleted file mode 100644 index c5e986e895b2..000000000000 --- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java +++ /dev/null @@ -1,806 +0,0 @@ -/* - * Copyright (C) 2013 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.bluetooth; - -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -/** - * Represents a Bluetooth GATT Characteristic - * - * <p>A GATT characteristic is a basic data element used to construct a GATT service, - * {@link BluetoothGattService}. The characteristic contains a value as well as - * additional information and optional GATT descriptors, {@link BluetoothGattDescriptor}. - */ -public class BluetoothGattCharacteristic implements Parcelable { - - /** - * Characteristic proprty: Characteristic is broadcastable. - */ - public static final int PROPERTY_BROADCAST = 0x01; - - /** - * Characteristic property: Characteristic is readable. - */ - public static final int PROPERTY_READ = 0x02; - - /** - * Characteristic property: Characteristic can be written without response. - */ - public static final int PROPERTY_WRITE_NO_RESPONSE = 0x04; - - /** - * Characteristic property: Characteristic can be written. - */ - public static final int PROPERTY_WRITE = 0x08; - - /** - * Characteristic property: Characteristic supports notification - */ - public static final int PROPERTY_NOTIFY = 0x10; - - /** - * Characteristic property: Characteristic supports indication - */ - public static final int PROPERTY_INDICATE = 0x20; - - /** - * Characteristic property: Characteristic supports write with signature - */ - public static final int PROPERTY_SIGNED_WRITE = 0x40; - - /** - * Characteristic property: Characteristic has extended properties - */ - public static final int PROPERTY_EXTENDED_PROPS = 0x80; - - /** - * Characteristic read permission - */ - public static final int PERMISSION_READ = 0x01; - - /** - * Characteristic permission: Allow encrypted read operations - */ - public static final int PERMISSION_READ_ENCRYPTED = 0x02; - - /** - * Characteristic permission: Allow reading with person-in-the-middle protection - */ - public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04; - - /** - * Characteristic write permission - */ - public static final int PERMISSION_WRITE = 0x10; - - /** - * Characteristic permission: Allow encrypted writes - */ - public static final int PERMISSION_WRITE_ENCRYPTED = 0x20; - - /** - * Characteristic permission: Allow encrypted writes with person-in-the-middle - * protection - */ - public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40; - - /** - * Characteristic permission: Allow signed write operations - */ - public static final int PERMISSION_WRITE_SIGNED = 0x80; - - /** - * Characteristic permission: Allow signed write operations with - * person-in-the-middle protection - */ - public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100; - - /** - * Write characteristic, requesting acknoledgement by the remote device - */ - public static final int WRITE_TYPE_DEFAULT = 0x02; - - /** - * Write characteristic without requiring a response by the remote device - */ - public static final int WRITE_TYPE_NO_RESPONSE = 0x01; - - /** - * Write characteristic including authentication signature - */ - public static final int WRITE_TYPE_SIGNED = 0x04; - - /** - * Characteristic value format type uint8 - */ - public static final int FORMAT_UINT8 = 0x11; - - /** - * Characteristic value format type uint16 - */ - public static final int FORMAT_UINT16 = 0x12; - - /** - * Characteristic value format type uint32 - */ - public static final int FORMAT_UINT32 = 0x14; - - /** - * Characteristic value format type sint8 - */ - public static final int FORMAT_SINT8 = 0x21; - - /** - * Characteristic value format type sint16 - */ - public static final int FORMAT_SINT16 = 0x22; - - /** - * Characteristic value format type sint32 - */ - public static final int FORMAT_SINT32 = 0x24; - - /** - * Characteristic value format type sfloat (16-bit float) - */ - public static final int FORMAT_SFLOAT = 0x32; - - /** - * Characteristic value format type float (32-bit float) - */ - public static final int FORMAT_FLOAT = 0x34; - - - /** - * The UUID of this characteristic. - * - * @hide - */ - protected UUID mUuid; - - /** - * Instance ID for this characteristic. - * - * @hide - */ - @UnsupportedAppUsage - protected int mInstance; - - /** - * Characteristic properties. - * - * @hide - */ - protected int mProperties; - - /** - * Characteristic permissions. - * - * @hide - */ - protected int mPermissions; - - /** - * Key size (default = 16). - * - * @hide - */ - protected int mKeySize = 16; - - /** - * Write type for this characteristic. - * See WRITE_TYPE_* constants. - * - * @hide - */ - protected int mWriteType; - - /** - * Back-reference to the service this characteristic belongs to. - * - * @hide - */ - @UnsupportedAppUsage - protected BluetoothGattService mService; - - /** - * The cached value of this characteristic. - * - * @hide - */ - protected byte[] mValue; - - /** - * List of descriptors included in this characteristic. - */ - protected List<BluetoothGattDescriptor> mDescriptors; - - /** - * Create a new BluetoothGattCharacteristic. - * - * @param uuid The UUID for this characteristic - * @param properties Properties of this characteristic - * @param permissions Permissions for this characteristic - */ - public BluetoothGattCharacteristic(UUID uuid, int properties, int permissions) { - initCharacteristic(null, uuid, 0, properties, permissions); - } - - /** - * Create a new BluetoothGattCharacteristic - * - * @hide - */ - /*package*/ BluetoothGattCharacteristic(BluetoothGattService service, - UUID uuid, int instanceId, - int properties, int permissions) { - initCharacteristic(service, uuid, instanceId, properties, permissions); - } - - /** - * Create a new BluetoothGattCharacteristic - * - * @hide - */ - public BluetoothGattCharacteristic(UUID uuid, int instanceId, - int properties, int permissions) { - initCharacteristic(null, uuid, instanceId, properties, permissions); - } - - private void initCharacteristic(BluetoothGattService service, - UUID uuid, int instanceId, - int properties, int permissions) { - mUuid = uuid; - mInstance = instanceId; - mProperties = properties; - mPermissions = permissions; - mService = service; - mValue = null; - mDescriptors = new ArrayList<BluetoothGattDescriptor>(); - - if ((mProperties & PROPERTY_WRITE_NO_RESPONSE) != 0) { - mWriteType = WRITE_TYPE_NO_RESPONSE; - } else { - mWriteType = WRITE_TYPE_DEFAULT; - } - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(new ParcelUuid(mUuid), 0); - out.writeInt(mInstance); - out.writeInt(mProperties); - out.writeInt(mPermissions); - out.writeInt(mKeySize); - out.writeInt(mWriteType); - out.writeTypedList(mDescriptors); - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattCharacteristic> CREATOR = - new Parcelable.Creator<BluetoothGattCharacteristic>() { - public BluetoothGattCharacteristic createFromParcel(Parcel in) { - return new BluetoothGattCharacteristic(in); - } - - public BluetoothGattCharacteristic[] newArray(int size) { - return new BluetoothGattCharacteristic[size]; - } - }; - - private BluetoothGattCharacteristic(Parcel in) { - mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); - mInstance = in.readInt(); - mProperties = in.readInt(); - mPermissions = in.readInt(); - mKeySize = in.readInt(); - mWriteType = in.readInt(); - - mDescriptors = new ArrayList<BluetoothGattDescriptor>(); - - ArrayList<BluetoothGattDescriptor> descs = - in.createTypedArrayList(BluetoothGattDescriptor.CREATOR); - if (descs != null) { - for (BluetoothGattDescriptor desc : descs) { - desc.setCharacteristic(this); - mDescriptors.add(desc); - } - } - } - - /** - * Returns the desired key size. - * - * @hide - */ - public int getKeySize() { - return mKeySize; - } - - /** - * Adds a descriptor to this characteristic. - * - * @param descriptor Descriptor to be added to this characteristic. - * @return true, if the descriptor was added to the characteristic - */ - public boolean addDescriptor(BluetoothGattDescriptor descriptor) { - mDescriptors.add(descriptor); - descriptor.setCharacteristic(this); - return true; - } - - /** - * Get a descriptor by UUID and isntance id. - * - * @hide - */ - /*package*/ BluetoothGattDescriptor getDescriptor(UUID uuid, int instanceId) { - for (BluetoothGattDescriptor descriptor : mDescriptors) { - if (descriptor.getUuid().equals(uuid) - && descriptor.getInstanceId() == instanceId) { - return descriptor; - } - } - return null; - } - - /** - * Returns the service this characteristic belongs to. - * - * @return The asscociated service - */ - public BluetoothGattService getService() { - return mService; - } - - /** - * Sets the service associated with this device. - * - * @hide - */ - @UnsupportedAppUsage - /*package*/ void setService(BluetoothGattService service) { - mService = service; - } - - /** - * Returns the UUID of this characteristic - * - * @return UUID of this characteristic - */ - public UUID getUuid() { - return mUuid; - } - - /** - * Returns the instance ID for this characteristic. - * - * <p>If a remote device offers multiple characteristics with the same UUID, - * the instance ID is used to distuinguish between characteristics. - * - * @return Instance ID of this characteristic - */ - public int getInstanceId() { - return mInstance; - } - - /** - * Force the instance ID. - * - * @hide - */ - public void setInstanceId(int instanceId) { - mInstance = instanceId; - } - - /** - * Returns the properties of this characteristic. - * - * <p>The properties contain a bit mask of property flags indicating - * the features of this characteristic. - * - * @return Properties of this characteristic - */ - public int getProperties() { - return mProperties; - } - - /** - * Returns the permissions for this characteristic. - * - * @return Permissions of this characteristic - */ - public int getPermissions() { - return mPermissions; - } - - /** - * Gets the write type for this characteristic. - * - * @return Write type for this characteristic - */ - public int getWriteType() { - return mWriteType; - } - - /** - * Set the write type for this characteristic - * - * <p>Setting the write type of a characteristic determines how the - * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} function - * write this characteristic. - * - * @param writeType The write type to for this characteristic. Can be one of: {@link - * #WRITE_TYPE_DEFAULT}, {@link #WRITE_TYPE_NO_RESPONSE} or {@link #WRITE_TYPE_SIGNED}. - */ - public void setWriteType(int writeType) { - mWriteType = writeType; - } - - /** - * Set the desired key size. - * - * @hide - */ - @UnsupportedAppUsage - public void setKeySize(int keySize) { - mKeySize = keySize; - } - - /** - * Returns a list of descriptors for this characteristic. - * - * @return Descriptors for this characteristic - */ - public List<BluetoothGattDescriptor> getDescriptors() { - return mDescriptors; - } - - /** - * Returns a descriptor with a given UUID out of the list of - * descriptors for this characteristic. - * - * @return GATT descriptor object or null if no descriptor with the given UUID was found. - */ - public BluetoothGattDescriptor getDescriptor(UUID uuid) { - for (BluetoothGattDescriptor descriptor : mDescriptors) { - if (descriptor.getUuid().equals(uuid)) { - return descriptor; - } - } - return null; - } - - /** - * Get the stored value for this characteristic. - * - * <p>This function returns the stored value for this characteristic as - * retrieved by calling {@link BluetoothGatt#readCharacteristic}. The cached - * value of the characteristic is updated as a result of a read characteristic - * operation or if a characteristic update notification has been received. - * - * @return Cached value of the characteristic - * - * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} instead - */ - @Deprecated - public byte[] getValue() { - return mValue; - } - - /** - * Return the stored value of this characteristic. - * - * <p>The formatType parameter determines how the characteristic value - * is to be interpreted. For example, settting formatType to - * {@link #FORMAT_UINT16} specifies that the first two bytes of the - * characteristic value at the given offset are interpreted to generate the - * return value. - * - * @param formatType The format type used to interpret the characteristic value. - * @param offset Offset at which the integer value can be found. - * @return Cached value of the characteristic or null of offset exceeds value size. - * - * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get - * the characteristic value - */ - @Deprecated - public Integer getIntValue(int formatType, int offset) { - if ((offset + getTypeLen(formatType)) > mValue.length) return null; - - switch (formatType) { - case FORMAT_UINT8: - return unsignedByteToInt(mValue[offset]); - - case FORMAT_UINT16: - return unsignedBytesToInt(mValue[offset], mValue[offset + 1]); - - case FORMAT_UINT32: - return unsignedBytesToInt(mValue[offset], mValue[offset + 1], - mValue[offset + 2], mValue[offset + 3]); - case FORMAT_SINT8: - return unsignedToSigned(unsignedByteToInt(mValue[offset]), 8); - - case FORMAT_SINT16: - return unsignedToSigned(unsignedBytesToInt(mValue[offset], - mValue[offset + 1]), 16); - - case FORMAT_SINT32: - return unsignedToSigned(unsignedBytesToInt(mValue[offset], - mValue[offset + 1], mValue[offset + 2], mValue[offset + 3]), 32); - } - - return null; - } - - /** - * Return the stored value of this characteristic. - * <p>See {@link #getValue} for details. - * - * @param formatType The format type used to interpret the characteristic value. - * @param offset Offset at which the float value can be found. - * @return Cached value of the characteristic at a given offset or null if the requested offset - * exceeds the value size. - * - * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get - * the characteristic value - */ - @Deprecated - public Float getFloatValue(int formatType, int offset) { - if ((offset + getTypeLen(formatType)) > mValue.length) return null; - - switch (formatType) { - case FORMAT_SFLOAT: - return bytesToFloat(mValue[offset], mValue[offset + 1]); - - case FORMAT_FLOAT: - return bytesToFloat(mValue[offset], mValue[offset + 1], - mValue[offset + 2], mValue[offset + 3]); - } - - return null; - } - - /** - * Return the stored value of this characteristic. - * <p>See {@link #getValue} for details. - * - * @param offset Offset at which the string value can be found. - * @return Cached value of the characteristic - * - * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get - * the characteristic value - */ - @Deprecated - public String getStringValue(int offset) { - if (mValue == null || offset > mValue.length) return null; - byte[] strBytes = new byte[mValue.length - offset]; - for (int i = 0; i != (mValue.length - offset); ++i) strBytes[i] = mValue[offset + i]; - return new String(strBytes); - } - - /** - * Updates the locally stored value of this characteristic. - * - * <p>This function modifies the locally stored cached value of this - * characteristic. To send the value to the remote device, call - * {@link BluetoothGatt#writeCharacteristic} to send the value to the - * remote device. - * - * @param value New value for this characteristic - * @return true if the locally stored value has been set, false if the requested value could not - * be stored locally. - * - * @deprecated Pass the characteristic value directly into - * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} - */ - @Deprecated - public boolean setValue(byte[] value) { - mValue = value; - return true; - } - - /** - * Set the locally stored value of this characteristic. - * <p>See {@link #setValue(byte[])} for details. - * - * @param value New value for this characteristic - * @param formatType Integer format type used to transform the value parameter - * @param offset Offset at which the value should be placed - * @return true if the locally stored value has been set - * - * @deprecated Pass the characteristic value directly into - * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} - */ - @Deprecated - public boolean setValue(int value, int formatType, int offset) { - int len = offset + getTypeLen(formatType); - if (mValue == null) mValue = new byte[len]; - if (len > mValue.length) return false; - - switch (formatType) { - case FORMAT_SINT8: - value = intToSignedBits(value, 8); - // Fall-through intended - case FORMAT_UINT8: - mValue[offset] = (byte) (value & 0xFF); - break; - - case FORMAT_SINT16: - value = intToSignedBits(value, 16); - // Fall-through intended - case FORMAT_UINT16: - mValue[offset++] = (byte) (value & 0xFF); - mValue[offset] = (byte) ((value >> 8) & 0xFF); - break; - - case FORMAT_SINT32: - value = intToSignedBits(value, 32); - // Fall-through intended - case FORMAT_UINT32: - mValue[offset++] = (byte) (value & 0xFF); - mValue[offset++] = (byte) ((value >> 8) & 0xFF); - mValue[offset++] = (byte) ((value >> 16) & 0xFF); - mValue[offset] = (byte) ((value >> 24) & 0xFF); - break; - - default: - return false; - } - return true; - } - - /** - * Set the locally stored value of this characteristic. - * <p>See {@link #setValue(byte[])} for details. - * - * @param mantissa Mantissa for this characteristic - * @param exponent exponent value for this characteristic - * @param formatType Float format type used to transform the value parameter - * @param offset Offset at which the value should be placed - * @return true if the locally stored value has been set - * - * @deprecated Pass the characteristic value directly into - * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} - */ - @Deprecated - public boolean setValue(int mantissa, int exponent, int formatType, int offset) { - int len = offset + getTypeLen(formatType); - if (mValue == null) mValue = new byte[len]; - if (len > mValue.length) return false; - - switch (formatType) { - case FORMAT_SFLOAT: - mantissa = intToSignedBits(mantissa, 12); - exponent = intToSignedBits(exponent, 4); - mValue[offset++] = (byte) (mantissa & 0xFF); - mValue[offset] = (byte) ((mantissa >> 8) & 0x0F); - mValue[offset] += (byte) ((exponent & 0x0F) << 4); - break; - - case FORMAT_FLOAT: - mantissa = intToSignedBits(mantissa, 24); - exponent = intToSignedBits(exponent, 8); - mValue[offset++] = (byte) (mantissa & 0xFF); - mValue[offset++] = (byte) ((mantissa >> 8) & 0xFF); - mValue[offset++] = (byte) ((mantissa >> 16) & 0xFF); - mValue[offset] += (byte) (exponent & 0xFF); - break; - - default: - return false; - } - - return true; - } - - /** - * Set the locally stored value of this characteristic. - * <p>See {@link #setValue(byte[])} for details. - * - * @param value New value for this characteristic - * @return true if the locally stored value has been set - * - * @deprecated Pass the characteristic value directly into - * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} - */ - @Deprecated - public boolean setValue(String value) { - mValue = value.getBytes(); - return true; - } - - /** - * Returns the size of a give value type. - */ - private int getTypeLen(int formatType) { - return formatType & 0xF; - } - - /** - * Convert a signed byte to an unsigned int. - */ - private int unsignedByteToInt(byte b) { - return b & 0xFF; - } - - /** - * Convert signed bytes to a 16-bit unsigned int. - */ - private int unsignedBytesToInt(byte b0, byte b1) { - return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8)); - } - - /** - * Convert signed bytes to a 32-bit unsigned int. - */ - private int unsignedBytesToInt(byte b0, byte b1, byte b2, byte b3) { - return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8)) - + (unsignedByteToInt(b2) << 16) + (unsignedByteToInt(b3) << 24); - } - - /** - * Convert signed bytes to a 16-bit short float value. - */ - private float bytesToFloat(byte b0, byte b1) { - int mantissa = unsignedToSigned(unsignedByteToInt(b0) - + ((unsignedByteToInt(b1) & 0x0F) << 8), 12); - int exponent = unsignedToSigned(unsignedByteToInt(b1) >> 4, 4); - return (float) (mantissa * Math.pow(10, exponent)); - } - - /** - * Convert signed bytes to a 32-bit short float value. - */ - private float bytesToFloat(byte b0, byte b1, byte b2, byte b3) { - int mantissa = unsignedToSigned(unsignedByteToInt(b0) - + (unsignedByteToInt(b1) << 8) - + (unsignedByteToInt(b2) << 16), 24); - return (float) (mantissa * Math.pow(10, b3)); - } - - /** - * Convert an unsigned integer value to a two's-complement encoded - * signed value. - */ - private int unsignedToSigned(int unsigned, int size) { - if ((unsigned & (1 << size - 1)) != 0) { - unsigned = -1 * ((1 << size - 1) - (unsigned & ((1 << size - 1) - 1))); - } - return unsigned; - } - - /** - * Convert an integer into the signed bits of a given length. - */ - private int intToSignedBits(int i, int size) { - if (i < 0) { - i = (1 << size - 1) + (i & ((1 << size - 1) - 1)); - } - return i; - } -} diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java deleted file mode 100644 index a35d5b99fd7b..000000000000 --- a/core/java/android/bluetooth/BluetoothGattDescriptor.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (C) 2013 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.bluetooth; - -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; - -import java.util.UUID; - -/** - * Represents a Bluetooth GATT Descriptor - * - * <p> GATT Descriptors contain additional information and attributes of a GATT - * characteristic, {@link BluetoothGattCharacteristic}. They can be used to describe - * the characteristic's features or to control certain behaviours of the characteristic. - */ -public class BluetoothGattDescriptor implements Parcelable { - - /** - * Value used to enable notification for a client configuration descriptor - */ - public static final byte[] ENABLE_NOTIFICATION_VALUE = {0x01, 0x00}; - - /** - * Value used to enable indication for a client configuration descriptor - */ - public static final byte[] ENABLE_INDICATION_VALUE = {0x02, 0x00}; - - /** - * Value used to disable notifications or indicatinos - */ - public static final byte[] DISABLE_NOTIFICATION_VALUE = {0x00, 0x00}; - - /** - * Descriptor read permission - */ - public static final int PERMISSION_READ = 0x01; - - /** - * Descriptor permission: Allow encrypted read operations - */ - public static final int PERMISSION_READ_ENCRYPTED = 0x02; - - /** - * Descriptor permission: Allow reading with person-in-the-middle protection - */ - public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04; - - /** - * Descriptor write permission - */ - public static final int PERMISSION_WRITE = 0x10; - - /** - * Descriptor permission: Allow encrypted writes - */ - public static final int PERMISSION_WRITE_ENCRYPTED = 0x20; - - /** - * Descriptor permission: Allow encrypted writes with person-in-the-middle - * protection - */ - public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40; - - /** - * Descriptor permission: Allow signed write operations - */ - public static final int PERMISSION_WRITE_SIGNED = 0x80; - - /** - * Descriptor permission: Allow signed write operations with - * person-in-the-middle protection - */ - public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100; - - /** - * The UUID of this descriptor. - * - * @hide - */ - protected UUID mUuid; - - /** - * Instance ID for this descriptor. - * - * @hide - */ - @UnsupportedAppUsage - protected int mInstance; - - /** - * Permissions for this descriptor - * - * @hide - */ - protected int mPermissions; - - /** - * Back-reference to the characteristic this descriptor belongs to. - * - * @hide - */ - @UnsupportedAppUsage - protected BluetoothGattCharacteristic mCharacteristic; - - /** - * The value for this descriptor. - * - * @hide - */ - protected byte[] mValue; - - /** - * Create a new BluetoothGattDescriptor. - * - * @param uuid The UUID for this descriptor - * @param permissions Permissions for this descriptor - */ - public BluetoothGattDescriptor(UUID uuid, int permissions) { - initDescriptor(null, uuid, 0, permissions); - } - - /** - * Create a new BluetoothGattDescriptor. - * - * @param characteristic The characteristic this descriptor belongs to - * @param uuid The UUID for this descriptor - * @param permissions Permissions for this descriptor - */ - /*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, - int instance, int permissions) { - initDescriptor(characteristic, uuid, instance, permissions); - } - - /** - * @hide - */ - public BluetoothGattDescriptor(UUID uuid, int instance, int permissions) { - initDescriptor(null, uuid, instance, permissions); - } - - private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, - int instance, int permissions) { - mCharacteristic = characteristic; - mUuid = uuid; - mInstance = instance; - mPermissions = permissions; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(new ParcelUuid(mUuid), 0); - out.writeInt(mInstance); - out.writeInt(mPermissions); - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattDescriptor> CREATOR = - new Parcelable.Creator<BluetoothGattDescriptor>() { - public BluetoothGattDescriptor createFromParcel(Parcel in) { - return new BluetoothGattDescriptor(in); - } - - public BluetoothGattDescriptor[] newArray(int size) { - return new BluetoothGattDescriptor[size]; - } - }; - - private BluetoothGattDescriptor(Parcel in) { - mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); - mInstance = in.readInt(); - mPermissions = in.readInt(); - } - - /** - * Returns the characteristic this descriptor belongs to. - * - * @return The characteristic. - */ - public BluetoothGattCharacteristic getCharacteristic() { - return mCharacteristic; - } - - /** - * Set the back-reference to the associated characteristic - * - * @hide - */ - @UnsupportedAppUsage - /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) { - mCharacteristic = characteristic; - } - - /** - * Returns the UUID of this descriptor. - * - * @return UUID of this descriptor - */ - public UUID getUuid() { - return mUuid; - } - - /** - * Returns the instance ID for this descriptor. - * - * <p>If a remote device offers multiple descriptors with the same UUID, - * the instance ID is used to distuinguish between descriptors. - * - * @return Instance ID of this descriptor - * @hide - */ - public int getInstanceId() { - return mInstance; - } - - /** - * Force the instance ID. - * - * @hide - */ - public void setInstanceId(int instanceId) { - mInstance = instanceId; - } - - /** - * Returns the permissions for this descriptor. - * - * @return Permissions of this descriptor - */ - public int getPermissions() { - return mPermissions; - } - - /** - * Returns the stored value for this descriptor - * - * <p>This function returns the stored value for this descriptor as - * retrieved by calling {@link BluetoothGatt#readDescriptor}. The cached - * value of the descriptor is updated as a result of a descriptor read - * operation. - * - * @return Cached value of the descriptor - * - * @deprecated Use {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)} instead - */ - @Deprecated - public byte[] getValue() { - return mValue; - } - - /** - * Updates the locally stored value of this descriptor. - * - * <p>This function modifies the locally stored cached value of this - * descriptor. To send the value to the remote device, call - * {@link BluetoothGatt#writeDescriptor} to send the value to the - * remote device. - * - * @param value New value for this descriptor - * @return true if the locally stored value has been set, false if the requested value could not - * be stored locally. - * - * @deprecated Pass the descriptor value directly into - * {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} - */ - @Deprecated - public boolean setValue(byte[] value) { - mValue = value; - return true; - } -} diff --git a/core/java/android/bluetooth/BluetoothGattIncludedService.java b/core/java/android/bluetooth/BluetoothGattIncludedService.java deleted file mode 100644 index 5580619033a6..000000000000 --- a/core/java/android/bluetooth/BluetoothGattIncludedService.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.bluetooth; - -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; - -import java.util.UUID; - -/** - * Represents a Bluetooth GATT Included Service - * - * @hide - */ -public class BluetoothGattIncludedService implements Parcelable { - - /** - * The UUID of this service. - */ - protected UUID mUuid; - - /** - * Instance ID for this service. - */ - protected int mInstanceId; - - /** - * Service type (Primary/Secondary). - */ - protected int mServiceType; - - /** - * Create a new BluetoothGattIncludedService - */ - public BluetoothGattIncludedService(UUID uuid, int instanceId, int serviceType) { - mUuid = uuid; - mInstanceId = instanceId; - mServiceType = serviceType; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(new ParcelUuid(mUuid), 0); - out.writeInt(mInstanceId); - out.writeInt(mServiceType); - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattIncludedService> CREATOR = - new Parcelable.Creator<BluetoothGattIncludedService>() { - public BluetoothGattIncludedService createFromParcel(Parcel in) { - return new BluetoothGattIncludedService(in); - } - - public BluetoothGattIncludedService[] newArray(int size) { - return new BluetoothGattIncludedService[size]; - } - }; - - private BluetoothGattIncludedService(Parcel in) { - mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); - mInstanceId = in.readInt(); - mServiceType = in.readInt(); - } - - /** - * Returns the UUID of this service - * - * @return UUID of this service - */ - public UUID getUuid() { - return mUuid; - } - - /** - * Returns the instance ID for this service - * - * <p>If a remote device offers multiple services with the same UUID - * (ex. multiple battery services for different batteries), the instance - * ID is used to distuinguish services. - * - * @return Instance ID of this service - */ - public int getInstanceId() { - return mInstanceId; - } - - /** - * Get the type of this service (primary/secondary) - */ - public int getType() { - return mServiceType; - } -} diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java deleted file mode 100644 index 08e0178403f1..000000000000 --- a/core/java/android/bluetooth/BluetoothGattServer.java +++ /dev/null @@ -1,954 +0,0 @@ -/* - * Copyright (C) 2013 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.bluetooth; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.RequiresNoPermission; -import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.content.AttributionSource; -import android.os.ParcelUuid; -import android.os.RemoteException; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -/** - * Public API for the Bluetooth GATT Profile server role. - * - * <p>This class provides Bluetooth GATT server role functionality, - * allowing applications to create Bluetooth Smart services and - * characteristics. - * - * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service - * via IPC. Use {@link BluetoothManager#openGattServer} to get an instance - * of this class. - */ -public final class BluetoothGattServer implements BluetoothProfile { - private static final String TAG = "BluetoothGattServer"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - private final IBluetoothGatt mService; - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - - private BluetoothGattServerCallback mCallback; - - private Object mServerIfLock = new Object(); - private int mServerIf; - private int mTransport; - private BluetoothGattService mPendingService; - private List<BluetoothGattService> mServices; - - private static final int CALLBACK_REG_TIMEOUT = 10000; - - /** - * Bluetooth GATT interface callbacks - */ - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final IBluetoothGattServerCallback mBluetoothGattServerCallback = - new IBluetoothGattServerCallback.Stub() { - /** - * Application interface registered - app is ready to go - * @hide - */ - @Override - public void onServerRegistered(int status, int serverIf) { - if (DBG) { - Log.d(TAG, "onServerRegistered() - status=" + status - + " serverIf=" + serverIf); - } - synchronized (mServerIfLock) { - if (mCallback != null) { - mServerIf = serverIf; - mServerIfLock.notify(); - } else { - // registration timeout - Log.e(TAG, "onServerRegistered: mCallback is null"); - } - } - } - - /** - * Server connection state changed - * @hide - */ - @Override - public void onServerConnectionState(int status, int serverIf, - boolean connected, String address) { - if (DBG) { - Log.d(TAG, "onServerConnectionState() - status=" + status - + " serverIf=" + serverIf + " device=" + address); - } - try { - mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status, - connected ? BluetoothProfile.STATE_CONNECTED : - BluetoothProfile.STATE_DISCONNECTED); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } - } - - /** - * Service has been added - * @hide - */ - @Override - public void onServiceAdded(int status, BluetoothGattService service) { - if (DBG) { - Log.d(TAG, "onServiceAdded() - handle=" + service.getInstanceId() - + " uuid=" + service.getUuid() + " status=" + status); - } - - if (mPendingService == null) { - return; - } - - BluetoothGattService tmp = mPendingService; - mPendingService = null; - - // Rewrite newly assigned handles to existing service. - tmp.setInstanceId(service.getInstanceId()); - List<BluetoothGattCharacteristic> temp_chars = tmp.getCharacteristics(); - List<BluetoothGattCharacteristic> svc_chars = service.getCharacteristics(); - for (int i = 0; i < svc_chars.size(); i++) { - BluetoothGattCharacteristic temp_char = temp_chars.get(i); - BluetoothGattCharacteristic svc_char = svc_chars.get(i); - - temp_char.setInstanceId(svc_char.getInstanceId()); - - List<BluetoothGattDescriptor> temp_descs = temp_char.getDescriptors(); - List<BluetoothGattDescriptor> svc_descs = svc_char.getDescriptors(); - for (int j = 0; j < svc_descs.size(); j++) { - temp_descs.get(j).setInstanceId(svc_descs.get(j).getInstanceId()); - } - } - - mServices.add(tmp); - - try { - mCallback.onServiceAdded((int) status, tmp); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } - } - - /** - * Remote client characteristic read request. - * @hide - */ - @Override - public void onCharacteristicReadRequest(String address, int transId, - int offset, boolean isLong, int handle) { - if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle); - if (characteristic == null) { - Log.w(TAG, "onCharacteristicReadRequest() no char for handle " + handle); - return; - } - - try { - mCallback.onCharacteristicReadRequest(device, transId, offset, - characteristic); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } - } - - /** - * Remote client descriptor read request. - * @hide - */ - @Override - public void onDescriptorReadRequest(String address, int transId, - int offset, boolean isLong, int handle) { - if (VDBG) Log.d(TAG, "onCharacteristicReadRequest() - handle=" + handle); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle); - if (descriptor == null) { - Log.w(TAG, "onDescriptorReadRequest() no desc for handle " + handle); - return; - } - - try { - mCallback.onDescriptorReadRequest(device, transId, offset, descriptor); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } - } - - /** - * Remote client characteristic write request. - * @hide - */ - @Override - public void onCharacteristicWriteRequest(String address, int transId, - int offset, int length, boolean isPrep, boolean needRsp, - int handle, byte[] value) { - if (VDBG) Log.d(TAG, "onCharacteristicWriteRequest() - handle=" + handle); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattCharacteristic characteristic = getCharacteristicByHandle(handle); - if (characteristic == null) { - Log.w(TAG, "onCharacteristicWriteRequest() no char for handle " + handle); - return; - } - - try { - mCallback.onCharacteristicWriteRequest(device, transId, characteristic, - isPrep, needRsp, offset, value); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } - - } - - /** - * Remote client descriptor write request. - * @hide - */ - @Override - public void onDescriptorWriteRequest(String address, int transId, int offset, - int length, boolean isPrep, boolean needRsp, int handle, byte[] value) { - if (VDBG) Log.d(TAG, "onDescriptorWriteRequest() - handle=" + handle); - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattDescriptor descriptor = getDescriptorByHandle(handle); - if (descriptor == null) { - Log.w(TAG, "onDescriptorWriteRequest() no desc for handle " + handle); - return; - } - - try { - mCallback.onDescriptorWriteRequest(device, transId, descriptor, - isPrep, needRsp, offset, value); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } - } - - /** - * Execute pending writes. - * @hide - */ - @Override - public void onExecuteWrite(String address, int transId, - boolean execWrite) { - if (DBG) { - Log.d(TAG, "onExecuteWrite() - " - + "device=" + address + ", transId=" + transId - + "execWrite=" + execWrite); - } - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onExecuteWrite(device, transId, execWrite); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception in callback", ex); - } - } - - /** - * A notification/indication has been sent. - * @hide - */ - @Override - public void onNotificationSent(String address, int status) { - if (VDBG) { - Log.d(TAG, "onNotificationSent() - " - + "device=" + address + ", status=" + status); - } - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onNotificationSent(device, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); - } - } - - /** - * The MTU for a connection has changed - * @hide - */ - @Override - public void onMtuChanged(String address, int mtu) { - if (DBG) { - Log.d(TAG, "onMtuChanged() - " - + "device=" + address + ", mtu=" + mtu); - } - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onMtuChanged(device, mtu); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); - } - } - - /** - * The PHY for a connection was updated - * @hide - */ - @Override - public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { - if (DBG) { - Log.d(TAG, - "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy - + ", rxPHy=" + rxPhy); - } - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onPhyUpdate(device, txPhy, rxPhy, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); - } - } - - /** - * The PHY for a connection was read - * @hide - */ - @Override - public void onPhyRead(String address, int txPhy, int rxPhy, int status) { - if (DBG) { - Log.d(TAG, - "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy - + ", rxPHy=" + rxPhy); - } - - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onPhyRead(device, txPhy, rxPhy, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); - } - } - - /** - * Callback invoked when the given connection is updated - * @hide - */ - @Override - public void onConnectionUpdated(String address, int interval, int latency, - int timeout, int status) { - if (DBG) { - Log.d(TAG, "onConnectionUpdated() - Device=" + address - + " interval=" + interval + " latency=" + latency - + " timeout=" + timeout + " status=" + status); - } - BluetoothDevice device = mAdapter.getRemoteDevice(address); - if (device == null) return; - - try { - mCallback.onConnectionUpdated(device, interval, latency, - timeout, status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); - } - } - - }; - - /** - * Create a BluetoothGattServer proxy object. - */ - /* package */ BluetoothGattServer(IBluetoothGatt iGatt, int transport, - BluetoothAdapter adapter) { - mService = iGatt; - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mCallback = null; - mServerIf = 0; - mTransport = transport; - mServices = new ArrayList<BluetoothGattService>(); - } - - /** - * Returns a characteristic with given handle. - * - * @hide - */ - /*package*/ BluetoothGattCharacteristic getCharacteristicByHandle(int handle) { - for (BluetoothGattService svc : mServices) { - for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { - if (charac.getInstanceId() == handle) { - return charac; - } - } - } - return null; - } - - /** - * Returns a descriptor with given handle. - * - * @hide - */ - /*package*/ BluetoothGattDescriptor getDescriptorByHandle(int handle) { - for (BluetoothGattService svc : mServices) { - for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { - for (BluetoothGattDescriptor desc : charac.getDescriptors()) { - if (desc.getInstanceId() == handle) { - return desc; - } - } - } - } - return null; - } - - /** - * Close this GATT server instance. - * - * Application should call this method as early as possible after it is done with - * this GATT server. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void close() { - if (DBG) Log.d(TAG, "close()"); - unregisterCallback(); - } - - /** - * Register an application callback to start using GattServer. - * - * <p>This is an asynchronous call. The callback is used to notify - * success or failure if the function returns true. - * - * @param callback GATT callback handler that will receive asynchronous callbacks. - * @return true, the callback will be called to notify success or failure, false on immediate - * error - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { - return registerCallback(callback, false); - } - - /** - * Register an application callback to start using GattServer. - * - * <p>This is an asynchronous call. The callback is used to notify - * success or failure if the function returns true. - * - * @param callback GATT callback handler that will receive asynchronous callbacks. - * @param eatt_support indicates if server can use eatt - * @return true, the callback will be called to notify success or failure, false on immediate - * error - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - /*package*/ boolean registerCallback(BluetoothGattServerCallback callback, - boolean eatt_support) { - if (DBG) Log.d(TAG, "registerCallback()"); - if (mService == null) { - Log.e(TAG, "GATT service not available"); - return false; - } - UUID uuid = UUID.randomUUID(); - if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); - - synchronized (mServerIfLock) { - if (mCallback != null) { - Log.e(TAG, "App can register callback only once"); - return false; - } - - mCallback = callback; - try { - mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback, - eatt_support, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - mCallback = null; - return false; - } - - try { - mServerIfLock.wait(CALLBACK_REG_TIMEOUT); - } catch (InterruptedException e) { - Log.e(TAG, "" + e); - mCallback = null; - } - - if (mServerIf == 0) { - mCallback = null; - return false; - } else { - return true; - } - } - } - - /** - * Unregister the current application and callbacks. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private void unregisterCallback() { - if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf); - if (mService == null || mServerIf == 0) return; - - try { - mCallback = null; - mService.unregisterServer(mServerIf, mAttributionSource); - mServerIf = 0; - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - /** - * Returns a service by UUID, instance and type. - * - * @hide - */ - /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { - for (BluetoothGattService svc : mServices) { - if (svc.getType() == type - && svc.getInstanceId() == instanceId - && svc.getUuid().equals(uuid)) { - return svc; - } - } - return null; - } - - /** - * Initiate a connection to a Bluetooth GATT capable device. - * - * <p>The connection may not be established right away, but will be - * completed when the remote device is available. A - * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be - * invoked when the connection state changes as a result of this function. - * - * <p>The autoConnect parameter determines whether to actively connect to - * the remote device, or rather passively scan and finalize the connection - * when the remote device is in range/available. Generally, the first ever - * connection to a device should be direct (autoConnect set to false) and - * subsequent connections to known devices should be invoked with the - * autoConnect parameter set to true. - * - * @param autoConnect Whether to directly connect to the remote device (false) or to - * automatically connect as soon as the remote device becomes available (true). - * @return true, if the connection attempt was initiated successfully - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean connect(BluetoothDevice device, boolean autoConnect) { - if (DBG) { - Log.d(TAG, - "connect() - device: " + device.getAddress() + ", auto: " + autoConnect); - } - if (mService == null || mServerIf == 0) return false; - - try { - // autoConnect is inverse of "isDirect" - mService.serverConnect( - mServerIf, device.getAddress(), !autoConnect, mTransport, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * Disconnects an established connection, or cancels a connection attempt - * currently in progress. - * - * @param device Remote device - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void cancelConnection(BluetoothDevice device) { - if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress()); - if (mService == null || mServerIf == 0) return; - - try { - mService.serverDisconnect(mServerIf, device.getAddress(), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - /** - * Set the preferred connection PHY for this app. Please note that this is just a - * recommendation, whether the PHY change will happen depends on other applications peferences, - * local and remote controller capabilities. Controller can override these settings. <p> {@link - * BluetoothGattServerCallback#onPhyUpdate} will be triggered as a result of this call, even if - * no PHY change happens. It is also triggered when remote device updates the PHY. - * - * @param device The remote device to send this response to - * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link - * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link - * BluetoothDevice#PHY_LE_CODED_MASK}. - * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link - * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link - * BluetoothDevice#PHY_LE_CODED_MASK}. - * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one - * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or - * {@link BluetoothDevice#PHY_OPTION_S8} - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) { - try { - mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy, - phyOptions, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - /** - * Read the current transmitter PHY and receiver PHY of the connection. The values are returned - * in {@link BluetoothGattServerCallback#onPhyRead} - * - * @param device The remote device to send this response to - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void readPhy(BluetoothDevice device) { - try { - mService.serverReadPhy(mServerIf, device.getAddress(), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - /** - * Send a response to a read or write request to a remote device. - * - * <p>This function must be invoked in when a remote read/write request - * is received by one of these callback methods: - * - * <ul> - * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest} - * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest} - * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest} - * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest} - * </ul> - * - * @param device The remote device to send this response to - * @param requestId The ID of the request that was received with the callback - * @param status The status of the request to be sent to the remote devices - * @param offset Value offset for partial read/write response - * @param value The value of the attribute that was read/written (optional) - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean sendResponse(BluetoothDevice device, int requestId, - int status, int offset, byte[] value) { - if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress()); - if (mService == null || mServerIf == 0) return false; - - try { - mService.sendResponse(mServerIf, device.getAddress(), requestId, - status, offset, value, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - return true; - } - - /** - * Send a notification or indication that a local characteristic has been - * updated. - * - * <p>A notification or indication is sent to the remote device to signal - * that the characteristic has been updated. This function should be invoked - * for every client that requests notifications/indications by writing - * to the "Client Configuration" descriptor for the given characteristic. - * - * @param device The remote device to receive the notification/indication - * @param characteristic The local characteristic that has been updated - * @param confirm true to request confirmation from the client (indication), false to send a - * notification - * @return true, if the notification has been triggered successfully - * @throws IllegalArgumentException - * - * @deprecated Use {@link BluetoothGattServer#notifyCharacteristicChanged(BluetoothDevice, - * BluetoothGattCharacteristic, boolean, byte[])} as this is not memory safe. - */ - @Deprecated - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean notifyCharacteristicChanged(BluetoothDevice device, - BluetoothGattCharacteristic characteristic, boolean confirm) { - return notifyCharacteristicChanged(device, characteristic, confirm, - characteristic.getValue()) == BluetoothStatusCodes.SUCCESS; - } - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - BluetoothStatusCodes.SUCCESS, - BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, - BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION, - BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED, - BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, - BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED, - BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY, - BluetoothStatusCodes.ERROR_UNKNOWN - }) - public @interface NotifyCharacteristicReturnValues{} - - /** - * Send a notification or indication that a local characteristic has been - * updated. - * - * <p>A notification or indication is sent to the remote device to signal - * that the characteristic has been updated. This function should be invoked - * for every client that requests notifications/indications by writing - * to the "Client Configuration" descriptor for the given characteristic. - * - * @param device the remote device to receive the notification/indication - * @param characteristic the local characteristic that has been updated - * @param confirm {@code true} to request confirmation from the client (indication) or - * {@code false} to send a notification - * @param value the characteristic value - * @return whether the notification has been triggered successfully - * @throws IllegalArgumentException if the characteristic value or service is null - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @NotifyCharacteristicReturnValues - public int notifyCharacteristicChanged(@NonNull BluetoothDevice device, - @NonNull BluetoothGattCharacteristic characteristic, boolean confirm, - @NonNull byte[] value) { - if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); - if (mService == null || mServerIf == 0) { - return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; - } - - if (characteristic == null) { - throw new IllegalArgumentException("characteristic must not be null"); - } - if (device == null) { - throw new IllegalArgumentException("device must not be null"); - } - BluetoothGattService service = characteristic.getService(); - if (service == null) { - throw new IllegalArgumentException("Characteristic must have a non-null service"); - } - if (value == null) { - throw new IllegalArgumentException("Characteristic value must not be null"); - } - - try { - return mService.sendNotification(mServerIf, device.getAddress(), - characteristic.getInstanceId(), confirm, - value, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - throw e.rethrowFromSystemServer(); - } - } - - /** - * Add a service to the list of services to be hosted. - * - * <p>Once a service has been addded to the list, the service and its - * included characteristics will be provided by the local device. - * - * <p>If the local device has already exposed services when this function - * is called, a service update notification will be sent to all clients. - * - * <p>The {@link BluetoothGattServerCallback#onServiceAdded} callback will indicate - * whether this service has been added successfully. Do not add another service - * before this callback. - * - * @param service Service to be added to the list of services provided by this device. - * @return true, if the request to add service has been initiated - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean addService(BluetoothGattService service) { - if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid()); - if (mService == null || mServerIf == 0) return false; - - mPendingService = service; - - try { - mService.addService(mServerIf, service, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * Removes a service from the list of services to be provided. - * - * @param service Service to be removed. - * @return true, if the service has been removed - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean removeService(BluetoothGattService service) { - if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid()); - if (mService == null || mServerIf == 0) return false; - - BluetoothGattService intService = getService(service.getUuid(), - service.getInstanceId(), service.getType()); - if (intService == null) return false; - - try { - mService.removeService(mServerIf, service.getInstanceId(), mAttributionSource); - mServices.remove(intService); - } catch (RemoteException e) { - Log.e(TAG, "", e); - return false; - } - - return true; - } - - /** - * Remove all services from the list of provided services. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void clearServices() { - if (DBG) Log.d(TAG, "clearServices()"); - if (mService == null || mServerIf == 0) return; - - try { - mService.clearServices(mServerIf, mAttributionSource); - mServices.clear(); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - /** - * Returns a list of GATT services offered by this device. - * - * <p>An application must call {@link #addService} to add a serice to the - * list of services offered by this device. - * - * @return List of services. Returns an empty list if no services have been added yet. - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public List<BluetoothGattService> getServices() { - return mServices; - } - - /** - * Returns a {@link BluetoothGattService} from the list of services offered - * by this device. - * - * <p>If multiple instances of the same service (as identified by UUID) - * exist, the first instance of the service is returned. - * - * @param uuid UUID of the requested service - * @return BluetoothGattService if supported, or null if the requested service is not offered by - * this device. - */ - @RequiresLegacyBluetoothPermission - @RequiresNoPermission - public BluetoothGattService getService(UUID uuid) { - for (BluetoothGattService service : mServices) { - if (service.getUuid().equals(uuid)) { - return service; - } - } - - return null; - } - - - /** - * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} - * with {@link BluetoothProfile#GATT} as argument - * - * @throws UnsupportedOperationException - */ - @Override - @RequiresNoPermission - public int getConnectionState(BluetoothDevice device) { - throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); - } - - /** - * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} - * with {@link BluetoothProfile#GATT} as argument - * - * @throws UnsupportedOperationException - */ - @Override - @RequiresNoPermission - public List<BluetoothDevice> getConnectedDevices() { - throw new UnsupportedOperationException( - "Use BluetoothManager#getConnectedDevices instead."); - } - - /** - * Not supported - please use - * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} - * with {@link BluetoothProfile#GATT} as first argument - * - * @throws UnsupportedOperationException - */ - @Override - @RequiresNoPermission - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - throw new UnsupportedOperationException( - "Use BluetoothManager#getDevicesMatchingConnectionStates instead."); - } -} diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java deleted file mode 100644 index 0ead5f57e86c..000000000000 --- a/core/java/android/bluetooth/BluetoothGattServerCallback.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -/** - * This abstract class is used to implement {@link BluetoothGattServer} callbacks. - */ -public abstract class BluetoothGattServerCallback { - - /** - * Callback indicating when a remote device has been connected or disconnected. - * - * @param device Remote device that has been connected or disconnected. - * @param status Status of the connect or disconnect operation. - * @param newState Returns the new connection state. Can be one of {@link - * BluetoothProfile#STATE_DISCONNECTED} or {@link BluetoothProfile#STATE_CONNECTED} - */ - public void onConnectionStateChange(BluetoothDevice device, int status, - int newState) { - } - - /** - * Indicates whether a local service has been added successfully. - * - * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service was added - * successfully. - * @param service The service that has been added - */ - public void onServiceAdded(int status, BluetoothGattService service) { - } - - /** - * A remote client has requested to read a local characteristic. - * - * <p>An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the read operation - * @param requestId The Id of the request - * @param offset Offset into the value of the characteristic - * @param characteristic Characteristic to be read - */ - public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattCharacteristic characteristic) { - } - - /** - * A remote client has requested to write to a local characteristic. - * - * <p>An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operation - * @param requestId The Id of the request - * @param characteristic Characteristic to be written to. - * @param preparedWrite true, if this write operation should be queued for later execution. - * @param responseNeeded true, if the remote device requires a response - * @param offset The offset given for the value - * @param value The value the client wants to assign to the characteristic - */ - public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattCharacteristic characteristic, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { - } - - /** - * A remote client has requested to read a local descriptor. - * - * <p>An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the read operation - * @param requestId The Id of the request - * @param offset Offset into the value of the characteristic - * @param descriptor Descriptor to be read - */ - public void onDescriptorReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattDescriptor descriptor) { - } - - /** - * A remote client has requested to write to a local descriptor. - * - * <p>An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operation - * @param requestId The Id of the request - * @param descriptor Descriptor to be written to. - * @param preparedWrite true, if this write operation should be queued for later execution. - * @param responseNeeded true, if the remote device requires a response - * @param offset The offset given for the value - * @param value The value the client wants to assign to the descriptor - */ - public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattDescriptor descriptor, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { - } - - /** - * Execute all pending write operations for this device. - * - * <p>An application must call {@link BluetoothGattServer#sendResponse} - * to complete the request. - * - * @param device The remote device that has requested the write operations - * @param requestId The Id of the request - * @param execute Whether the pending writes should be executed (true) or cancelled (false) - */ - public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { - } - - /** - * Callback invoked when a notification or indication has been sent to - * a remote device. - * - * <p>When multiple notifications are to be sent, an application must - * wait for this callback to be received before sending additional - * notifications. - * - * @param device The remote device the notification has been sent to - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful - */ - public void onNotificationSent(BluetoothDevice device, int status) { - } - - /** - * Callback indicating the MTU for a given device connection has changed. - * - * <p>This callback will be invoked if a remote client has requested to change - * the MTU for a given connection. - * - * @param device The remote device that requested the MTU change - * @param mtu The new MTU size - */ - public void onMtuChanged(BluetoothDevice device, int mtu) { - } - - /** - * Callback triggered as result of {@link BluetoothGattServer#setPreferredPhy}, or as a result - * of remote device changing the PHY. - * - * @param device The remote device - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link - * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link - * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param status Status of the PHY update operation. {@link BluetoothGatt#GATT_SUCCESS} if the - * operation succeeds. - */ - public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) { - } - - /** - * Callback triggered as result of {@link BluetoothGattServer#readPhy} - * - * @param device The remote device that requested the PHY read - * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link - * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M}, {@link - * BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED} - * @param status Status of the PHY read operation. {@link BluetoothGatt#GATT_SUCCESS} if the - * operation succeeds. - */ - public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) { - } - - /** - * Callback indicating the connection parameters were updated. - * - * @param device The remote device involved - * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from - * 6 (7.5ms) to 3200 (4000ms). - * @param latency Worker latency for the connection in number of connection events. Valid range - * is from 0 to 499 - * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10 - * (0.1s) to 3200 (32s) - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the connection has been updated - * successfully - * @hide - */ - public void onConnectionUpdated(BluetoothDevice device, int interval, int latency, int timeout, - int status) { - } - -} diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java deleted file mode 100644 index f64d09fc30d9..000000000000 --- a/core/java/android/bluetooth/BluetoothGattService.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright (C) 2013 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.bluetooth; - -import android.annotation.RequiresPermission; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -/** - * Represents a Bluetooth GATT Service - * - * <p> Gatt Service contains a collection of {@link BluetoothGattCharacteristic}, - * as well as referenced services. - */ -public class BluetoothGattService implements Parcelable { - - /** - * Primary service - */ - public static final int SERVICE_TYPE_PRIMARY = 0; - - /** - * Secondary service (included by primary services) - */ - public static final int SERVICE_TYPE_SECONDARY = 1; - - - /** - * The remote device this service is associated with. - * This applies to client applications only. - * - * @hide - */ - @UnsupportedAppUsage - protected BluetoothDevice mDevice; - - /** - * The UUID of this service. - * - * @hide - */ - protected UUID mUuid; - - /** - * Instance ID for this service. - * - * @hide - */ - protected int mInstanceId; - - /** - * Handle counter override (for conformance testing). - * - * @hide - */ - protected int mHandles = 0; - - /** - * Service type (Primary/Secondary). - * - * @hide - */ - protected int mServiceType; - - /** - * List of characteristics included in this service. - */ - protected List<BluetoothGattCharacteristic> mCharacteristics; - - /** - * List of included services for this service. - */ - protected List<BluetoothGattService> mIncludedServices; - - /** - * Whether the service uuid should be advertised. - */ - private boolean mAdvertisePreferred; - - /** - * Create a new BluetoothGattService. - * - * @param uuid The UUID for this service - * @param serviceType The type of this service, - * {@link BluetoothGattService#SERVICE_TYPE_PRIMARY} - * or {@link BluetoothGattService#SERVICE_TYPE_SECONDARY} - */ - public BluetoothGattService(UUID uuid, int serviceType) { - mDevice = null; - mUuid = uuid; - mInstanceId = 0; - mServiceType = serviceType; - mCharacteristics = new ArrayList<BluetoothGattCharacteristic>(); - mIncludedServices = new ArrayList<BluetoothGattService>(); - } - - /** - * Create a new BluetoothGattService - * - * @hide - */ - /*package*/ BluetoothGattService(BluetoothDevice device, UUID uuid, - int instanceId, int serviceType) { - mDevice = device; - mUuid = uuid; - mInstanceId = instanceId; - mServiceType = serviceType; - mCharacteristics = new ArrayList<BluetoothGattCharacteristic>(); - mIncludedServices = new ArrayList<BluetoothGattService>(); - } - - /** - * Create a new BluetoothGattService - * - * @hide - */ - public BluetoothGattService(UUID uuid, int instanceId, int serviceType) { - mDevice = null; - mUuid = uuid; - mInstanceId = instanceId; - mServiceType = serviceType; - mCharacteristics = new ArrayList<BluetoothGattCharacteristic>(); - mIncludedServices = new ArrayList<BluetoothGattService>(); - } - - /** - * @hide - */ - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(new ParcelUuid(mUuid), 0); - out.writeInt(mInstanceId); - out.writeInt(mServiceType); - out.writeTypedList(mCharacteristics); - - ArrayList<BluetoothGattIncludedService> includedServices = - new ArrayList<BluetoothGattIncludedService>(mIncludedServices.size()); - for (BluetoothGattService s : mIncludedServices) { - includedServices.add(new BluetoothGattIncludedService(s.getUuid(), - s.getInstanceId(), s.getType())); - } - out.writeTypedList(includedServices); - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothGattService> CREATOR = - new Parcelable.Creator<BluetoothGattService>() { - public BluetoothGattService createFromParcel(Parcel in) { - return new BluetoothGattService(in); - } - - public BluetoothGattService[] newArray(int size) { - return new BluetoothGattService[size]; - } - }; - - private BluetoothGattService(Parcel in) { - mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); - mInstanceId = in.readInt(); - mServiceType = in.readInt(); - - mCharacteristics = new ArrayList<BluetoothGattCharacteristic>(); - - ArrayList<BluetoothGattCharacteristic> chrcs = - in.createTypedArrayList(BluetoothGattCharacteristic.CREATOR); - if (chrcs != null) { - for (BluetoothGattCharacteristic chrc : chrcs) { - chrc.setService(this); - mCharacteristics.add(chrc); - } - } - - mIncludedServices = new ArrayList<BluetoothGattService>(); - - ArrayList<BluetoothGattIncludedService> inclSvcs = - in.createTypedArrayList(BluetoothGattIncludedService.CREATOR); - if (chrcs != null) { - for (BluetoothGattIncludedService isvc : inclSvcs) { - mIncludedServices.add(new BluetoothGattService(null, isvc.getUuid(), - isvc.getInstanceId(), isvc.getType())); - } - } - } - - /** - * Returns the device associated with this service. - * - * @hide - */ - /*package*/ BluetoothDevice getDevice() { - return mDevice; - } - - /** - * Returns the device associated with this service. - * - * @hide - */ - /*package*/ void setDevice(BluetoothDevice device) { - mDevice = device; - } - - /** - * Add an included service to this service. - * - * @param service The service to be added - * @return true, if the included service was added to the service - */ - @RequiresLegacyBluetoothPermission - public boolean addService(BluetoothGattService service) { - mIncludedServices.add(service); - return true; - } - - /** - * Add a characteristic to this service. - * - * @param characteristic The characteristics to be added - * @return true, if the characteristic was added to the service - */ - @RequiresLegacyBluetoothPermission - public boolean addCharacteristic(BluetoothGattCharacteristic characteristic) { - mCharacteristics.add(characteristic); - characteristic.setService(this); - return true; - } - - /** - * Get characteristic by UUID and instanceId. - * - * @hide - */ - /*package*/ BluetoothGattCharacteristic getCharacteristic(UUID uuid, int instanceId) { - for (BluetoothGattCharacteristic characteristic : mCharacteristics) { - if (uuid.equals(characteristic.getUuid()) - && characteristic.getInstanceId() == instanceId) { - return characteristic; - } - } - return null; - } - - /** - * Force the instance ID. - * - * @hide - */ - @UnsupportedAppUsage - public void setInstanceId(int instanceId) { - mInstanceId = instanceId; - } - - /** - * Get the handle count override (conformance testing. - * - * @hide - */ - /*package*/ int getHandles() { - return mHandles; - } - - /** - * Force the number of handles to reserve for this service. - * This is needed for conformance testing only. - * - * @hide - */ - public void setHandles(int handles) { - mHandles = handles; - } - - /** - * Add an included service to the internal map. - * - * @hide - */ - public void addIncludedService(BluetoothGattService includedService) { - mIncludedServices.add(includedService); - } - - /** - * Returns the UUID of this service - * - * @return UUID of this service - */ - public UUID getUuid() { - return mUuid; - } - - /** - * Returns the instance ID for this service - * - * <p>If a remote device offers multiple services with the same UUID - * (ex. multiple battery services for different batteries), the instance - * ID is used to distuinguish services. - * - * @return Instance ID of this service - */ - public int getInstanceId() { - return mInstanceId; - } - - /** - * Get the type of this service (primary/secondary) - */ - public int getType() { - return mServiceType; - } - - /** - * Get the list of included GATT services for this service. - * - * @return List of included services or empty list if no included services were discovered. - */ - public List<BluetoothGattService> getIncludedServices() { - return mIncludedServices; - } - - /** - * Returns a list of characteristics included in this service. - * - * @return Characteristics included in this service - */ - public List<BluetoothGattCharacteristic> getCharacteristics() { - return mCharacteristics; - } - - /** - * Returns a characteristic with a given UUID out of the list of - * characteristics offered by this service. - * - * <p>This is a convenience function to allow access to a given characteristic - * without enumerating over the list returned by {@link #getCharacteristics} - * manually. - * - * <p>If a remote service offers multiple characteristics with the same - * UUID, the first instance of a characteristic with the given UUID - * is returned. - * - * @return GATT characteristic object or null if no characteristic with the given UUID was - * found. - */ - public BluetoothGattCharacteristic getCharacteristic(UUID uuid) { - for (BluetoothGattCharacteristic characteristic : mCharacteristics) { - if (uuid.equals(characteristic.getUuid())) { - return characteristic; - } - } - return null; - } - - /** - * Returns whether the uuid of the service should be advertised. - * - * @hide - */ - public boolean isAdvertisePreferred() { - return mAdvertisePreferred; - } - - /** - * Set whether the service uuid should be advertised. - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void setAdvertisePreferred(boolean advertisePreferred) { - mAdvertisePreferred = advertisePreferred; - } -} diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java deleted file mode 100644 index 1b141c9afaca..000000000000 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ /dev/null @@ -1,1505 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.util.CloseGuard; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * Public API for controlling the Bluetooth Headset Service. This includes both - * Bluetooth Headset and Handsfree (v1.5) profiles. - * - * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset - * Service via IPC. - * - * <p> Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothHeadset proxy object. Use - * {@link BluetoothAdapter#closeProfileProxy} to close the service connection. - * - * <p> Android only supports one connected Bluetooth Headset at a time. - * Each method is protected with its appropriate permission. - */ -public final class BluetoothHeadset implements BluetoothProfile { - private static final String TAG = "BluetoothHeadset"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** - * Intent used to broadcast the change in connection state of the Headset - * profile. - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED"; - - /** - * Intent used to broadcast the change in the Audio Connection state of the - * HFP profile. - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED}, - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_AUDIO_STATE_CHANGED = - "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"; - - /** - * Intent used to broadcast the selection of a connected device as active. - * - * <p>This intent will have one extra: - * <ul> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can - * be null if no device is active. </li> - * </ul> - * - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(trackingBug = 171933273) - public static final String ACTION_ACTIVE_DEVICE_CHANGED = - "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; - - /** - * Intent used to broadcast that the headset has posted a - * vendor-specific event. - * - * <p>This intent will have 4 extras and 1 category. - * <ul> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device - * </li> - * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor - * specific command </li> - * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT - * command type which can be one of {@link #AT_CMD_TYPE_READ}, - * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET}, - * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}. </li> - * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command - * arguments. </li> - * </ul> - * - * <p> The category is the Company ID of the vendor defining the - * vendor-specific command. {@link BluetoothAssignedNumbers} - * - * For example, for Plantronics specific events - * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55 - * - * <p> For example, an AT+XEVENT=foo,3 will get translated into - * <ul> - * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT </li> - * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li> - * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li> - * </ul> - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT = - "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT"; - - /** - * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} - * intents that contains the name of the vendor-specific command. - */ - public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = - "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD"; - - /** - * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} - * intents that contains the AT command type of the vendor-specific command. - */ - public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = - "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE"; - - /** - * AT command type READ used with - * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - * For example, AT+VGM?. There are no arguments for this command type. - */ - public static final int AT_CMD_TYPE_READ = 0; - - /** - * AT command type TEST used with - * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - * For example, AT+VGM=?. There are no arguments for this command type. - */ - public static final int AT_CMD_TYPE_TEST = 1; - - /** - * AT command type SET used with - * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - * For example, AT+VGM=<args>. - */ - public static final int AT_CMD_TYPE_SET = 2; - - /** - * AT command type BASIC used with - * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - * For example, ATD. Single character commands and everything following the - * character are arguments. - */ - public static final int AT_CMD_TYPE_BASIC = 3; - - /** - * AT command type ACTION used with - * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - * For example, AT+CHUP. There are no arguments for action commands. - */ - public static final int AT_CMD_TYPE_ACTION = 4; - - /** - * A Parcelable String array extra field in - * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains - * the arguments to the vendor-specific command. - */ - public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = - "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS"; - - /** - * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} - * for the companyId - */ - public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = - "android.bluetooth.headset.intent.category.companyid"; - - /** - * A vendor-specific command for unsolicited result code. - */ - public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID"; - - /** - * A vendor-specific AT command - * - * @hide - */ - public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL"; - - /** - * A vendor-specific AT command - * - * @hide - */ - public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV"; - - /** - * Battery level indicator associated with - * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV} - * - * @hide - */ - public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1; - - /** - * A vendor-specific AT command - * - * @hide - */ - public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT"; - - /** - * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT} - * - * @hide - */ - public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY"; - - /** - * Headset state when SCO audio is not connected. - * This state can be one of - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of - * {@link #ACTION_AUDIO_STATE_CHANGED} intent. - */ - public static final int STATE_AUDIO_DISCONNECTED = 10; - - /** - * Headset state when SCO audio is connecting. - * This state can be one of - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of - * {@link #ACTION_AUDIO_STATE_CHANGED} intent. - */ - public static final int STATE_AUDIO_CONNECTING = 11; - - /** - * Headset state when SCO audio is connected. - * This state can be one of - * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of - * {@link #ACTION_AUDIO_STATE_CHANGED} intent. - */ - public static final int STATE_AUDIO_CONNECTED = 12; - - /** - * Intent used to broadcast the headset's indicator status - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which - * is supported by the headset ( as indicated by AT+BIND command in the SLC - * sequence) or whose value is changed (indicated by AT+BIEV command) </li> - * <li> {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator. </li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - Remote device. </li> - * </ul> - * <p>{@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators - * are given an assigned number. Below shows the assigned number of Indicator added so far - * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled - * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery - * - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_HF_INDICATORS_VALUE_CHANGED = - "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED"; - - /** - * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} - * intents that contains the assigned number of the headset indicator as defined by - * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7 - * - * @hide - */ - public static final String EXTRA_HF_INDICATORS_IND_ID = - "android.bluetooth.headset.extra.HF_INDICATORS_IND_ID"; - - /** - * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED} - * intents that contains the value of the Headset indicator that is being sent. - * - * @hide - */ - public static final String EXTRA_HF_INDICATORS_IND_VALUE = - "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE"; - - private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100; - private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101; - - private final CloseGuard mCloseGuard = new CloseGuard(); - - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHeadset mService; - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - - @SuppressLint("AndroidFrameworkBluetoothPermission") - 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 BluetoothHeadset proxy object. - */ - /* package */ BluetoothHeadset(Context context, ServiceListener l, BluetoothAdapter adapter) { - mContext = context; - mServiceListener = l; - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - - // Preserve legacy compatibility where apps were depending on - // registerStateChangeCallback() performing a permissions check which - // has been relaxed in modern platform versions - if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R - && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Need BLUETOOTH permission"); - } - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - mCloseGuard.open("close"); - } - - private boolean doBind() { - synchronized (mConnection) { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - try { - return mAdapter.getBluetoothManager().bindBluetoothProfileService( - BluetoothProfile.HEADSET, mConnection); - } catch (RemoteException e) { - Log.e(TAG, "Unable to bind HeadsetService", e); - } - } - } - return false; - } - - private void doUnbind() { - synchronized (mConnection) { - if (mService != null) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - try { - mAdapter.getBluetoothManager().unbindBluetoothProfileService( - BluetoothProfile.HEADSET, mConnection); - } catch (RemoteException e) { - Log.e(TAG, "Unable to unbind HeadsetService", e); - } finally { - mService = null; - } - } - } - } - - /** - * Close the connection to the backing service. - * Other public functions of BluetoothHeadset will return default error - * results once close() has been called. Multiple invocations of close() - * are ok. - */ - @UnsupportedAppUsage - /*package*/ void close() { - if (VDBG) log("close()"); - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "", re); - } - } - mServiceListener = null; - doUnbind(); - mCloseGuard.close(); - } - - /** {@hide} */ - @Override - protected void finalize() throws Throwable { - mCloseGuard.warnIfOpen(); - close(); - } - - /** - * Initiate connection to a profile of the remote bluetooth device. - * - * <p> Currently, the system supports only 1 connection to the - * headset/handsfree profile. The API will automatically disconnect connected - * devices before connecting. - * - * <p> This API returns false in scenarios like the profile on the - * device is already connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that - * connection state intent for the profile will be broadcasted with - * the state. Users can get the connection state of the profile - * from this intent. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public boolean connect(BluetoothDevice device) { - if (DBG) log("connect(" + device + ")"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connectWithAttribution(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate disconnection from a profile - * - * <p> This API will return false in scenarios like the profile on the - * Bluetooth device is not in connected state etc. When this API returns, - * true, it is guaranteed that the connection state change - * intent will be broadcasted with the state. Users can get the - * disconnection state of the profile from this intent. - * - * <p> If the disconnection is initiated by a remote device, the state - * will transition from {@link #STATE_CONNECTED} to - * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the - * host (local) device the state will transition from - * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to - * state {@link #STATE_DISCONNECTED}. The transition to - * {@link #STATE_DISCONNECTING} can be used to distinguish between the - * two scenarios. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnect(BluetoothDevice device) { - if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnectWithAttribution(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getConnectedDevices() { - if (VDBG) log("getConnectedDevices()"); - final IBluetoothHeadset service = mService; - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevicesWithAttribution(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHeadset service = mService; - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(BluetoothDevice device) { - if (VDBG) log("getConnectionState(" + device + ")"); - final IBluetoothHeadset service = mService; - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionStateWithAttribution(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or - * {@link BluetoothProfile#PRIORITY_OFF} - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)} - * @removed - */ - @Deprecated - @SystemApi - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the priority of the profile. - * - * <p> The priority can be any of: - * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, - * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - final IBluetoothHeadset service = mService; - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Checks whether the headset supports some form of noise reduction - * - * @param device Bluetooth device - * @return true if echo cancellation and/or noise reduction is supported, false otherwise - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) { - if (DBG) log("isNoiseReductionSupported()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isNoiseReductionSupported(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Checks whether the headset supports voice recognition - * - * @param device Bluetooth device - * @return true if voice recognition is supported, false otherwise - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) { - if (DBG) log("isVoiceRecognitionSupported()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isVoiceRecognitionSupported(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Start Bluetooth voice recognition. This methods sends the voice - * recognition AT command to the headset and establishes the - * audio connection. - * - * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. - * If this function returns true, this intent will be broadcasted with - * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. - * - * <p> {@link #EXTRA_STATE} will transition from - * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when - * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} - * in case of failure to establish the audio connection. - * - * @param device Bluetooth headset - * @return false if there is no headset connected, or the connected headset doesn't support - * voice recognition, or voice recognition is already started, or audio channel is occupied, - * or on error, true otherwise - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public boolean startVoiceRecognition(BluetoothDevice device) { - if (DBG) log("startVoiceRecognition()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.startVoiceRecognition(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Stop Bluetooth Voice Recognition mode, and shut down the - * Bluetooth audio path. - * - * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. - * If this function returns true, this intent will be broadcasted with - * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. - * - * @param device Bluetooth headset - * @return false if there is no headset connected, or voice recognition has not started, - * or voice recognition has ended on this headset, or on error, true otherwise - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean stopVoiceRecognition(BluetoothDevice device) { - if (DBG) log("stopVoiceRecognition()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.stopVoiceRecognition(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Check if Bluetooth SCO audio is connected. - * - * @param device Bluetooth headset - * @return true if SCO is connected, false otherwise or on error - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isAudioConnected(BluetoothDevice device) { - if (VDBG) log("isAudioConnected()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isAudioConnected(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Indicates if current platform supports voice dialing over bluetooth SCO. - * - * @return true if voice dialing over bluetooth is supported, false otherwise. - * @hide - */ - public static boolean isBluetoothVoiceDialingEnabled(Context context) { - return context.getResources().getBoolean( - com.android.internal.R.bool.config_bluetooth_sco_off_call); - } - - /** - * Get the current audio state of the Headset. - * Note: This is an internal function and shouldn't be exposed - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getAudioState(BluetoothDevice device) { - if (VDBG) log("getAudioState"); - final IBluetoothHeadset service = mService; - final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (!isDisabled()) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getAudioState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any - * audio to the HF unless explicitly told to. - * This method should be used in cases where the SCO channel is shared between multiple profiles - * and must be delegated by a source knowledgeable - * Note: This is an internal function and shouldn't be exposed - * - * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void setAudioRouteAllowed(boolean allowed) { - if (VDBG) log("setAudioRouteAllowed"); - final IBluetoothHeadset service = mService; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver recv = new SynchronousResultReceiver(); - service.setAudioRouteAllowed(allowed, mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}. - * Note: This is an internal function and shouldn't be exposed - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean getAudioRouteAllowed() { - if (VDBG) log("getAudioRouteAllowed"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.getAudioRouteAllowed(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Force SCO audio to be opened regardless any other restrictions - * - * @param forced Whether or not SCO audio connection should be forced: True to force SCO audio - * False to use SCO audio in normal manner - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void setForceScoAudio(boolean forced) { - if (VDBG) log("setForceScoAudio " + String.valueOf(forced)); - final IBluetoothHeadset service = mService; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver recv = new SynchronousResultReceiver(); - service.setForceScoAudio(forced, mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Check if at least one headset's SCO audio is connected or connecting - * - * @return true if at least one device's SCO audio is connected or connecting, false otherwise - * or on error - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isAudioOn() { - if (VDBG) log("isAudioOn()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isAudioOn(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiates a connection of headset audio to the current active device - * - * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. - * If this function returns true, this intent will be broadcasted with - * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. - * - * <p> {@link #EXTRA_STATE} will transition from - * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when - * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} - * in case of failure to establish the audio connection. - * - * Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true - * before calling this method - * - * @return false if there was some error such as there is no active headset - * @hide - */ - @UnsupportedAppUsage - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean connectAudio() { - if (VDBG) log("connectAudio()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connectAudio(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiates a disconnection of HFP SCO audio. - * Tear down voice recognition or virtual voice call if any. - * - * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. - * If this function returns true, this intent will be broadcasted with - * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. - * - * @return false if audio is not connected, or on error, true otherwise - * @hide - */ - @UnsupportedAppUsage - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnectAudio() { - if (VDBG) log("disconnectAudio()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnectAudio(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiates a SCO channel connection as a virtual voice call to the current active device - * Active handsfree device will be notified of incoming call and connected call. - * - * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. - * If this function returns true, this intent will be broadcasted with - * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. - * - * <p> {@link #EXTRA_STATE} will transition from - * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when - * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} - * in case of failure to establish the audio connection. - * - * @return true if successful, false if one of the following case applies - * - SCO audio is not idle (connecting or connected) - * - virtual call has already started - * - there is no active device - * - a Telecom managed call is going on - * - binder is dead or Bluetooth is disabled or other error - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public boolean startScoUsingVirtualVoiceCall() { - if (DBG) log("startScoUsingVirtualVoiceCall()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.startScoUsingVirtualVoiceCall(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Terminates an ongoing SCO connection and the associated virtual call. - * - * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. - * If this function returns true, this intent will be broadcasted with - * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. - * - * @return true if successful, false if one of the following case applies - * - virtual voice call is not started or has ended - * - binder is dead or Bluetooth is disabled or other error - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public boolean stopScoUsingVirtualVoiceCall() { - if (DBG) log("stopScoUsingVirtualVoiceCall()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.stopScoUsingVirtualVoiceCall(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Notify Headset of phone state change. - * This is a backdoor for phone app to call BluetoothHeadset since - * there is currently not a good way to get precise call state change outside - * of phone app. - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public void phoneStateChanged(int numActive, int numHeld, int callState, String number, - int type, String name) { - final IBluetoothHeadset service = mService; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - service.phoneStateChanged(numActive, numHeld, callState, number, type, name, - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Send Headset of CLCC response - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - public void clccResponse(int index, int direction, int status, int mode, boolean mpty, - String number, int type) { - final IBluetoothHeadset service = mService; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver recv = new SynchronousResultReceiver(); - service.clccResponse(index, direction, status, mode, mpty, number, type, - mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Sends a vendor-specific unsolicited result code to the headset. - * - * <p>The actual string to be sent is <code>command + ": " + arg</code>. For example, if {@code - * command} is {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} and {@code arg} is {@code "0"}, the - * string <code>"+ANDROID: 0"</code> will be sent. - * - * <p>Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}. - * - * @param device Bluetooth headset. - * @param command A vendor-specific command. - * @param arg The argument that will be attached to the command. - * @return {@code false} if there is no headset connected, or if the command is not an allowed - * vendor-specific unsolicited result code, or on error. {@code true} otherwise. - * @throws IllegalArgumentException if {@code command} is {@code null}. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, - String arg) { - if (DBG) { - log("sendVendorSpecificResultCode()"); - } - if (command == null) { - throw new IllegalArgumentException("command is null"); - } - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.sendVendorSpecificResultCode(device, command, arg, - mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Select a connected device as active. - * - * The active device selection is per profile. An active device's - * purpose is profile-specific. For example, in HFP and HSP profiles, - * it is the device used for phone call audio. If a remote device is not - * connected, it cannot be selected as active. - * - * <p> This API returns false in scenarios like the profile on the - * device is not connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that the - * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted - * with the active device. - * - * @param device Remote Bluetooth Device, could be null if phone call audio should not be - * streamed to a headset - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.MODIFY_PHONE_STATE, - }) - @UnsupportedAppUsage(trackingBug = 171933273) - public boolean setActiveDevice(@Nullable BluetoothDevice device) { - if (DBG) { - Log.d(TAG, "setActiveDevice: " + device); - } - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && (device == null || isValidDevice(device))) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setActiveDevice(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the connected device that is active. - * - * @return the connected device that is active or null if no device - * is active. - * @hide - */ - @UnsupportedAppUsage(trackingBug = 171933273) - @Nullable - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothDevice getActiveDevice() { - if (VDBG) Log.d(TAG, "getActiveDevice"); - final IBluetoothHeadset service = mService; - final BluetoothDevice defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<BluetoothDevice> recv = - new SynchronousResultReceiver(); - service.getActiveDevice(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Check if in-band ringing is currently enabled. In-band ringing could be disabled during an - * active connection. - * - * @return true if in-band ringing is enabled, false if in-band ringing is disabled - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean isInbandRingingEnabled() { - if (DBG) log("isInbandRingingEnabled()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isInbandRingingEnabled(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Check if in-band ringing is supported for this platform. - * - * @return true if in-band ringing is supported, false if in-band ringing is not supported - * @hide - */ - public static boolean isInbandRingingSupported(Context context) { - return context.getResources().getBoolean( - com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support); - } - - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final IBluetoothProfileServiceConnection mConnection = - new IBluetoothProfileServiceConnection.Stub() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadset.Stub.asInterface(service); - mHandler.sendMessage(mHandler.obtainMessage( - MESSAGE_HEADSET_SERVICE_CONNECTED)); - } - - @Override - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - doUnbind(); - mHandler.sendMessage(mHandler.obtainMessage( - MESSAGE_HEADSET_SERVICE_DISCONNECTED)); - } - }; - - @UnsupportedAppUsage - private boolean isEnabled() { - return mAdapter.getState() == BluetoothAdapter.STATE_ON; - } - - private boolean isDisabled() { - return mAdapter.getState() == BluetoothAdapter.STATE_OFF; - } - - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - private static void log(String msg) { - Log.d(TAG, msg); - } - - @SuppressLint("AndroidFrameworkBluetoothPermission") - private final Handler mHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_HEADSET_SERVICE_CONNECTED: { - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, - BluetoothHeadset.this); - } - break; - } - case MESSAGE_HEADSET_SERVICE_DISCONNECTED: { - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); - } - break; - } - } - } - }; -} diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java deleted file mode 100644 index 7d7a7f798bb9..000000000000 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ /dev/null @@ -1,1356 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.Context; -import android.os.Build; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * Public API to control Hands Free Profile (HFP role only). - * <p> - * This class defines methods that shall be used by application to manage profile - * connection, calls states and calls actions. - * <p> - * - * @hide - */ -public final class BluetoothHeadsetClient implements BluetoothProfile { - private static final String TAG = "BluetoothHeadsetClient"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** - * Intent sent whenever connection to remote changes. - * - * <p>It includes two extras: - * <code>BluetoothProfile.EXTRA_PREVIOUS_STATE</code> - * and <code>BluetoothProfile.EXTRA_STATE</code>, which - * are mandatory. - * <p>There are also non mandatory feature extras: - * {@link #EXTRA_AG_FEATURE_3WAY_CALLING}, - * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}, - * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}, - * {@link #EXTRA_AG_FEATURE_REJECT_CALL}, - * {@link #EXTRA_AG_FEATURE_ECC}, - * {@link #EXTRA_AG_FEATURE_RESPONSE_AND_HOLD}, - * {@link #EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL}, - * {@link #EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL}, - * {@link #EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT}, - * {@link #EXTRA_AG_FEATURE_MERGE}, - * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH}, - * sent as boolean values only when <code>EXTRA_STATE</code> - * is set to <code>STATE_CONNECTED</code>.</p> - * - * <p>Note that features supported by AG are being sent as - * booleans with value <code>true</code>, - * and not supported ones are <strong>not</strong> being sent at all.</p> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED"; - - /** - * Intent sent whenever audio state changes. - * - * <p>It includes two mandatory extras: - * {@link BluetoothProfile#EXTRA_STATE}, - * {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}, - * with possible values: - * {@link #STATE_AUDIO_CONNECTING}, - * {@link #STATE_AUDIO_CONNECTED}, - * {@link #STATE_AUDIO_DISCONNECTED}</p> - * <p>When <code>EXTRA_STATE</code> is set - * to </code>STATE_AUDIO_CONNECTED</code>, - * it also includes {@link #EXTRA_AUDIO_WBS} - * indicating wide band speech support.</p> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_AUDIO_STATE_CHANGED = - "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED"; - - /** - * Intent sending updates of the Audio Gateway state. - * Each extra is being sent only when value it - * represents has been changed recently on AG. - * <p>It can contain one or more of the following extras: - * {@link #EXTRA_NETWORK_STATUS}, - * {@link #EXTRA_NETWORK_SIGNAL_STRENGTH}, - * {@link #EXTRA_NETWORK_ROAMING}, - * {@link #EXTRA_BATTERY_LEVEL}, - * {@link #EXTRA_OPERATOR_NAME}, - * {@link #EXTRA_VOICE_RECOGNITION}, - * {@link #EXTRA_IN_BAND_RING}</p> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_AG_EVENT = - "android.bluetooth.headsetclient.profile.action.AG_EVENT"; - - /** - * Intent sent whenever state of a call changes. - * - * <p>It includes: - * {@link #EXTRA_CALL}, - * with value of {@link BluetoothHeadsetClientCall} instance, - * representing actual call state.</p> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CALL_CHANGED = - "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED"; - - /** - * Intent that notifies about the result of the last issued action. - * Please note that not every action results in explicit action result code being sent. - * Instead other notifications about new Audio Gateway state might be sent, - * like <code>ACTION_AG_EVENT</code> with <code>EXTRA_VOICE_RECOGNITION</code> value - * when for example user started voice recognition from HF unit. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_RESULT = - "android.bluetooth.headsetclient.profile.action.RESULT"; - - /** - * Intent that notifies about vendor specific event arrival. Events not defined in - * HFP spec will be matched with supported vendor event list and this intent will - * be broadcasted upon a match. Supported vendor events are of format of - * of "+eventCode" or "+eventCode=xxxx" or "+eventCode:=xxxx". - * Vendor event can be a response to an vendor specific command or unsolicited. - * - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT = - "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT"; - - /** - * Intent that notifies about the number attached to the last voice tag - * recorded on AG. - * - * <p>It contains: - * {@link #EXTRA_NUMBER}, - * with a <code>String</code> value representing phone number.</p> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_LAST_VTAG = - "android.bluetooth.headsetclient.profile.action.LAST_VTAG"; - - public static final int STATE_AUDIO_DISCONNECTED = 0; - public static final int STATE_AUDIO_CONNECTING = 1; - public static final int STATE_AUDIO_CONNECTED = 2; - - /** - * Extra with information if connected audio is WBS. - * <p>Possible values: <code>true</code>, - * <code>false</code>.</p> - */ - public static final String EXTRA_AUDIO_WBS = - "android.bluetooth.headsetclient.extra.AUDIO_WBS"; - - /** - * Extra for AG_EVENT indicates network status. - * <p>Value: 0 - network unavailable, - * 1 - network available </p> - */ - public static final String EXTRA_NETWORK_STATUS = - "android.bluetooth.headsetclient.extra.NETWORK_STATUS"; - /** - * Extra for AG_EVENT intent indicates network signal strength. - * <p>Value: <code>Integer</code> representing signal strength.</p> - */ - public static final String EXTRA_NETWORK_SIGNAL_STRENGTH = - "android.bluetooth.headsetclient.extra.NETWORK_SIGNAL_STRENGTH"; - /** - * Extra for AG_EVENT intent indicates roaming state. - * <p>Value: 0 - no roaming - * 1 - active roaming</p> - */ - public static final String EXTRA_NETWORK_ROAMING = - "android.bluetooth.headsetclient.extra.NETWORK_ROAMING"; - /** - * Extra for AG_EVENT intent indicates the battery level. - * <p>Value: <code>Integer</code> representing signal strength.</p> - */ - public static final String EXTRA_BATTERY_LEVEL = - "android.bluetooth.headsetclient.extra.BATTERY_LEVEL"; - /** - * Extra for AG_EVENT intent indicates operator name. - * <p>Value: <code>String</code> representing operator name.</p> - */ - public static final String EXTRA_OPERATOR_NAME = - "android.bluetooth.headsetclient.extra.OPERATOR_NAME"; - /** - * Extra for AG_EVENT intent indicates voice recognition state. - * <p>Value: - * 0 - voice recognition stopped, - * 1 - voice recognition started.</p> - */ - public static final String EXTRA_VOICE_RECOGNITION = - "android.bluetooth.headsetclient.extra.VOICE_RECOGNITION"; - /** - * Extra for AG_EVENT intent indicates in band ring state. - * <p>Value: - * 0 - in band ring tone not supported, or - * 1 - in band ring tone supported.</p> - */ - public static final String EXTRA_IN_BAND_RING = - "android.bluetooth.headsetclient.extra.IN_BAND_RING"; - - /** - * Extra for AG_EVENT intent indicates subscriber info. - * <p>Value: <code>String</code> containing subscriber information.</p> - */ - public static final String EXTRA_SUBSCRIBER_INFO = - "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO"; - - /** - * Extra for AG_CALL_CHANGED intent indicates the - * {@link BluetoothHeadsetClientCall} object that has changed. - */ - public static final String EXTRA_CALL = - "android.bluetooth.headsetclient.extra.CALL"; - - /** - * Extra for ACTION_LAST_VTAG intent. - * <p>Value: <code>String</code> representing phone number - * corresponding to last voice tag recorded on AG</p> - */ - public static final String EXTRA_NUMBER = - "android.bluetooth.headsetclient.extra.NUMBER"; - - /** - * Extra for ACTION_RESULT intent that shows the result code of - * last issued action. - * <p>Possible results: - * {@link #ACTION_RESULT_OK}, - * {@link #ACTION_RESULT_ERROR}, - * {@link #ACTION_RESULT_ERROR_NO_CARRIER}, - * {@link #ACTION_RESULT_ERROR_BUSY}, - * {@link #ACTION_RESULT_ERROR_NO_ANSWER}, - * {@link #ACTION_RESULT_ERROR_DELAYED}, - * {@link #ACTION_RESULT_ERROR_BLACKLISTED}, - * {@link #ACTION_RESULT_ERROR_CME}</p> - */ - public static final String EXTRA_RESULT_CODE = - "android.bluetooth.headsetclient.extra.RESULT_CODE"; - - /** - * Extra for ACTION_RESULT intent that shows the extended result code of - * last issued action. - * <p>Value: <code>Integer</code> - error code.</p> - */ - public static final String EXTRA_CME_CODE = - "android.bluetooth.headsetclient.extra.CME_CODE"; - - /** - * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that - * indicates vendor ID. - */ - public static final String EXTRA_VENDOR_ID = - "android.bluetooth.headsetclient.extra.VENDOR_ID"; - - /** - * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that - * indicates vendor event code. - */ - public static final String EXTRA_VENDOR_EVENT_CODE = - "android.bluetooth.headsetclient.extra.VENDOR_EVENT_CODE"; - - /** - * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that - * contains full vendor event including event code and full arguments. - */ - public static final String EXTRA_VENDOR_EVENT_FULL_ARGS = - "android.bluetooth.headsetclient.extra.VENDOR_EVENT_FULL_ARGS"; - - - /* Extras for AG_FEATURES, extras type is boolean */ - // TODO verify if all of those are actually useful - /** - * AG feature: three way calling. - */ - public static final String EXTRA_AG_FEATURE_3WAY_CALLING = - "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING"; - /** - * AG feature: voice recognition. - */ - public static final String EXTRA_AG_FEATURE_VOICE_RECOGNITION = - "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION"; - /** - * AG feature: fetching phone number for voice tagging procedure. - */ - public static final String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT = - "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT"; - /** - * AG feature: ability to reject incoming call. - */ - public static final String EXTRA_AG_FEATURE_REJECT_CALL = - "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_REJECT_CALL"; - /** - * AG feature: enhanced call handling (terminate specific call, private consultation). - */ - public static final String EXTRA_AG_FEATURE_ECC = - "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ECC"; - /** - * AG feature: response and hold. - */ - public static final String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD = - "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD"; - /** - * AG call handling feature: accept held or waiting call in three way calling scenarios. - */ - public static final String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL = - "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL"; - /** - * AG call handling feature: release held or waiting call in three way calling scenarios. - */ - public static final String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL = - "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL"; - /** - * AG call handling feature: release active call and accept held or waiting call in three way - * calling scenarios. - */ - public static final String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT = - "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT"; - /** - * AG call handling feature: merge two calls, held and active - multi party conference mode. - */ - public static final String EXTRA_AG_FEATURE_MERGE = - "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE"; - /** - * AG call handling feature: merge calls and disconnect from multi party - * conversation leaving peers connected to each other. - * Note that this feature needs to be supported by mobile network operator - * as it requires connection and billing transfer. - */ - public static final String EXTRA_AG_FEATURE_MERGE_AND_DETACH = - "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH"; - - /* Action result codes */ - public static final int ACTION_RESULT_OK = 0; - public static final int ACTION_RESULT_ERROR = 1; - public static final int ACTION_RESULT_ERROR_NO_CARRIER = 2; - public static final int ACTION_RESULT_ERROR_BUSY = 3; - public static final int ACTION_RESULT_ERROR_NO_ANSWER = 4; - public static final int ACTION_RESULT_ERROR_DELAYED = 5; - public static final int ACTION_RESULT_ERROR_BLACKLISTED = 6; - public static final int ACTION_RESULT_ERROR_CME = 7; - - /* Detailed CME error codes */ - public static final int CME_PHONE_FAILURE = 0; - public static final int CME_NO_CONNECTION_TO_PHONE = 1; - public static final int CME_OPERATION_NOT_ALLOWED = 3; - public static final int CME_OPERATION_NOT_SUPPORTED = 4; - public static final int CME_PHSIM_PIN_REQUIRED = 5; - public static final int CME_PHFSIM_PIN_REQUIRED = 6; - public static final int CME_PHFSIM_PUK_REQUIRED = 7; - public static final int CME_SIM_NOT_INSERTED = 10; - public static final int CME_SIM_PIN_REQUIRED = 11; - public static final int CME_SIM_PUK_REQUIRED = 12; - public static final int CME_SIM_FAILURE = 13; - public static final int CME_SIM_BUSY = 14; - public static final int CME_SIM_WRONG = 15; - public static final int CME_INCORRECT_PASSWORD = 16; - public static final int CME_SIM_PIN2_REQUIRED = 17; - public static final int CME_SIM_PUK2_REQUIRED = 18; - public static final int CME_MEMORY_FULL = 20; - public static final int CME_INVALID_INDEX = 21; - public static final int CME_NOT_FOUND = 22; - public static final int CME_MEMORY_FAILURE = 23; - public static final int CME_TEXT_STRING_TOO_LONG = 24; - public static final int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25; - public static final int CME_DIAL_STRING_TOO_LONG = 26; - public static final int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27; - public static final int CME_NO_NETWORK_SERVICE = 30; - public static final int CME_NETWORK_TIMEOUT = 31; - public static final int CME_EMERGENCY_SERVICE_ONLY = 32; - public static final int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33; - public static final int CME_NOT_SUPPORTED_FOR_VOIP = 34; - public static final int CME_SIP_RESPONSE_CODE = 35; - public static final int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40; - public static final int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41; - public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42; - public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43; - public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44; - public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45; - public static final int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46; - public static final int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47; - public static final int CME_HIDDEN_KEY_REQUIRED = 48; - public static final int CME_EAP_NOT_SUPPORTED = 49; - public static final int CME_INCORRECT_PARAMETERS = 50; - - /* Action policy for other calls when accepting call */ - public static final int CALL_ACCEPT_NONE = 0; - public static final int CALL_ACCEPT_HOLD = 1; - public static final int CALL_ACCEPT_TERMINATE = 2; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT, - "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) { - @Override - public IBluetoothHeadsetClient getServiceInterface(IBinder service) { - return IBluetoothHeadsetClient.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothHeadsetClient proxy object. - */ - /* package */ BluetoothHeadsetClient(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - } - - /** - * Close the connection to the backing service. - * Other public functions of BluetoothHeadsetClient will return default error - * results once close() has been called. Multiple invocations of close() - * are ok. - */ - /*package*/ void close() { - if (VDBG) log("close()"); - mProfileConnector.disconnect(); - } - - private IBluetoothHeadsetClient getService() { - return mProfileConnector.getService(); - } - - /** - * Connects to remote device. - * - * Currently, the system supports only 1 connection. So, in case of the - * second connection, this implementation will disconnect already connected - * device automatically and will process the new one. - * - * @param device a remote device we want connect to - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean connect(BluetoothDevice device) { - if (DBG) log("connect(" + device + ")"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Disconnects remote device - * - * @param device a remote device we want disconnect - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnect(BluetoothDevice device) { - if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Return the list of connected remote devices - * - * @return list of connected devices; empty list if nothing is connected. - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getConnectedDevices() { - if (VDBG) log("getConnectedDevices()"); - final IBluetoothHeadsetClient service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Returns list of remote devices in a particular state - * - * @param states collection of states - * @return list of devices that state matches the states listed in <code>states</code>; empty - * list if nothing matches the <code>states</code> - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHeadsetClient service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Returns state of the <code>device</code> - * - * @param device a remote device - * @return the state of connection of the device - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(BluetoothDevice device) { - if (VDBG) log("getConnectionState(" + device + ")"); - final IBluetoothHeadsetClient service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF} - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the priority of the profile. - * - * <p> The priority can be any of: - * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - final IBluetoothHeadsetClient service = getService(); - final @ConnectionPolicy int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Starts voice recognition. - * - * @param device remote device - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent. - * - * <p>Feature required for successful execution is being reported by: {@link - * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature - * is not supported.</p> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean startVoiceRecognition(BluetoothDevice device) { - if (DBG) log("startVoiceRecognition()"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.startVoiceRecognition(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Send vendor specific AT command. - * - * @param device remote device - * @param vendorId vendor number by Bluetooth SIG - * @param atCommand command to be sent. It start with + prefix and only one command at one time. - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) { - if (DBG) log("sendVendorSpecificCommand()"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Stops voice recognition. - * - * @param device remote device - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent. - * - * <p>Feature required for successful execution is being reported by: {@link - * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature - * is not supported.</p> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean stopVoiceRecognition(BluetoothDevice device) { - if (DBG) log("stopVoiceRecognition()"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.stopVoiceRecognition(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Returns list of all calls in any state. - * - * @param device remote device - * @return list of calls; empty list if none call exists - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) { - if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = getService(); - final List<BluetoothHeadsetClientCall> defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<List<BluetoothHeadsetClientCall>> recv = - new SynchronousResultReceiver(); - service.getCurrentCalls(device, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Returns list of current values of AG indicators. - * - * @param device remote device - * @return bundle of AG indicators; null if device is not in CONNECTED state - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public Bundle getCurrentAgEvents(BluetoothDevice device) { - if (DBG) log("getCurrentAgEvents()"); - final IBluetoothHeadsetClient service = getService(); - final Bundle defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver(); - service.getCurrentAgEvents(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Accepts a call - * - * @param device remote device - * @param flag action policy while accepting a call. Possible values {@link #CALL_ACCEPT_NONE}, - * {@link #CALL_ACCEPT_HOLD}, {@link #CALL_ACCEPT_TERMINATE} - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean acceptCall(BluetoothDevice device, int flag) { - if (DBG) log("acceptCall()"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.acceptCall(device, flag, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Holds a call. - * - * @param device remote device - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean holdCall(BluetoothDevice device) { - if (DBG) log("holdCall()"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.holdCall(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Rejects a call. - * - * @param device remote device - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. - * - * <p>Feature required for successful execution is being reported by: {@link - * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not - * supported.</p> - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean rejectCall(BluetoothDevice device) { - if (DBG) log("rejectCall()"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.rejectCall(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Terminates a specified call. - * - * Works only when Extended Call Control is supported by Audio Gateway. - * - * @param device remote device - * @param call Handle of call obtained in {@link #dial(BluetoothDevice, String)} or obtained via - * {@link #ACTION_CALL_CHANGED}. {@code call} may be null in which case we will hangup all active - * calls. - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. - * - * <p>Feature required for successful execution is being reported by: {@link - * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not - * supported.</p> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { - if (DBG) log("terminateCall()"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.terminateCall(device, call, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Enters private mode with a specified call. - * - * Works only when Extended Call Control is supported by Audio Gateway. - * - * @param device remote device - * @param index index of the call to connect in private mode - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. - * - * <p>Feature required for successful execution is being reported by: {@link - * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not - * supported.</p> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean enterPrivateMode(BluetoothDevice device, int index) { - if (DBG) log("enterPrivateMode()"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.enterPrivateMode(device, index, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Performs explicit call transfer. - * - * That means connect other calls and disconnect. - * - * @param device remote device - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. - * - * <p>Feature required for successful execution is being reported by: {@link - * #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. This method invocation will fail silently when feature - * is not supported.</p> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean explicitCallTransfer(BluetoothDevice device) { - if (DBG) log("explicitCallTransfer()"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.explicitCallTransfer(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Places a call with specified number. - * - * @param device remote device - * @param number valid phone number - * @return <code>{@link BluetoothHeadsetClientCall} call</code> if command has been issued - * successfully; <code>{@link null}</code> otherwise; upon completion HFP sends {@link - * #ACTION_CALL_CHANGED} intent in case of success; {@link #ACTION_RESULT} is sent otherwise; - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { - if (DBG) log("dial()"); - final IBluetoothHeadsetClient service = getService(); - final BluetoothHeadsetClientCall defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<BluetoothHeadsetClientCall> recv = - new SynchronousResultReceiver(); - service.dial(device, number, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Sends DTMF code. - * - * Possible code values : 0,1,2,3,4,5,6,7,8,9,A,B,C,D,*,# - * - * @param device remote device - * @param code ASCII code - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_RESULT} intent; - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean sendDTMF(BluetoothDevice device, byte code) { - if (DBG) log("sendDTMF()"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.sendDTMF(device, code, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get a number corresponding to last voice tag recorded on AG. - * - * @param device remote device - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_LAST_VTAG} or {@link #ACTION_RESULT} - * intent; - * - * <p>Feature required for successful execution is being reported by: {@link - * #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. This method invocation will fail silently when - * feature is not supported.</p> - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean getLastVoiceTagNumber(BluetoothDevice device) { - if (DBG) log("getLastVoiceTagNumber()"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.getLastVoiceTagNumber(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Returns current audio state of Audio Gateway. - * - * Note: This is an internal function and shouldn't be exposed - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getAudioState(BluetoothDevice device) { - if (VDBG) log("getAudioState"); - final IBluetoothHeadsetClient service = getService(); - final int defaultValue = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getAudioState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } else { - return defaultValue; - } - return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; - } - - /** - * Sets whether audio routing is allowed. - * - * @param device remote device - * @param allowed if routing is allowed to the device Note: This is an internal function and - * shouldn't be exposed - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { - if (VDBG) log("setAudioRouteAllowed"); - final IBluetoothHeadsetClient service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver recv = new SynchronousResultReceiver(); - service.setAudioRouteAllowed(device, allowed, mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Returns whether audio routing is allowed. - * - * @param device remote device - * @return whether the command succeeded Note: This is an internal function and shouldn't be - * exposed - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean getAudioRouteAllowed(BluetoothDevice device) { - if (VDBG) log("getAudioRouteAllowed"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.getAudioRouteAllowed(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiates a connection of audio channel. - * - * It setup SCO channel with remote connected Handsfree AG device. - * - * @param device remote device - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean connectAudio(BluetoothDevice device) { - if (VDBG) log("connectAudio"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connectAudio(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Disconnects audio channel. - * - * It tears down the SCO channel from remote AG device. - * - * @param device remote device - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnectAudio(BluetoothDevice device) { - if (VDBG) log("disconnectAudio"); - final IBluetoothHeadsetClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnectAudio(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get Audio Gateway features - * - * @param device remote device - * @return bundle of AG features; null if no service or AG not connected - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public Bundle getCurrentAgFeatures(BluetoothDevice device) { - if (VDBG) log("getCurrentAgFeatures"); - final IBluetoothHeadsetClient service = getService(); - final Bundle defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver(); - service.getCurrentAgFeatures(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - private boolean isEnabled() { - return mAdapter.getState() == BluetoothAdapter.STATE_ON; - } - - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java deleted file mode 100644 index 032b507f5d3c..000000000000 --- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.NonNull; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.os.Build; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.SystemClock; - -import java.util.UUID; - -/** - * This class represents a single call, its state and properties. - * It implements {@link Parcelable} for inter-process message passing. - * - * @hide - */ -public final class BluetoothHeadsetClientCall implements Parcelable, Attributable { - - /* Call state */ - /** - * Call is active. - */ - public static final int CALL_STATE_ACTIVE = 0; - /** - * Call is in held state. - */ - public static final int CALL_STATE_HELD = 1; - /** - * Outgoing call that is being dialed right now. - */ - public static final int CALL_STATE_DIALING = 2; - /** - * Outgoing call that remote party has already been alerted about. - */ - public static final int CALL_STATE_ALERTING = 3; - /** - * Incoming call that can be accepted or rejected. - */ - public static final int CALL_STATE_INCOMING = 4; - /** - * Waiting call state when there is already an active call. - */ - public static final int CALL_STATE_WAITING = 5; - /** - * Call that has been held by response and hold - * (see Bluetooth specification for further references). - */ - public static final int CALL_STATE_HELD_BY_RESPONSE_AND_HOLD = 6; - /** - * Call that has been already terminated and should not be referenced as a valid call. - */ - public static final int CALL_STATE_TERMINATED = 7; - - private final BluetoothDevice mDevice; - private final int mId; - private int mState; - private String mNumber; - private boolean mMultiParty; - private final boolean mOutgoing; - private final UUID mUUID; - private final long mCreationElapsedMilli; - private final boolean mInBandRing; - - /** - * Creates BluetoothHeadsetClientCall instance. - */ - public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number, - boolean multiParty, boolean outgoing, boolean inBandRing) { - this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing, inBandRing); - } - - public BluetoothHeadsetClientCall(BluetoothDevice device, int id, UUID uuid, int state, - String number, boolean multiParty, boolean outgoing, boolean inBandRing) { - mDevice = device; - mId = id; - mUUID = uuid; - mState = state; - mNumber = number != null ? number : ""; - mMultiParty = multiParty; - mOutgoing = outgoing; - mInBandRing = inBandRing; - mCreationElapsedMilli = SystemClock.elapsedRealtime(); - } - - /** {@hide} */ - public void setAttributionSource(@NonNull AttributionSource attributionSource) { - Attributable.setAttributionSource(mDevice, attributionSource); - } - - /** - * Sets call's state. - * - * <p>Note: This is an internal function and shouldn't be exposed</p> - * - * @param state new call state. - */ - public void setState(int state) { - mState = state; - } - - /** - * Sets call's number. - * - * <p>Note: This is an internal function and shouldn't be exposed</p> - * - * @param number String representing phone number. - */ - public void setNumber(String number) { - mNumber = number; - } - - /** - * Sets this call as multi party call. - * - * <p>Note: This is an internal function and shouldn't be exposed</p> - * - * @param multiParty if <code>true</code> sets this call as a part of multi party conference. - */ - public void setMultiParty(boolean multiParty) { - mMultiParty = multiParty; - } - - /** - * Gets call's device. - * - * @return call device. - */ - public BluetoothDevice getDevice() { - return mDevice; - } - - /** - * Gets call's Id. - * - * @return call id. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public int getId() { - return mId; - } - - /** - * Gets call's UUID. - * - * @return call uuid - * @hide - */ - public UUID getUUID() { - return mUUID; - } - - /** - * Gets call's current state. - * - * @return state of this particular phone call. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public int getState() { - return mState; - } - - /** - * Gets call's number. - * - * @return string representing phone number. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public String getNumber() { - return mNumber; - } - - /** - * Gets call's creation time in millis since epoch. - * - * @return long representing the creation time. - */ - public long getCreationElapsedMilli() { - return mCreationElapsedMilli; - } - - /** - * Checks if call is an active call in a conference mode (aka multi party). - * - * @return <code>true</code> if call is a multi party call, <code>false</code> otherwise. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public boolean isMultiParty() { - return mMultiParty; - } - - /** - * Checks if this call is an outgoing call. - * - * @return <code>true</code> if its outgoing call, <code>false</code> otherwise. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public boolean isOutgoing() { - return mOutgoing; - } - - /** - * Checks if the ringtone will be generated by the connected phone - * - * @return <code>true</code> if in band ring is enabled, <code>false</code> otherwise. - */ - public boolean isInBandRing() { - return mInBandRing; - } - - - @Override - public String toString() { - return toString(false); - } - - /** - * Generate a log string for this call - * @param loggable whether device address should be logged - * @return log string - */ - public String toString(boolean loggable) { - StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: "); - builder.append(loggable ? mDevice : mDevice.hashCode()); - builder.append(", mId: "); - builder.append(mId); - builder.append(", mUUID: "); - builder.append(mUUID); - builder.append(", mState: "); - switch (mState) { - case CALL_STATE_ACTIVE: - builder.append("ACTIVE"); - break; - case CALL_STATE_HELD: - builder.append("HELD"); - break; - case CALL_STATE_DIALING: - builder.append("DIALING"); - break; - case CALL_STATE_ALERTING: - builder.append("ALERTING"); - break; - case CALL_STATE_INCOMING: - builder.append("INCOMING"); - break; - case CALL_STATE_WAITING: - builder.append("WAITING"); - break; - case CALL_STATE_HELD_BY_RESPONSE_AND_HOLD: - builder.append("HELD_BY_RESPONSE_AND_HOLD"); - break; - case CALL_STATE_TERMINATED: - builder.append("TERMINATED"); - break; - default: - builder.append(mState); - break; - } - builder.append(", mNumber: "); - builder.append(loggable ? mNumber : mNumber.hashCode()); - builder.append(", mMultiParty: "); - builder.append(mMultiParty); - builder.append(", mOutgoing: "); - builder.append(mOutgoing); - builder.append(", mInBandRing: "); - builder.append(mInBandRing); - builder.append("}"); - return builder.toString(); - } - - /** - * {@link Parcelable.Creator} interface implementation. - */ - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHeadsetClientCall> CREATOR = - new Parcelable.Creator<BluetoothHeadsetClientCall>() { - @Override - public BluetoothHeadsetClientCall createFromParcel(Parcel in) { - return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null), - in.readInt(), UUID.fromString(in.readString()), in.readInt(), - in.readString(), in.readInt() == 1, in.readInt() == 1, - in.readInt() == 1); - } - - @Override - public BluetoothHeadsetClientCall[] newArray(int size) { - return new BluetoothHeadsetClientCall[size]; - } - }; - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(mDevice, 0); - out.writeInt(mId); - out.writeString(mUUID.toString()); - out.writeInt(mState); - out.writeString(mNumber); - out.writeInt(mMultiParty ? 1 : 0); - out.writeInt(mOutgoing ? 1 : 0); - out.writeInt(mInBandRing ? 1 : 0); - } - - @Override - public int describeContents() { - return 0; - } -} diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java deleted file mode 100644 index 65f68a943e08..000000000000 --- a/core/java/android/bluetooth/BluetoothHealth.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.os.ParcelFileDescriptor; -import android.util.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * Public API for Bluetooth Health Profile. - * - * <p>BluetoothHealth is a proxy object for controlling the Bluetooth - * Service via IPC. - * - * <p> How to connect to a health device which is acting in the source role. - * <li> Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothHealth proxy object. </li> - * <li> Create an {@link BluetoothHealth} callback and call - * {@link #registerSinkAppConfiguration} to register an application - * configuration </li> - * <li> Pair with the remote device. This currently needs to be done manually - * from Bluetooth Settings </li> - * <li> Connect to a health device using {@link #connectChannelToSource}. Some - * devices will connect the channel automatically. The {@link BluetoothHealth} - * callback will inform the application of channel state change. </li> - * <li> Use the file descriptor provided with a connected channel to read and - * write data to the health channel. </li> - * <li> The received data needs to be interpreted using a health manager which - * implements the IEEE 11073-xxxxx specifications. - * <li> When done, close the health channel by calling {@link #disconnectChannel} - * and unregister the application configuration calling - * {@link #unregisterAppConfiguration} - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New apps - * should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ -@Deprecated -public final class BluetoothHealth implements BluetoothProfile { - private static final String TAG = "BluetoothHealth"; - /** - * Health Profile Source Role - the health device. - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int SOURCE_ROLE = 1 << 0; - - /** - * Health Profile Sink Role the device talking to the health device. - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int SINK_ROLE = 1 << 1; - - /** - * Health Profile - Channel Type used - Reliable - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int CHANNEL_TYPE_RELIABLE = 10; - - /** - * Health Profile - Channel Type used - Streaming - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int CHANNEL_TYPE_STREAMING = 11; - - /** - * Hide auto-created default constructor - * @hide - */ - BluetoothHealth() {} - - /** - * Register an application configuration that acts as a Health SINK. - * This is the configuration that will be used to communicate with health devices - * which will act as the {@link #SOURCE_ROLE}. This is an asynchronous call and so - * the callback is used to notify success or failure if the function returns true. - * - * @param name The friendly name associated with the application or configuration. - * @param dataType The dataType of the Source role of Health Profile to which the sink wants to - * connect to. - * @param callback A callback to indicate success or failure of the registration and all - * operations done on this application configuration. - * @return If true, callback will be called. - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public boolean registerSinkAppConfiguration(String name, int dataType, - BluetoothHealthCallback callback) { - Log.e(TAG, "registerSinkAppConfiguration(): BluetoothHealth is deprecated"); - return false; - } - - /** - * Unregister an application configuration that has been registered using - * {@link #registerSinkAppConfiguration} - * - * @param config The health app configuration - * @return Success or failure. - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { - Log.e(TAG, "unregisterAppConfiguration(): BluetoothHealth is deprecated"); - return false; - } - - /** - * Connect to a health device which has the {@link #SOURCE_ROLE}. - * This is an asynchronous call. If this function returns true, the callback - * associated with the application configuration will be called. - * - * @param device The remote Bluetooth device. - * @param config The application configuration which has been registered using {@link - * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } - * @return If true, the callback associated with the application config will be called. - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public boolean connectChannelToSource(BluetoothDevice device, - BluetoothHealthAppConfiguration config) { - Log.e(TAG, "connectChannelToSource(): BluetoothHealth is deprecated"); - return false; - } - - /** - * Disconnect a connected health channel. - * This is an asynchronous call. If this function returns true, the callback - * associated with the application configuration will be called. - * - * @param device The remote Bluetooth device. - * @param config The application configuration which has been registered using {@link - * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } - * @param channelId The channel id associated with the channel - * @return If true, the callback associated with the application config will be called. - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public boolean disconnectChannel(BluetoothDevice device, - BluetoothHealthAppConfiguration config, int channelId) { - Log.e(TAG, "disconnectChannel(): BluetoothHealth is deprecated"); - return false; - } - - /** - * Get the file descriptor of the main channel associated with the remote device - * and application configuration. - * - * <p> Its the responsibility of the caller to close the ParcelFileDescriptor - * when done. - * - * @param device The remote Bluetooth health device - * @param config The application configuration - * @return null on failure, ParcelFileDescriptor on success. - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, - BluetoothHealthAppConfiguration config) { - Log.e(TAG, "getMainChannelFd(): BluetoothHealth is deprecated"); - return null; - } - - /** - * Get the current connection state of the profile. - * - * This is not specific to any application configuration but represents the connection - * state of the local Bluetooth adapter with the remote device. This can be used - * by applications like status bar which would just like to know the state of the - * local adapter. - * - * @param device Remote bluetooth device. - * @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link - * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} - */ - @Override - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public int getConnectionState(BluetoothDevice device) { - Log.e(TAG, "getConnectionState(): BluetoothHealth is deprecated"); - return STATE_DISCONNECTED; - } - - /** - * Get connected devices for the health profile. - * - * <p> Return the set of devices which are in state {@link #STATE_CONNECTED} - * - * This is not specific to any application configuration but represents the connection - * state of the local Bluetooth adapter for this profile. This can be used - * by applications like status bar which would just like to know the state of the - * local adapter. - * - * @return List of devices. The list will be empty on error. - */ - @Override - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public List<BluetoothDevice> getConnectedDevices() { - Log.e(TAG, "getConnectedDevices(): BluetoothHealth is deprecated"); - return new ArrayList<>(); - } - - /** - * Get a list of devices that match any of the given connection - * states. - * - * <p> If none of the devices match any of the given states, - * an empty list will be returned. - * - * <p>This is not specific to any application configuration but represents the connection - * state of the local Bluetooth adapter for this profile. This can be used - * by applications like status bar which would just like to know the state of the - * local adapter. - * - * @param states Array of states. States can be one of {@link #STATE_CONNECTED}, {@link - * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, - * @return List of devices. The list will be empty on error. - */ - @Override - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SuppressLint("AndroidFrameworkRequiresPermission") - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - Log.e(TAG, "getDevicesMatchingConnectionStates(): BluetoothHealth is deprecated"); - return new ArrayList<>(); - } - - /** Health Channel Connection State - Disconnected - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int STATE_CHANNEL_DISCONNECTED = 0; - /** Health Channel Connection State - Connecting - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int STATE_CHANNEL_CONNECTING = 1; - /** Health Channel Connection State - Connected - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int STATE_CHANNEL_CONNECTED = 2; - /** Health Channel Connection State - Disconnecting - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int STATE_CHANNEL_DISCONNECTING = 3; - - /** Health App Configuration registration success - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0; - /** Health App Configuration registration failure - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int APP_CONFIG_REGISTRATION_FAILURE = 1; - /** Health App Configuration un-registration success - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; - /** Health App Configuration un-registration failure - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; -} diff --git a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java deleted file mode 100644 index 2f66df258b53..000000000000 --- a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package android.bluetooth; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * The Bluetooth Health Application Configuration that is used in conjunction with - * the {@link BluetoothHealth} class. This class represents an application configuration - * that the Bluetooth Health third party application will register to communicate with the - * remote Bluetooth health device. - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ -@Deprecated -public final class BluetoothHealthAppConfiguration implements Parcelable { - - /** - * Hide auto-created default constructor - * @hide - */ - BluetoothHealthAppConfiguration() {} - - @Override - public int describeContents() { - return 0; - } - - /** - * Return the data type associated with this application configuration. - * - * @return dataType - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public int getDataType() { - return 0; - } - - /** - * Return the name of the application configuration. - * - * @return String name - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public String getName() { - return null; - } - - /** - * Return the role associated with this application configuration. - * - * @return One of {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE} - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public int getRole() { - return 0; - } - - /** - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHealthAppConfiguration> CREATOR = - new Parcelable.Creator<BluetoothHealthAppConfiguration>() { - @Override - public BluetoothHealthAppConfiguration createFromParcel(Parcel in) { - return new BluetoothHealthAppConfiguration(); - } - - @Override - public BluetoothHealthAppConfiguration[] newArray(int size) { - return new BluetoothHealthAppConfiguration[size]; - } - }; - - @Override - public void writeToParcel(Parcel out, int flags) {} -} diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java deleted file mode 100644 index 4769212c5361..000000000000 --- a/core/java/android/bluetooth/BluetoothHealthCallback.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package android.bluetooth; - -import android.annotation.BinderThread; -import android.os.ParcelFileDescriptor; -import android.util.Log; - -/** - * This abstract class is used to implement {@link BluetoothHealth} callbacks. - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ -@Deprecated -public abstract class BluetoothHealthCallback { - private static final String TAG = "BluetoothHealthCallback"; - - /** - * Callback to inform change in registration state of the health - * application. - * <p> This callback is called on the binder thread (not on the UI thread) - * - * @param config Bluetooth Health app configuration - * @param status Success or failure of the registration or unregistration calls. Can be one of - * {@link BluetoothHealth#APP_CONFIG_REGISTRATION_SUCCESS} or {@link - * BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or - * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS} - * or {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE} - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @BinderThread - @Deprecated - public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, - int status) { - Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status); - } - - /** - * Callback to inform change in channel state. - * <p> Its the responsibility of the implementor of this callback to close the - * parcel file descriptor when done. This callback is called on the Binder - * thread (not the UI thread) - * - * @param config The Health app configutation - * @param device The Bluetooth Device - * @param prevState The previous state of the channel - * @param newState The new state of the channel. - * @param fd The Parcel File Descriptor when the channel state is connected. - * @param channelId The id associated with the channel. This id will be used in future calls - * like when disconnecting the channel. - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @BinderThread - @Deprecated - public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, - BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd, - int channelId) { - Log.d(TAG, "onHealthChannelStateChange: " + config + "Device: " + device - + "prevState:" + prevState + "newState:" + newState + "ParcelFd:" + fd - + "ChannelId:" + channelId); - } -} diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java deleted file mode 100644 index 339a75fe0fbe..000000000000 --- a/core/java/android/bluetooth/BluetoothHearingAid.java +++ /dev/null @@ -1,691 +0,0 @@ -/* - * Copyright 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 android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SystemApi; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.Context; -import android.os.Build; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This class provides the public APIs to control the Hearing Aid profile. - * - * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothHearingAid proxy object. - * - * <p> Android only supports one set of connected Bluetooth Hearing Aid device at a time. Each - * method is protected with its appropriate permission. - */ -public final class BluetoothHearingAid implements BluetoothProfile { - private static final String TAG = "BluetoothHearingAid"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** - * Intent used to broadcast the change in connection state of the Hearing Aid - * profile. Please note that in the binaural case, there will be two different LE devices for - * the left and right side and each device will have their own connection state changes.S - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED"; - - /** - * Intent used to broadcast the selection of a connected device as active. - * - * <p>This intent will have one extra: - * <ul> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can - * be null if no device is active. </li> - * </ul> - * - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_ACTIVE_DEVICE_CHANGED = - "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED"; - - /** - * This device represents Left Hearing Aid. - * - * @hide - */ - public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT; - - /** - * This device represents Right Hearing Aid. - * - * @hide - */ - public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT; - - /** - * This device is Monaural. - * - * @hide - */ - public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL; - - /** - * This device is Binaural (should receive only left or right audio). - * - * @hide - */ - public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL; - - /** - * Indicates the HiSyncID could not be read and is unavailable. - * - * @hide - */ - public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothHearingAid> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID, - "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) { - @Override - public IBluetoothHearingAid getServiceInterface(IBinder service) { - return IBluetoothHearingAid.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothHearingAid proxy object for interacting with the local - * Bluetooth Hearing Aid service. - */ - /* package */ BluetoothHearingAid(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - } - - /*package*/ void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothHearingAid getService() { - return mProfileConnector.getService(); - } - - /** - * Initiate connection to a profile of the remote bluetooth device. - * - * <p> This API returns false in scenarios like the profile on the - * device is already connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that - * connection state intent for the profile will be broadcasted with - * the state. Users can get the connection state of the profile - * from this intent. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean connect(BluetoothDevice device) { - if (DBG) log("connect(" + device + ")"); - final IBluetoothHearingAid service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate disconnection from a profile - * - * <p> This API will return false in scenarios like the profile on the - * Bluetooth device is not in connected state etc. When this API returns, - * true, it is guaranteed that the connection state change - * intent will be broadcasted with the state. Users can get the - * disconnection state of the profile from this intent. - * - * <p> If the disconnection is initiated by a remote device, the state - * will transition from {@link #STATE_CONNECTED} to - * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the - * host (local) device the state will transition from - * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to - * state {@link #STATE_DISCONNECTED}. The transition to - * {@link #STATE_DISCONNECTING} can be used to distinguish between the - * two scenarios. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean disconnect(BluetoothDevice device) { - if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHearingAid service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @NonNull List<BluetoothDevice> getConnectedDevices() { - if (VDBG) log("getConnectedDevices()"); - final IBluetoothHearingAid service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates( - @NonNull int[] states) { - if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHearingAid service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @BluetoothProfile.BtProfileState int getConnectionState( - @NonNull BluetoothDevice device) { - if (VDBG) log("getState(" + device + ")"); - final IBluetoothHearingAid service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Select a connected device as active. - * - * The active device selection is per profile. An active device's - * purpose is profile-specific. For example, Hearing Aid audio - * streaming is to the active Hearing Aid device. If a remote device - * is not connected, it cannot be selected as active. - * - * <p> This API returns false in scenarios like the profile on the - * device is not connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that the - * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted - * with the active device. - * - * @param device the remote Bluetooth device. Could be null to clear - * the active device and stop streaming audio to a Bluetooth device. - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public boolean setActiveDevice(@Nullable BluetoothDevice device) { - if (DBG) log("setActiveDevice(" + device + ")"); - final IBluetoothHearingAid service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && ((device == null) || isValidDevice(device))) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setActiveDevice(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the connected physical Hearing Aid devices that are active - * - * @return the list of active devices. The first element is the left active - * device; the second element is the right active device. If either or both side - * is not active, it will be null on that position. Returns empty list on error. - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @NonNull List<BluetoothDevice> getActiveDevices() { - if (VDBG) log("getActiveDevices()"); - final IBluetoothHearingAid service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getActiveDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - verifyDeviceNotNull(device, "setConnectionPolicy"); - final IBluetoothHearingAid service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the priority of the profile. - * - * <p> The priority can be any of: - * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - verifyDeviceNotNull(device, "getConnectionPolicy"); - final IBluetoothHearingAid service = getService(); - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Helper for converting a state to a string. - * - * For debug use only - strings are not internationalized. - * - * @hide - */ - public static String stateToString(int state) { - switch (state) { - case STATE_DISCONNECTED: - return "disconnected"; - case STATE_CONNECTING: - return "connecting"; - case STATE_CONNECTED: - return "connected"; - case STATE_DISCONNECTING: - return "disconnecting"; - default: - return "<unknown state " + state + ">"; - } - } - - /** - * Tells remote device to set an absolute volume. - * - * @param volume Absolute volume to be set on remote - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void setVolume(int volume) { - if (DBG) Log.d(TAG, "setVolume(" + volume + ")"); - final IBluetoothHearingAid service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver recv = new SynchronousResultReceiver(); - service.setVolume(volume, mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Get the HiSyncId (unique hearing aid device identifier) of the device. - * - * <a href=https://source.android.com/devices/bluetooth/asha#hisyncid>HiSyncId documentation - * can be found here</a> - * - * @param device Bluetooth device - * @return the HiSyncId of the device - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public long getHiSyncId(@NonNull BluetoothDevice device) { - if (VDBG) log("getHiSyncId(" + device + ")"); - verifyDeviceNotNull(device, "getConnectionPolicy"); - final IBluetoothHearingAid service = getService(); - final long defaultValue = HI_SYNC_ID_INVALID; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Long> recv = new SynchronousResultReceiver(); - service.getHiSyncId(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the side of the device. - * - * @param device Bluetooth device. - * @return SIDE_LEFT or SIDE_RIGHT - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getDeviceSide(BluetoothDevice device) { - if (VDBG) log("getDeviceSide(" + device + ")"); - final IBluetoothHearingAid service = getService(); - final int defaultValue = SIDE_LEFT; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getDeviceSide(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the mode of the device. - * - * @param device Bluetooth device - * @return MODE_MONAURAL or MODE_BINAURAL - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getDeviceMode(BluetoothDevice device) { - if (VDBG) log("getDeviceMode(" + device + ")"); - final IBluetoothHearingAid service = getService(); - final int defaultValue = MODE_MONAURAL; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getDeviceMode(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; - } - - private void verifyDeviceNotNull(BluetoothDevice device, String methodName) { - if (device == null) { - Log.e(TAG, methodName + ": device param is null"); - throw new IllegalArgumentException("Device cannot be null"); - } - } - - private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; - - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java deleted file mode 100644 index 44a355b5f75c..000000000000 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ /dev/null @@ -1,848 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SystemApi; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.content.AttributionSource; -import android.content.Context; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeoutException; - -/** - * Provides the public APIs to control the Bluetooth HID Device profile. - * - * <p>BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC. - * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object. - */ -public final class BluetoothHidDevice implements BluetoothProfile { - private static final String TAG = BluetoothHidDevice.class.getSimpleName(); - private static final boolean DBG = false; - - /** - * Intent used to broadcast the change in connection state of the Input Host profile. - * - * <p>This intent will have 3 extras: - * - * <ul> - * <li>{@link #EXTRA_STATE} - The current state of the profile. - * <li>{@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. - * <li>{@link BluetoothDevice#EXTRA_DEVICE} - The remote device. - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link - * #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link - * #STATE_DISCONNECTING}. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED"; - - /** - * Constant representing unspecified HID device subclass. - * - * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback) - */ - public static final byte SUBCLASS1_NONE = (byte) 0x00; - /** - * Constant representing keyboard subclass. - * - * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback) - */ - public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40; - /** - * Constant representing mouse subclass. - * - * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback) - */ - public static final byte SUBCLASS1_MOUSE = (byte) 0x80; - /** - * Constant representing combo keyboard and mouse subclass. - * - * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback) - */ - public static final byte SUBCLASS1_COMBO = (byte) 0xC0; - - /** - * Constant representing uncategorized HID device subclass. - * - * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback) - */ - public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00; - /** - * Constant representing joystick subclass. - * - * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback) - */ - public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01; - /** - * Constant representing gamepad subclass. - * - * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback) - */ - public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; - /** - * Constant representing remote control subclass. - * - * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback) - */ - public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; - /** - * Constant representing sensing device subclass. - * - * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback) - */ - public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; - /** - * Constant representing digitizer tablet subclass. - * - * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback) - */ - public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05; - /** - * Constant representing card reader subclass. - * - * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback) - */ - public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; - - /** - * Constant representing HID Input Report type. - * - * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) - * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) - * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) - */ - public static final byte REPORT_TYPE_INPUT = (byte) 1; - /** - * Constant representing HID Output Report type. - * - * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) - * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) - * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) - */ - public static final byte REPORT_TYPE_OUTPUT = (byte) 2; - /** - * Constant representing HID Feature Report type. - * - * @see Callback#onGetReport(BluetoothDevice, byte, byte, int) - * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) - * @see Callback#onInterruptData(BluetoothDevice, byte, byte[]) - */ - public static final byte REPORT_TYPE_FEATURE = (byte) 3; - - /** - * Constant representing success response for Set Report. - * - * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) - */ - public static final byte ERROR_RSP_SUCCESS = (byte) 0; - /** - * Constant representing error response for Set Report due to "not ready". - * - * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) - */ - public static final byte ERROR_RSP_NOT_READY = (byte) 1; - /** - * Constant representing error response for Set Report due to "invalid report ID". - * - * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) - */ - public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2; - /** - * Constant representing error response for Set Report due to "unsupported request". - * - * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) - */ - public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3; - /** - * Constant representing error response for Set Report due to "invalid parameter". - * - * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) - */ - public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4; - /** - * Constant representing error response for Set Report with unknown reason. - * - * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[]) - */ - public static final byte ERROR_RSP_UNKNOWN = (byte) 14; - - /** - * Constant representing boot protocol mode used set by host. Default is always {@link - * #PROTOCOL_REPORT_MODE} unless notified otherwise. - * - * @see Callback#onSetProtocol(BluetoothDevice, byte) - */ - public static final byte PROTOCOL_BOOT_MODE = (byte) 0; - /** - * Constant representing report protocol mode used set by host. Default is always {@link - * #PROTOCOL_REPORT_MODE} unless notified otherwise. - * - * @see Callback#onSetProtocol(BluetoothDevice, byte) - */ - public static final byte PROTOCOL_REPORT_MODE = (byte) 1; - - /** - * The template class that applications use to call callback functions on events from the HID - * host. Callback functions are wrapped in this class and registered to the Android system - * during app registration. - */ - public abstract static class Callback { - - private static final String TAG = "BluetoothHidDevCallback"; - - /** - * Callback called when application registration state changes. Usually it's called due to - * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[], - * Executor, Callback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also - * unsolicited in case e.g. Bluetooth was turned off in which case application is - * unregistered automatically. - * - * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently - * has Virtual Cable established with device. Only valid when application is registered, - * can be <code>null</code>. - * @param registered <code>true</code> if application is registered, <code>false</code> - * otherwise. - */ - public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { - Log.d( - TAG, - "onAppStatusChanged: pluggedDevice=" - + pluggedDevice - + " registered=" - + registered); - } - - /** - * Callback called when connection state with remote host was changed. Application can - * assume than Virtual Cable is established when called with {@link - * BluetoothProfile#STATE_CONNECTED} <code>state</code>. - * - * @param device {@link BluetoothDevice} object representing host device which connection - * state was changed. - * @param state Connection state as defined in {@link BluetoothProfile}. - */ - public void onConnectionStateChanged(BluetoothDevice device, int state) { - Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state); - } - - /** - * Callback called when GET_REPORT is received from remote host. Should be replied by - * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, - * byte[])}. - * - * @param type Requested Report Type. - * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor. - * @param bufferSize Requested buffer size, application shall respond with at least given - * number of bytes. - */ - public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { - Log.d( - TAG, - "onGetReport: device=" - + device - + " type=" - + type - + " id=" - + id - + " bufferSize=" - + bufferSize); - } - - /** - * Callback called when SET_REPORT is received from remote host. In case received data are - * invalid, application shall respond with {@link - * BluetoothHidDevice#reportError(BluetoothDevice, byte)}. - * - * @param type Report Type. - * @param id Report Id. - * @param data Report data. - */ - public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { - Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id); - } - - /** - * Callback called when SET_PROTOCOL is received from remote host. Application shall use - * this information to send only reports valid for given protocol mode. By default, {@link - * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. - * - * @param protocol Protocol Mode. - */ - public void onSetProtocol(BluetoothDevice device, byte protocol) { - Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol); - } - - /** - * Callback called when report data is received over interrupt channel. Report Type is - * assumed to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. - * - * @param reportId Report Id. - * @param data Report data. - */ - public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { - Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId); - } - - /** - * Callback called when Virtual Cable is removed. After this callback is received connection - * will be disconnected automatically. - */ - public void onVirtualCableUnplug(BluetoothDevice device) { - Log.d(TAG, "onVirtualCableUnplug: device=" + device); - } - } - - private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub { - - private final Executor mExecutor; - private final Callback mCallback; - private final AttributionSource mAttributionSource; - - CallbackWrapper(Executor executor, Callback callback, AttributionSource attributionSource) { - mExecutor = executor; - mCallback = callback; - mAttributionSource = attributionSource; - } - - @Override - public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) { - Attributable.setAttributionSource(pluggedDevice, mAttributionSource); - final long token = clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered)); - } finally { - restoreCallingIdentity(token); - } - } - - @Override - public void onConnectionStateChanged(BluetoothDevice device, int state) { - Attributable.setAttributionSource(device, mAttributionSource); - final long token = clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state)); - } finally { - restoreCallingIdentity(token); - } - } - - @Override - public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) { - Attributable.setAttributionSource(device, mAttributionSource); - final long token = clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize)); - } finally { - restoreCallingIdentity(token); - } - } - - @Override - public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) { - Attributable.setAttributionSource(device, mAttributionSource); - final long token = clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data)); - } finally { - restoreCallingIdentity(token); - } - } - - @Override - public void onSetProtocol(BluetoothDevice device, byte protocol) { - Attributable.setAttributionSource(device, mAttributionSource); - final long token = clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol)); - } finally { - restoreCallingIdentity(token); - } - } - - @Override - public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) { - Attributable.setAttributionSource(device, mAttributionSource); - final long token = clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data)); - } finally { - restoreCallingIdentity(token); - } - } - - @Override - public void onVirtualCableUnplug(BluetoothDevice device) { - Attributable.setAttributionSource(device, mAttributionSource); - final long token = clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device)); - } finally { - restoreCallingIdentity(token); - } - } - } - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothHidDevice> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE, - "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) { - @Override - public IBluetoothHidDevice getServiceInterface(IBinder service) { - return IBluetoothHidDevice.Stub.asInterface(service); - } - }; - - BluetoothHidDevice(Context context, ServiceListener listener, BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - } - - void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothHidDevice getService() { - return mProfileConnector.getService(); - } - - /** {@inheritDoc} */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getConnectedDevices() { - final IBluetoothHidDevice service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** {@inheritDoc} */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - final IBluetoothHidDevice service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** {@inheritDoc} */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(BluetoothDevice device) { - final IBluetoothHidDevice service = getService(); - final int defaultValue = STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Registers application to be used for HID device. Connections to HID Device are only possible - * when application is registered. Only one application can be registered at one time. When an - * application is registered, the HID Host service will be disabled until it is unregistered. - * When no longer used, application should be unregistered using {@link #unregisterApp()}. The - * app will be automatically unregistered if it is not foreground. The registration status - * should be tracked by the application by handling callback from Callback#onAppStatusChanged. - * The app registration status is not related to the return value of this method. - * - * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. The HID - * Device SDP record is required. - * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings. The - * Incoming QoS Settings is not required. Use null or default - * BluetoothHidDeviceAppQosSettings.Builder for default values. - * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. The - * Outgoing QoS Settings is not required. Use null or default - * BluetoothHidDeviceAppQosSettings.Builder for default values. - * @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 if the command is successfully sent; otherwise false. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean registerApp( - BluetoothHidDeviceAppSdpSettings sdp, - BluetoothHidDeviceAppQosSettings inQos, - BluetoothHidDeviceAppQosSettings outQos, - Executor executor, - Callback callback) { - boolean result = false; - - if (sdp == null) { - throw new IllegalArgumentException("sdp parameter cannot be null"); - } - - if (executor == null) { - throw new IllegalArgumentException("executor parameter cannot be null"); - } - - if (callback == null) { - throw new IllegalArgumentException("callback parameter cannot be null"); - } - - final IBluetoothHidDevice service = getService(); - final boolean defaultValue = result; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - CallbackWrapper cbw = new CallbackWrapper(executor, callback, mAttributionSource); - service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource, recv); - result = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Unregisters application. Active connection will be disconnected and no new connections will - * be allowed until registered again using {@link #registerApp - * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings, - * BluetoothHidDeviceAppQosSettings, Executor, Callback)}. The registration status should be - * tracked by the application by handling callback from Callback#onAppStatusChanged. The app - * registration status is not related to the return value of this method. - * - * @return true if the command is successfully sent; otherwise false. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean unregisterApp() { - final IBluetoothHidDevice service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.unregisterApp(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Sends report to remote host using interrupt channel. - * - * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id are not defined in - * descriptor. - * @param data Report data, not including Report Id. - * @return true if the command is successfully sent; otherwise false. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean sendReport(BluetoothDevice device, int id, byte[] data) { - final IBluetoothHidDevice service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.sendReport(device, id, data, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Sends report to remote host as reply for GET_REPORT request from {@link - * Callback#onGetReport(BluetoothDevice, byte, byte, int)}. - * - * @param type Report Type, as in request. - * @param id Report Id, as in request. - * @param data Report data, not including Report Id. - * @return true if the command is successfully sent; otherwise false. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { - final IBluetoothHidDevice service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.replyReport(device, type, id, data, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Sends error handshake message as reply for invalid SET_REPORT request from {@link - * Callback#onSetReport(BluetoothDevice, byte, byte, byte[])}. - * - * @param error Error to be sent for SET_REPORT via HANDSHAKE. - * @return true if the command is successfully sent; otherwise false. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean reportError(BluetoothDevice device, byte error) { - final IBluetoothHidDevice service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.reportError(device, error, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Gets the application name of the current HidDeviceService user. - * - * @return the current user name, or empty string if cannot get the name - * {@hide} - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public String getUserAppName() { - final IBluetoothHidDevice service = getService(); - final String defaultValue = ""; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<String> recv = new SynchronousResultReceiver(); - service.getUserAppName(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiates connection to host which is currently paired with this device. If the application - * is not registered, #connect(BluetoothDevice) will fail. The connection state should be - * tracked by the application by handling callback from Callback#onConnectionStateChanged. The - * connection state is not related to the return value of this method. - * - * @return true if the command is successfully sent; otherwise false. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean connect(BluetoothDevice device) { - final IBluetoothHidDevice service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Disconnects from currently connected host. The connection state should be tracked by the - * application by handling callback from Callback#onConnectionStateChanged. The connection state - * is not related to the return value of this method. - * - * @return true if the command is successfully sent; otherwise false. - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnect(BluetoothDevice device) { - final IBluetoothHidDevice service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Connects Hid Device if connectionPolicy is {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} - * and disconnects Hid device if connectionPolicy is - * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}. - * - * <p> The device should already be paired. - * Connection policy can be one of: - * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, - * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, - * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy determines whether hid device should be connected or disconnected - * @return true if hid device is connected or disconnected, false otherwise - * - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothHidDevice service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; - } - - private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; - - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; - } - - private static void log(String msg) { - if (DBG) { - Log.d(TAG, msg); - } - } -} diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java deleted file mode 100644 index b21ebe59d816..000000000000 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Represents the Quality of Service (QoS) settings for a Bluetooth HID Device application. - * - * <p>The BluetoothHidDevice framework will update the L2CAP QoS settings for the app during - * registration. - * - * <p>{@see BluetoothHidDevice} - */ -public final class BluetoothHidDeviceAppQosSettings implements Parcelable { - - private final int mServiceType; - private final int mTokenRate; - private final int mTokenBucketSize; - private final int mPeakBandwidth; - private final int mLatency; - private final int mDelayVariation; - - public static final int SERVICE_NO_TRAFFIC = 0x00; - public static final int SERVICE_BEST_EFFORT = 0x01; - public static final int SERVICE_GUARANTEED = 0x02; - - public static final int MAX = (int) 0xffffffff; - - /** - * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS - * Settings is optional. Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and - * Appendix D for parameters. - * - * @param serviceType L2CAP service type, default = SERVICE_BEST_EFFORT - * @param tokenRate L2CAP token rate, default = 0 - * @param tokenBucketSize L2CAP token bucket size, default = 0 - * @param peakBandwidth L2CAP peak bandwidth, default = 0 - * @param latency L2CAP latency, default = MAX - * @param delayVariation L2CAP delay variation, default = MAX - */ - public BluetoothHidDeviceAppQosSettings( - int serviceType, - int tokenRate, - int tokenBucketSize, - int peakBandwidth, - int latency, - int delayVariation) { - mServiceType = serviceType; - mTokenRate = tokenRate; - mTokenBucketSize = tokenBucketSize; - mPeakBandwidth = peakBandwidth; - mLatency = latency; - mDelayVariation = delayVariation; - } - - public int getServiceType() { - return mServiceType; - } - - public int getTokenRate() { - return mTokenRate; - } - - public int getTokenBucketSize() { - return mTokenBucketSize; - } - - public int getPeakBandwidth() { - return mPeakBandwidth; - } - - public int getLatency() { - return mLatency; - } - - public int getDelayVariation() { - return mDelayVariation; - } - - @Override - public int describeContents() { - return 0; - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHidDeviceAppQosSettings> CREATOR = - new Parcelable.Creator<BluetoothHidDeviceAppQosSettings>() { - - @Override - public BluetoothHidDeviceAppQosSettings createFromParcel(Parcel in) { - - return new BluetoothHidDeviceAppQosSettings( - in.readInt(), - in.readInt(), - in.readInt(), - in.readInt(), - in.readInt(), - in.readInt()); - } - - @Override - public BluetoothHidDeviceAppQosSettings[] newArray(int size) { - return new BluetoothHidDeviceAppQosSettings[size]; - } - }; - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mServiceType); - out.writeInt(mTokenRate); - out.writeInt(mTokenBucketSize); - out.writeInt(mPeakBandwidth); - out.writeInt(mLatency); - out.writeInt(mDelayVariation); - } -} diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java deleted file mode 100644 index 4e1a2aaedcf0..000000000000 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.os.Parcel; -import android.os.Parcelable; -import android.util.EventLog; - - -/** - * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth HID Device application. - * - * <p>The BluetoothHidDevice framework adds the SDP record during app registration, so that the - * Android device can be discovered as a Bluetooth HID Device. - * - * <p>{@see BluetoothHidDevice} - */ -public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { - - private static final int MAX_DESCRIPTOR_SIZE = 2048; - - private final String mName; - private final String mDescription; - private final String mProvider; - private final byte mSubclass; - private final byte[] mDescriptors; - - /** - * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record. - * - * @param name Name of this Bluetooth HID device. Maximum length is 50 bytes. - * @param description Description for this Bluetooth HID device. Maximum length is 50 bytes. - * @param provider Provider of this Bluetooth HID device. Maximum length is 50 bytes. - * @param subclass Subclass of this Bluetooth HID device. See <a - * href="www.usb.org/developers/hidpage/HID1_11.pdf"> - * www.usb.org/developers/hidpage/HID1_11.pdf Section 4.2</a> - * @param descriptors Descriptors of this Bluetooth HID device. See <a - * href="www.usb.org/developers/hidpage/HID1_11.pdf"> - * www.usb.org/developers/hidpage/HID1_11.pdf Chapter 6</a> Maximum length is 2048 bytes. - */ - public BluetoothHidDeviceAppSdpSettings( - String name, String description, String provider, byte subclass, byte[] descriptors) { - mName = name; - mDescription = description; - mProvider = provider; - mSubclass = subclass; - - if (descriptors == null || descriptors.length > MAX_DESCRIPTOR_SIZE) { - EventLog.writeEvent(0x534e4554, "119819889", -1, ""); - throw new IllegalArgumentException("descriptors must be not null and shorter than " - + MAX_DESCRIPTOR_SIZE); - } - mDescriptors = descriptors.clone(); - } - - public String getName() { - return mName; - } - - public String getDescription() { - return mDescription; - } - - public String getProvider() { - return mProvider; - } - - public byte getSubclass() { - return mSubclass; - } - - public byte[] getDescriptors() { - return mDescriptors; - } - - @Override - public int describeContents() { - return 0; - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothHidDeviceAppSdpSettings> CREATOR = - new Parcelable.Creator<BluetoothHidDeviceAppSdpSettings>() { - - @Override - public BluetoothHidDeviceAppSdpSettings createFromParcel(Parcel in) { - - return new BluetoothHidDeviceAppSdpSettings( - in.readString(), - in.readString(), - in.readString(), - in.readByte(), - in.createByteArray()); - } - - @Override - public BluetoothHidDeviceAppSdpSettings[] newArray(int size) { - return new BluetoothHidDeviceAppSdpSettings[size]; - } - }; - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(mName); - out.writeString(mDescription); - out.writeString(mProvider); - out.writeByte(mSubclass); - out.writeByteArray(mDescriptors); - } -} diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java deleted file mode 100644 index ecbeddf2b853..000000000000 --- a/core/java/android/bluetooth/BluetoothHidHost.java +++ /dev/null @@ -1,831 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.content.AttributionSource; -import android.content.Context; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - - -/** - * This class provides the public APIs to control the Bluetooth Input - * Device Profile. - * - * <p>BluetoothHidHost is a proxy object for controlling the Bluetooth - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothHidHost proxy object. - * - * <p>Each method is protected with its appropriate permission. - * - * @hide - */ -@SystemApi -public final class BluetoothHidHost implements BluetoothProfile { - private static final String TAG = "BluetoothHidHost"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** - * Intent used to broadcast the change in connection state of the Input - * Device profile. - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - */ - @SuppressLint("ActionValue") - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; - - /** - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_PROTOCOL_MODE_CHANGED = - "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED"; - - /** - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_HANDSHAKE = - "android.bluetooth.input.profile.action.HANDSHAKE"; - - /** - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_REPORT = - "android.bluetooth.input.profile.action.REPORT"; - - /** - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_VIRTUAL_UNPLUG_STATUS = - "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS"; - - /** - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_IDLE_TIME_CHANGED = - "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED"; - - /** - * Return codes for the connect and disconnect Bluez / Dbus calls. - * - * @hide - */ - public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000; - - /** - * @hide - */ - public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001; - - /** - * @hide - */ - public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002; - - /** - * @hide - */ - public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003; - - /** - * @hide - */ - public static final int INPUT_OPERATION_SUCCESS = 5004; - - /** - * @hide - */ - public static final int PROTOCOL_REPORT_MODE = 0; - - /** - * @hide - */ - public static final int PROTOCOL_BOOT_MODE = 1; - - /** - * @hide - */ - public static final int PROTOCOL_UNSUPPORTED_MODE = 255; - - /* int reportType, int reportType, int bufferSize */ - /** - * @hide - */ - public static final byte REPORT_TYPE_INPUT = 1; - - /** - * @hide - */ - public static final byte REPORT_TYPE_OUTPUT = 2; - - /** - * @hide - */ - public static final byte REPORT_TYPE_FEATURE = 3; - - /** - * @hide - */ - public static final int VIRTUAL_UNPLUG_STATUS_SUCCESS = 0; - - /** - * @hide - */ - public static final int VIRTUAL_UNPLUG_STATUS_FAIL = 1; - - /** - * @hide - */ - public static final String EXTRA_PROTOCOL_MODE = - "android.bluetooth.BluetoothHidHost.extra.PROTOCOL_MODE"; - - /** - * @hide - */ - public static final String EXTRA_REPORT_TYPE = - "android.bluetooth.BluetoothHidHost.extra.REPORT_TYPE"; - - /** - * @hide - */ - public static final String EXTRA_REPORT_ID = - "android.bluetooth.BluetoothHidHost.extra.REPORT_ID"; - - /** - * @hide - */ - public static final String EXTRA_REPORT_BUFFER_SIZE = - "android.bluetooth.BluetoothHidHost.extra.REPORT_BUFFER_SIZE"; - - /** - * @hide - */ - public static final String EXTRA_REPORT = "android.bluetooth.BluetoothHidHost.extra.REPORT"; - - /** - * @hide - */ - public static final String EXTRA_STATUS = "android.bluetooth.BluetoothHidHost.extra.STATUS"; - - /** - * @hide - */ - public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = - "android.bluetooth.BluetoothHidHost.extra.VIRTUAL_UNPLUG_STATUS"; - - /** - * @hide - */ - public static final String EXTRA_IDLE_TIME = - "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME"; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothHidHost> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST, - "BluetoothHidHost", IBluetoothHidHost.class.getName()) { - @Override - public IBluetoothHidHost getServiceInterface(IBinder service) { - return IBluetoothHidHost.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothHidHost proxy object for interacting with the local - * Bluetooth Service which handles the InputDevice profile - */ - /* package */ BluetoothHidHost(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - } - - /*package*/ void close() { - if (VDBG) log("close()"); - mProfileConnector.disconnect(); - } - - private IBluetoothHidHost getService() { - return mProfileConnector.getService(); - } - - /** - * Initiate connection to a profile of the remote bluetooth device. - * - * <p> The system supports connection to multiple input devices. - * - * <p> This API returns false in scenarios like the profile on the - * device is already connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that - * connection state intent for the profile will be broadcasted with - * the state. Users can get the connection state of the profile - * from this intent. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean connect(BluetoothDevice device) { - if (DBG) log("connect(" + device + ")"); - final IBluetoothHidHost service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate disconnection from a profile - * - * <p> This API will return false in scenarios like the profile on the - * Bluetooth device is not in connected state etc. When this API returns, - * true, it is guaranteed that the connection state change - * intent will be broadcasted with the state. Users can get the - * disconnection state of the profile from this intent. - * - * <p> If the disconnection is initiated by a remote device, the state - * will transition from {@link #STATE_CONNECTED} to - * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the - * host (local) device the state will transition from - * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to - * state {@link #STATE_DISCONNECTED}. The transition to - * {@link #STATE_DISCONNECTING} can be used to distinguish between the - * two scenarios. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean disconnect(BluetoothDevice device) { - if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHidHost service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - * - * @hide - */ - @SystemApi - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public @NonNull List<BluetoothDevice> getConnectedDevices() { - if (VDBG) log("getConnectedDevices()"); - final IBluetoothHidHost service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - * - * @hide - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHidHost service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - * - * @hide - */ - @SystemApi - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(@NonNull BluetoothDevice device) { - if (VDBG) log("getState(" + device + ")"); - if (device == null) { - throw new IllegalArgumentException("device must not be null"); - } - final IBluetoothHidHost service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - if (device == null) { - throw new IllegalArgumentException("device must not be null"); - } - final IBluetoothHidHost service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the priority of the profile. - * - * <p> The priority can be any of: - * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - if (device == null) { - throw new IllegalArgumentException("device must not be null"); - } - final IBluetoothHidHost service = getService(); - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - private boolean isEnabled() { - return mAdapter.getState() == BluetoothAdapter.STATE_ON; - } - - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - /** - * Initiate virtual unplug for a HID input device. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean virtualUnplug(BluetoothDevice device) { - if (DBG) log("virtualUnplug(" + device + ")"); - final IBluetoothHidHost service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.virtualUnplug(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Send Get_Protocol_Mode command to the connected HID input device. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean getProtocolMode(BluetoothDevice device) { - if (VDBG) log("getProtocolMode(" + device + ")"); - final IBluetoothHidHost service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.getProtocolMode(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Send Set_Protocol_Mode command to the connected HID input device. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { - if (DBG) log("setProtocolMode(" + device + ")"); - final IBluetoothHidHost service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setProtocolMode(device, protocolMode, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Send Get_Report command to the connected HID input device. - * - * @param device Remote Bluetooth Device - * @param reportType Report type - * @param reportId Report ID - * @param bufferSize Report receiving buffer size - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, - int bufferSize) { - if (VDBG) { - log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId - + "bufferSize=" + bufferSize); - } - final IBluetoothHidHost service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.getReport(device, reportType, reportId, bufferSize, mAttributionSource, - recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Send Set_Report command to the connected HID input device. - * - * @param device Remote Bluetooth Device - * @param reportType Report type - * @param report Report receiving buffer size - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setReport(BluetoothDevice device, byte reportType, String report) { - if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); - final IBluetoothHidHost service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setReport(device, reportType, report, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Send Send_Data command to the connected HID input device. - * - * @param device Remote Bluetooth Device - * @param report Report to send - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean sendData(BluetoothDevice device, String report) { - if (DBG) log("sendData(" + device + "), report=" + report); - final IBluetoothHidHost service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.sendData(device, report, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Send Get_Idle_Time command to the connected HID input device. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean getIdleTime(BluetoothDevice device) { - if (DBG) log("getIdletime(" + device + ")"); - final IBluetoothHidHost service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.getIdleTime(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Send Set_Idle_Time command to the connected HID input device. - * - * @param device Remote Bluetooth Device - * @param idleTime Idle time to be set on HID Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setIdleTime(BluetoothDevice device, byte idleTime) { - if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); - final IBluetoothHidHost service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setIdleTime(device, idleTime, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java deleted file mode 100644 index 95f9229f0446..000000000000 --- a/core/java/android/bluetooth/BluetoothInputStream.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2009 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.bluetooth; - -import android.annotation.SuppressLint; - -import java.io.IOException; -import java.io.InputStream; - -/** - * BluetoothInputStream. - * - * Used to write to a Bluetooth socket. - * - * @hide - */ -@SuppressLint("AndroidFrameworkBluetoothPermission") -/*package*/ final class BluetoothInputStream extends InputStream { - private BluetoothSocket mSocket; - - /*package*/ BluetoothInputStream(BluetoothSocket s) { - mSocket = s; - } - - /** - * Return number of bytes available before this stream will block. - */ - public int available() throws IOException { - return mSocket.available(); - } - - public void close() throws IOException { - mSocket.close(); - } - - /** - * Reads a single byte from this stream and returns it as an integer in the - * range from 0 to 255. Returns -1 if the end of the stream has been - * reached. Blocks until one byte has been read, the end of the source - * stream is detected or an exception is thrown. - * - * @return the byte read or -1 if the end of stream has been reached. - * @throws IOException if the stream is closed or another IOException occurs. - * @since Android 1.5 - */ - public int read() throws IOException { - byte[] b = new byte[1]; - int ret = mSocket.read(b, 0, 1); - if (ret == 1) { - return (int) b[0] & 0xff; - } else { - return -1; - } - } - - /** - * Reads at most {@code length} bytes from this stream and stores them in - * the byte array {@code b} starting at {@code offset}. - * - * @param b the byte array in which to store the bytes read. - * @param offset the initial position in {@code buffer} to store the bytes read from this - * stream. - * @param length the maximum number of bytes to store in {@code b}. - * @return the number of bytes actually read or -1 if the end of the stream has been reached. - * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code length < 0}, or if {@code - * offset + length} is greater than the length of {@code b}. - * @throws IOException if the stream is closed or another IOException occurs. - * @since Android 1.5 - */ - public int read(byte[] b, int offset, int length) throws IOException { - if (b == null) { - throw new NullPointerException("byte array is null"); - } - if ((offset | length) < 0 || length > b.length - offset) { - throw new ArrayIndexOutOfBoundsException("invalid offset or length"); - } - return mSocket.read(b, offset, length); - } -} diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java deleted file mode 100644 index 15db686b3be4..000000000000 --- a/core/java/android/bluetooth/BluetoothLeAudio.java +++ /dev/null @@ -1,829 +0,0 @@ -/* - * Copyright 2020 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 static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.content.AttributionSource; -import android.content.Context; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.CloseGuard; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This class provides the public APIs to control the LeAudio profile. - * - * <p>BluetoothLeAudio is a proxy object for controlling the Bluetooth LE Audio - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothLeAudio proxy object. - * - * <p> Android only supports one set of connected Bluetooth LeAudio device at a time. Each - * method is protected with its appropriate permission. - */ -public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { - private static final String TAG = "BluetoothLeAudio"; - private static final boolean DBG = false; - private static final boolean VDBG = false; - - private CloseGuard mCloseGuard; - - /** - * Intent used to broadcast the change in connection state of the LeAudio - * profile. Please note that in the binaural case, there will be two different LE devices for - * the left and right side and each device will have their own connection state changes. - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = - "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED"; - - /** - * Intent used to broadcast the selection of a connected device as active. - * - * <p>This intent will have one extra: - * <ul> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can - * be null if no device is active. </li> - * </ul> - * - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED = - "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED"; - - /** - * Intent used to broadcast group node status information. - * - * <p>This intent will have 3 extra: - * <ul> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can - * be null if no device is active. </li> - * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li> - * <li> {@link #EXTRA_LE_AUDIO_GROUP_NODE_STATUS} - Group node status. </li> - * </ul> - * - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED = - "android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED"; - - - /** - * Intent used to broadcast group status information. - * - * <p>This intent will have 4 extra: - * <ul> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can - * be null if no device is active. </li> - * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li> - * <li> {@link #EXTRA_LE_AUDIO_GROUP_STATUS} - Group status. </li> - * </ul> - * - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_LE_AUDIO_GROUP_STATUS_CHANGED = - "android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED"; - - /** - * Intent used to broadcast group audio configuration changed information. - * - * <p>This intent will have 5 extra: - * <ul> - * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li> - * <li> {@link #EXTRA_LE_AUDIO_DIRECTION} - Direction as bit mask. </li> - * <li> {@link #EXTRA_LE_AUDIO_SINK_LOCATION} - Sink location as per Bluetooth Assigned - * Numbers </li> - * <li> {@link #EXTRA_LE_AUDIO_SOURCE_LOCATION} - Source location as per Bluetooth Assigned - * Numbers </li> - * <li> {@link #EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS} - Available contexts for group as per - * Bluetooth Assigned Numbers </li> - * </ul> - * - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_LE_AUDIO_CONF_CHANGED = - "android.bluetooth.action.LE_AUDIO_CONF_CHANGED"; - - /** - * Indicates unspecified audio content. - * @hide - */ - public static final int CONTEXT_TYPE_UNSPECIFIED = 0x0001; - - /** - * Indicates conversation between humans as, for example, in telephony or video calls. - * @hide - */ - public static final int CONTEXT_TYPE_COMMUNICATION = 0x0002; - - /** - * Indicates media as, for example, in music, public radio, podcast or video soundtrack. - * @hide - */ - public static final int CONTEXT_TYPE_MEDIA = 0x0004; - - /** - * Indicates instructional audio as, for example, in navigation, traffic announcements - * or user guidance. - * @hide - */ - public static final int CONTEXT_TYPE_INSTRUCTIONAL = 0x0008; - - /** - * Indicates attention seeking audio as, for example, in beeps signalling arrival of a message - * or keyboard clicks. - * @hide - */ - public static final int CONTEXT_TYPE_ATTENTION_SEEKING = 0x0010; - - /** - * Indicates immediate alerts as, for example, in a low battery alarm, timer expiry or alarm - * clock. - * @hide - */ - public static final int CONTEXT_TYPE_IMMEDIATE_ALERT = 0x0020; - - /** - * Indicates man machine communication as, for example, with voice recognition or virtual - * assistant. - * @hide - */ - public static final int CONTEXT_TYPE_MAN_MACHINE = 0x0040; - - /** - * Indicates emergency alerts as, for example, with fire alarms or other urgent alerts. - * @hide - */ - public static final int CONTEXT_TYPE_EMERGENCY_ALERT = 0x0080; - - /** - * Indicates ringtone as in a call alert. - * @hide - */ - public static final int CONTEXT_TYPE_RINGTONE = 0x0100; - - /** - * Indicates audio associated with a television program and/or with metadata conforming to the - * Bluetooth Broadcast TV profile. - * @hide - */ - public static final int CONTEXT_TYPE_TV = 0x0200; - - /** - * Indicates audio associated with a low latency live audio stream. - * - * @hide - */ - public static final int CONTEXT_TYPE_LIVE = 0x0400; - - /** - * Indicates audio associated with a video game stream. - * @hide - */ - public static final int CONTEXT_TYPE_GAME = 0x0800; - - /** - * This represents an invalid group ID. - * - * @hide - */ - public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; - - /** - * Contains group id. - * @hide - */ - public static final String EXTRA_LE_AUDIO_GROUP_ID = - "android.bluetooth.extra.LE_AUDIO_GROUP_ID"; - - /** - * Contains group node status, can be any of - * <p> - * <ul> - * <li> {@link #GROUP_NODE_ADDED} </li> - * <li> {@link #GROUP_NODE_REMOVED} </li> - * </ul> - * <p> - * @hide - */ - public static final String EXTRA_LE_AUDIO_GROUP_NODE_STATUS = - "android.bluetooth.extra.LE_AUDIO_GROUP_NODE_STATUS"; - - /** - * Contains group status, can be any of - * - * <p> - * <ul> - * <li> {@link #GROUP_STATUS_ACTIVE} </li> - * <li> {@link #GROUP_STATUS_INACTIVE} </li> - * </ul> - * <p> - * @hide - */ - public static final String EXTRA_LE_AUDIO_GROUP_STATUS = - "android.bluetooth.extra.LE_AUDIO_GROUP_STATUS"; - - /** - * Contains bit mask for direction, bit 0 set when Sink, bit 1 set when Source. - * @hide - */ - public static final String EXTRA_LE_AUDIO_DIRECTION = - "android.bluetooth.extra.LE_AUDIO_DIRECTION"; - - /** - * Contains source location as per Bluetooth Assigned Numbers - * @hide - */ - public static final String EXTRA_LE_AUDIO_SOURCE_LOCATION = - "android.bluetooth.extra.LE_AUDIO_SOURCE_LOCATION"; - - /** - * Contains sink location as per Bluetooth Assigned Numbers - * @hide - */ - public static final String EXTRA_LE_AUDIO_SINK_LOCATION = - "android.bluetooth.extra.LE_AUDIO_SINK_LOCATION"; - - /** - * Contains available context types for group as per Bluetooth Assigned Numbers - * @hide - */ - public static final String EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS = - "android.bluetooth.extra.LE_AUDIO_AVAILABLE_CONTEXTS"; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - /** - * Indicating that group is Active ( Audio device is available ) - * @hide - */ - public static final int GROUP_STATUS_ACTIVE = IBluetoothLeAudio.GROUP_STATUS_ACTIVE; - - /** - * Indicating that group is Inactive ( Audio device is not available ) - * @hide - */ - public static final int GROUP_STATUS_INACTIVE = IBluetoothLeAudio.GROUP_STATUS_INACTIVE; - - /** - * Indicating that node has been added to the group. - * @hide - */ - public static final int GROUP_NODE_ADDED = IBluetoothLeAudio.GROUP_NODE_ADDED; - - /** - * Indicating that node has been removed from the group. - * @hide - */ - public static final int GROUP_NODE_REMOVED = IBluetoothLeAudio.GROUP_NODE_REMOVED; - - private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio", - IBluetoothLeAudio.class.getName()) { - @Override - public IBluetoothLeAudio getServiceInterface(IBinder service) { - return IBluetoothLeAudio.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothLeAudio proxy object for interacting with the local - * Bluetooth LeAudio service. - */ - /* package */ BluetoothLeAudio(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - mCloseGuard = new CloseGuard(); - mCloseGuard.open("close"); - } - - /** - * @hide - */ - public void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothLeAudio getService() { - return mProfileConnector.getService(); - } - - protected void finalize() { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } - close(); - } - - /** - * Initiate connection to a profile of the remote bluetooth device. - * - * <p> This API returns false in scenarios like the profile on the - * device is already connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that - * connection state intent for the profile will be broadcasted with - * the state. Users can get the connection state of the profile - * from this intent. - * - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean connect(@Nullable BluetoothDevice device) { - if (DBG) log("connect(" + device + ")"); - final IBluetoothLeAudio service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate disconnection from a profile - * - * <p> This API will return false in scenarios like the profile on the - * Bluetooth device is not in connected state etc. When this API returns, - * true, it is guaranteed that the connection state change - * intent will be broadcasted with the state. Users can get the - * disconnection state of the profile from this intent. - * - * <p> If the disconnection is initiated by a remote device, the state - * will transition from {@link #STATE_CONNECTED} to - * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the - * host (local) device the state will transition from - * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to - * state {@link #STATE_DISCONNECTED}. The transition to - * {@link #STATE_DISCONNECTING} can be used to distinguish between the - * two scenarios. - * - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnect(@Nullable BluetoothDevice device) { - if (DBG) log("disconnect(" + device + ")"); - final IBluetoothLeAudio service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @NonNull List<BluetoothDevice> getConnectedDevices() { - if (VDBG) log("getConnectedDevices()"); - final IBluetoothLeAudio service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates( - @NonNull int[] states) { - if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothLeAudio service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - */ - @Override - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { - if (VDBG) log("getState(" + device + ")"); - final IBluetoothLeAudio service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Select a connected device as active. - * - * The active device selection is per profile. An active device's - * purpose is profile-specific. For example, LeAudio audio - * streaming is to the active LeAudio device. If a remote device - * is not connected, it cannot be selected as active. - * - * <p> This API returns false in scenarios like the profile on the - * device is not connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that the - * {@link #ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED} intent will be broadcasted - * with the active device. - * - * - * @param device the remote Bluetooth device. Could be null to clear - * the active device and stop streaming audio to a Bluetooth device. - * @return false on immediate error, true otherwise - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean setActiveDevice(@Nullable BluetoothDevice device) { - if (DBG) log("setActiveDevice(" + device + ")"); - final IBluetoothLeAudio service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled() && ((device == null) || isValidDevice(device))) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setActiveDevice(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the connected LeAudio devices that are active - * - * @return the list of active devices. Returns empty list on error. - * @hide - */ - @NonNull - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getActiveDevices() { - if (VDBG) log("getActiveDevice()"); - final IBluetoothLeAudio service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getActiveDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get device group id. Devices with same group id belong to same group (i.e left and right - * earbud) - * @param device LE Audio capable device - * @return group id that this device currently belongs to - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getGroupId(@NonNull BluetoothDevice device) { - if (VDBG) log("getGroupId()"); - final IBluetoothLeAudio service = getService(); - final int defaultValue = GROUP_ID_INVALID; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled()) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getGroupId(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set volume for the streaming devices - * - * @param volume volume to set - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) - public void setVolume(int volume) { - if (VDBG) log("setVolume(vol: " + volume + " )"); - final IBluetoothLeAudio service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled()) { - try { - final SynchronousResultReceiver recv = new SynchronousResultReceiver(); - service.setVolume(volume, mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Add device to the given group. - * @param group_id group ID the device is being added to - * @param device the active device - * @return true on success, otherwise false - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED - }) - public boolean groupAddNode(int group_id, @NonNull BluetoothDevice device) { - if (VDBG) log("groupAddNode()"); - final IBluetoothLeAudio service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.groupAddNode(group_id, device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Remove device from a given group. - * @param group_id group ID the device is being removed from - * @param device the active device - * @return true on success, otherwise false - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED - }) - public boolean groupRemoveNode(int group_id, @NonNull BluetoothDevice device) { - if (VDBG) log("groupRemoveNode()"); - final IBluetoothLeAudio service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.groupRemoveNode(group_id, device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothLeAudio service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - final IBluetoothLeAudio service = getService(); - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (mAdapter.isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - - /** - * Helper for converting a state to a string. - * - * For debug use only - strings are not internationalized. - * - * @hide - */ - public static String stateToString(int state) { - switch (state) { - case STATE_DISCONNECTED: - return "disconnected"; - case STATE_CONNECTING: - return "connecting"; - case STATE_CONNECTED: - return "connected"; - case STATE_DISCONNECTING: - return "disconnecting"; - default: - return "<unknown state " + state + ">"; - } - } - - private boolean isValidDevice(@Nullable BluetoothDevice device) { - if (device == null) return false; - - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java b/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java deleted file mode 100644 index dcaf4b682f44..000000000000 --- a/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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.bluetooth; - -import android.annotation.IntDef; -import android.annotation.NonNull; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Represents the codec configuration for a Bluetooth LE Audio source device. - * <p>Contains the source codec type. - * <p>The source codec type values are the same as those supported by the - * device hardware. - * - * {@see BluetoothLeAudioCodecConfig} - */ -public final class BluetoothLeAudioCodecConfig { - // Add an entry for each source codec here. - - /** @hide */ - @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = { - SOURCE_CODEC_TYPE_LC3, - SOURCE_CODEC_TYPE_INVALID - }) - @Retention(RetentionPolicy.SOURCE) - public @interface SourceCodecType {}; - - public static final int SOURCE_CODEC_TYPE_LC3 = 0; - public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; - - /** - * Represents the count of valid source codec types. Can be accessed via - * {@link #getMaxCodecType}. - */ - private static final int SOURCE_CODEC_TYPE_MAX = 1; - - private final @SourceCodecType int mCodecType; - - /** - * Creates a new BluetoothLeAudioCodecConfig. - * - * @param codecType the source codec type - */ - private BluetoothLeAudioCodecConfig(@SourceCodecType int codecType) { - mCodecType = codecType; - } - - @Override - public String toString() { - return "{codecName:" + getCodecName() + "}"; - } - - /** - * Gets the codec type. - * - * @return the codec type - */ - public @SourceCodecType int getCodecType() { - return mCodecType; - } - - /** - * Returns the valid codec types count. - */ - public static int getMaxCodecType() { - return SOURCE_CODEC_TYPE_MAX; - } - - /** - * Gets the codec name. - * - * @return the codec name - */ - public @NonNull String getCodecName() { - switch (mCodecType) { - case SOURCE_CODEC_TYPE_LC3: - return "LC3"; - case SOURCE_CODEC_TYPE_INVALID: - return "INVALID CODEC"; - default: - break; - } - return "UNKNOWN CODEC(" + mCodecType + ")"; - } - - /** - * Builder for {@link BluetoothLeAudioCodecConfig}. - * <p> By default, the codec type will be set to - * {@link BluetoothLeAudioCodecConfig#SOURCE_CODEC_TYPE_INVALID} - */ - public static final class Builder { - private int mCodecType = BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID; - - /** - * Set codec type for Bluetooth codec config. - * - * @param codecType of this codec - * @return the same Builder instance - */ - public @NonNull Builder setCodecType(@SourceCodecType int codecType) { - mCodecType = codecType; - return this; - } - - /** - * Build {@link BluetoothLeAudioCodecConfig}. - * @return new BluetoothLeAudioCodecConfig built - */ - public @NonNull BluetoothLeAudioCodecConfig build() { - return new BluetoothLeAudioCodecConfig(mCodecType); - } - } -} diff --git a/core/java/android/bluetooth/BluetoothLeBroadcast.java b/core/java/android/bluetooth/BluetoothLeBroadcast.java deleted file mode 100644 index fed9f911d5b3..000000000000 --- a/core/java/android/bluetooth/BluetoothLeBroadcast.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * 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.bluetooth; - -import android.annotation.IntDef; -import android.content.Context; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.List; - -/** - * This class provides the public APIs to control the Bluetooth LE Broadcast Source profile. - * - * <p>BluetoothLeBroadcast is a proxy object for controlling the Bluetooth LE Broadcast - * Source Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} - * to get the BluetoothLeBroadcast proxy object. - * - * @hide - */ -public final class BluetoothLeBroadcast implements BluetoothProfile { - private static final String TAG = "BluetoothLeBroadcast"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** - * Constants used by the LE Audio Broadcast profile for the Broadcast state - * - * @hide - */ - @IntDef(prefix = {"LE_AUDIO_BROADCAST_STATE_"}, value = { - LE_AUDIO_BROADCAST_STATE_DISABLED, - LE_AUDIO_BROADCAST_STATE_ENABLING, - LE_AUDIO_BROADCAST_STATE_ENABLED, - LE_AUDIO_BROADCAST_STATE_DISABLING, - LE_AUDIO_BROADCAST_STATE_PLAYING, - LE_AUDIO_BROADCAST_STATE_NOT_PLAYING - }) - @Retention(RetentionPolicy.SOURCE) - public @interface LeAudioBroadcastState {} - - /** - * Indicates that LE Audio Broadcast mode is currently disabled - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_STATE_DISABLED = 10; - - /** - * Indicates that LE Audio Broadcast mode is being enabled - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_STATE_ENABLING = 11; - - /** - * Indicates that LE Audio Broadcast mode is currently enabled - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_STATE_ENABLED = 12; - /** - * Indicates that LE Audio Broadcast mode is being disabled - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_STATE_DISABLING = 13; - - /** - * Indicates that an LE Audio Broadcast mode is currently playing - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_STATE_PLAYING = 14; - - /** - * Indicates that LE Audio Broadcast is currently not playing - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_STATE_NOT_PLAYING = 15; - - /** - * Constants used by the LE Audio Broadcast profile for encryption key length - * - * @hide - */ - @IntDef(prefix = {"LE_AUDIO_BROADCAST_ENCRYPTION_KEY_"}, value = { - LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT, - LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT - }) - @Retention(RetentionPolicy.SOURCE) - public @interface LeAudioEncryptionKeyLength {} - - /** - * Indicates that the LE Audio Broadcast encryption key size is 32 bits. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT = 16; - - /** - * Indicates that the LE Audio Broadcast encryption key size is 128 bits. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT = 17; - - /** - * Interface for receiving events related to broadcasts - */ - public interface Callback { - /** - * Called when broadcast state has changed - * - * @param prevState broadcast state before the change - * @param newState broadcast state after the change - */ - @LeAudioBroadcastState - void onBroadcastStateChange(int prevState, int newState); - /** - * Called when encryption key has been updated - * - * @param success true if the key was updated successfully, false otherwise - */ - void onEncryptionKeySet(boolean success); - } - - /** - * Create a BluetoothLeBroadcast proxy object for interacting with the local - * LE Audio Broadcast Source service. - * - * @hide - */ - /*package*/ BluetoothLeBroadcast(Context context, - BluetoothProfile.ServiceListener listener) { - } - - /** - * Not supported since LE Audio Broadcasts do not establish a connection - * - * @throws UnsupportedOperationException - * - * @hide - */ - @Override - public int getConnectionState(BluetoothDevice device) { - throw new UnsupportedOperationException( - "LE Audio Broadcasts are not connection-oriented."); - } - - /** - * Not supported since LE Audio Broadcasts do not establish a connection - * - * @throws UnsupportedOperationException - * - * @hide - */ - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - throw new UnsupportedOperationException( - "LE Audio Broadcasts are not connection-oriented."); - } - - /** - * Not supported since LE Audio Broadcasts do not establish a connection - * - * @throws UnsupportedOperationException - * - * @hide - */ - @Override - public List<BluetoothDevice> getConnectedDevices() { - throw new UnsupportedOperationException( - "LE Audio Broadcasts are not connection-oriented."); - } - - /** - * Enable LE Audio Broadcast mode. - * - * Generates a new broadcast ID and enables sending of encrypted or unencrypted - * isochronous PDUs - * - * @hide - */ - public int enableBroadcastMode() { - if (DBG) log("enableBroadcastMode"); - return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED; - } - - /** - * Disable LE Audio Broadcast mode. - * - * @hide - */ - public int disableBroadcastMode() { - if (DBG) log("disableBroadcastMode"); - return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED; - } - - /** - * Get the current LE Audio broadcast state - * - * @hide - */ - @LeAudioBroadcastState - public int getBroadcastState() { - if (DBG) log("getBroadcastState"); - return LE_AUDIO_BROADCAST_STATE_DISABLED; - } - - /** - * Enable LE Audio broadcast encryption - * - * @param keyLength if useExisting is true, this specifies the length of the key that should - * be generated - * @param useExisting true, if an existing key should be used - * false, if a new key should be generated - * - * @hide - */ - @LeAudioEncryptionKeyLength - public int enableEncryption(boolean useExisting, int keyLength) { - if (DBG) log("enableEncryption useExisting=" + useExisting + " keyLength=" + keyLength); - return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED; - } - - /** - * Disable LE Audio broadcast encryption - * - * @param removeExisting true, if the existing key should be removed - * false, otherwise - * - * @hide - */ - public int disableEncryption(boolean removeExisting) { - if (DBG) log("disableEncryption removeExisting=" + removeExisting); - return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED; - } - - /** - * Enable or disable LE Audio broadcast encryption - * - * @param key use the provided key if non-null, generate a new key if null - * @param keyLength 0 if encryption is disabled, 4 bytes (low security), - * 16 bytes (high security) - * - * @hide - */ - @LeAudioEncryptionKeyLength - public int setEncryptionKey(byte[] key, int keyLength) { - if (DBG) log("setEncryptionKey key=" + key + " keyLength=" + keyLength); - return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED; - } - - - /** - * Get the encryption key that was set before - * - * @return encryption key as a byte array or null if no encryption key was set - * - * @hide - */ - public byte[] getEncryptionKey() { - if (DBG) log("getEncryptionKey"); - return null; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java b/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java deleted file mode 100644 index b866cce22470..000000000000 --- a/core/java/android/bluetooth/BluetoothLeBroadcastAssistantCallback.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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.bluetooth; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.bluetooth.le.ScanResult; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * This class provides a set of callbacks that are invoked when scanning for Broadcast Sources is - * offloaded to a Broadcast Assistant. - * - * <p>An LE Audio Broadcast Assistant can help a Broadcast Sink to scan for available Broadcast - * Sources. The Broadcast Sink achieves this by offloading the scan to a Broadcast Assistant. This - * is facilitated by the Broadcast Audio Scan Service (BASS). A BASS server is a GATT server that is - * part of the Scan Delegator on a Broadcast Sink. A BASS client instead runs on the Broadcast - * Assistant. - * - * <p>Once a GATT connection is established between the BASS client and the BASS server, the - * Broadcast Sink can offload the scans to the Broadcast Assistant. Upon finding new Broadcast - * Sources, the Broadcast Assistant then notifies the Broadcast Sink about these over the - * established GATT connection. The Scan Delegator on the Broadcast Sink can also notify the - * Assistant about changes such as addition and removal of Broadcast Sources. - * - * @hide - */ -public abstract class BluetoothLeBroadcastAssistantCallback { - - /** - * Broadcast Audio Scan Service (BASS) codes returned by a BASS Server - * - * @hide - */ - @IntDef( - prefix = "BASS_STATUS_", - value = { - BASS_STATUS_SUCCESS, - BASS_STATUS_FAILURE, - BASS_STATUS_INVALID_GATT_HANDLE, - BASS_STATUS_TXN_TIMEOUT, - BASS_STATUS_INVALID_SOURCE_ID, - BASS_STATUS_COLOCATED_SRC_UNAVAILABLE, - BASS_STATUS_INVALID_SOURCE_SELECTED, - BASS_STATUS_SOURCE_UNAVAILABLE, - BASS_STATUS_DUPLICATE_ADDITION, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface BassStatus {} - - public static final int BASS_STATUS_SUCCESS = 0x00; - public static final int BASS_STATUS_FAILURE = 0x01; - public static final int BASS_STATUS_INVALID_GATT_HANDLE = 0x02; - public static final int BASS_STATUS_TXN_TIMEOUT = 0x03; - - public static final int BASS_STATUS_INVALID_SOURCE_ID = 0x04; - public static final int BASS_STATUS_COLOCATED_SRC_UNAVAILABLE = 0x05; - public static final int BASS_STATUS_INVALID_SOURCE_SELECTED = 0x06; - public static final int BASS_STATUS_SOURCE_UNAVAILABLE = 0x07; - public static final int BASS_STATUS_DUPLICATE_ADDITION = 0x08; - public static final int BASS_STATUS_NO_EMPTY_SLOT = 0x09; - public static final int BASS_STATUS_INVALID_GROUP_OP = 0x10; - - /** - * Callback invoked when a new LE Audio Broadcast Source is found. - * - * @param result {@link ScanResult} scan result representing a Broadcast Source - */ - public void onBluetoothLeBroadcastSourceFound(@NonNull ScanResult result) {} - - /** - * Callback invoked when the Broadcast Assistant synchronizes with Periodic Advertisements (PAs) - * of an LE Audio Broadcast Source. - * - * @param source the selected Broadcast Source - */ - public void onBluetoothLeBroadcastSourceSelected( - @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {} - - /** - * Callback invoked when the Broadcast Assistant loses synchronization with an LE Audio - * Broadcast Source. - * - * @param source the Broadcast Source with which synchronization was lost - */ - public void onBluetoothLeBroadcastSourceLost( - @NonNull BluetoothLeBroadcastSourceInfo source, @BassStatus int status) {} - - /** - * Callback invoked when a new LE Audio Broadcast Source has been successfully added to the Scan - * Delegator (within a Broadcast Sink, for example). - * - * @param sink Scan Delegator device on which a new Broadcast Source has been added - * @param source the added Broadcast Source - */ - public void onBluetoothLeBroadcastSourceAdded( - @NonNull BluetoothDevice sink, - @NonNull BluetoothLeBroadcastSourceInfo source, - @BassStatus int status) {} - - /** - * Callback invoked when an existing LE Audio Broadcast Source within a remote Scan Delegator - * has been updated. - * - * @param sink Scan Delegator device on which a Broadcast Source has been updated - * @param source the updated Broadcast Source - */ - public void onBluetoothLeBroadcastSourceUpdated( - @NonNull BluetoothDevice sink, - @NonNull BluetoothLeBroadcastSourceInfo source, - @BassStatus int status) {} - - /** - * Callback invoked when an LE Audio Broadcast Source has been successfully removed from the - * Scan Delegator (within a Broadcast Sink, for example). - * - * @param sink Scan Delegator device from which a Broadcast Source has been removed - * @param source the removed Broadcast Source - */ - public void onBluetoothLeBroadcastSourceRemoved( - @NonNull BluetoothDevice sink, - @NonNull BluetoothLeBroadcastSourceInfo source, - @BassStatus int status) {} -} diff --git a/core/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java b/core/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java deleted file mode 100644 index cb47280acc7e..000000000000 --- a/core/java/android/bluetooth/BluetoothLeBroadcastSourceInfo.java +++ /dev/null @@ -1,788 +0,0 @@ -/* - * 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.bluetooth; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * This class represents an LE Audio Broadcast Source and the associated information that is needed - * by Broadcast Audio Scan Service (BASS) residing on a Scan Delegator. - * - * <p>For example, the Scan Delegator on an LE Audio Broadcast Sink can use the information - * contained within an instance of this class to synchronize with an LE Audio Broadcast Source in - * order to listen to a Broadcast Audio Stream. - * - * <p>BroadcastAssistant has a BASS client which facilitates scanning and discovery of Broadcast - * Sources on behalf of say a Broadcast Sink. Upon successful discovery of one or more Broadcast - * sources, this information needs to be communicated to the BASS Server residing within the Scan - * Delegator on a Broadcast Sink. This is achieved using the Periodic Advertising Synchronization - * Transfer (PAST) procedure. This procedure uses information contained within an instance of this - * class. - * - * @hide - */ -public final class BluetoothLeBroadcastSourceInfo implements Parcelable { - private static final String TAG = "BluetoothLeBroadcastSourceInfo"; - private static final boolean DBG = true; - - /** - * Constants representing Broadcast Source address types - * - * @hide - */ - @IntDef( - prefix = "LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_", - value = { - LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_PUBLIC, - LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_RANDOM, - LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID - }) - @Retention(RetentionPolicy.SOURCE) - public @interface LeAudioBroadcastSourceAddressType {} - - /** - * Represents a public address used by an LE Audio Broadcast Source - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_PUBLIC = 0; - - /** - * Represents a random address used by an LE Audio Broadcast Source - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_RANDOM = 1; - - /** - * Represents an invalid address used by an LE Audio Broadcast Seurce - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID = 0xFFFF; - - /** - * Periodic Advertising Synchronization state - * - * <p>Periodic Advertising (PA) enables the LE Audio Broadcast Assistant to discover broadcast - * audio streams as well as the audio stream configuration on behalf of an LE Audio Broadcast - * Sink. This information can then be transferred to the LE Audio Broadcast Sink using the - * Periodic Advertising Synchronizaton Transfer (PAST) procedure. - * - * @hide - */ - @IntDef( - prefix = "LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_", - value = { - LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IDLE, - LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNCINFO_REQ, - LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IN_SYNC, - LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNC_FAIL, - LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_NO_PAST - }) - @Retention(RetentionPolicy.SOURCE) - public @interface LeAudioBroadcastSinkPaSyncState {} - - /** - * Indicates that the Broadcast Sink is not synchronized with the Periodic Advertisements (PA) - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IDLE = 0; - - /** - * Indicates that the Broadcast Sink requested the Broadcast Assistant to synchronize with the - * Periodic Advertisements (PA). - * - * <p>This is also known as scan delegation or scan offloading. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNCINFO_REQ = 1; - - /** - * Indicates that the Broadcast Sink is synchronized with the Periodic Advertisements (PA). - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_IN_SYNC = 2; - - /** - * Indicates that the Broadcast Sink was unable to synchronize with the Periodic Advertisements - * (PA). - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_SYNC_FAIL = 3; - - /** - * Indicates that the Broadcast Sink should be synchronized with the Periodic Advertisements - * (PA) using the Periodic Advertisements Synchronization Transfert (PAST) procedure. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_NO_PAST = 4; - - /** - * Indicates that the Broadcast Sink synchornization state is invalid. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID = 0xFFFF; - - /** @hide */ - @IntDef( - prefix = "LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_", - value = { - LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED, - LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_SYNCHRONIZED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface LeAudioBroadcastSinkAudioSyncState {} - - /** - * Indicates that the Broadcast Sink is not synchronized with a Broadcast Audio Stream. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED = 0; - - /** - * Indicates that the Broadcast Sink is synchronized with a Broadcast Audio Stream. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_SYNCHRONIZED = 1; - - /** - * Indicates that the Broadcast Sink audio synchronization state is invalid. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID = 0xFFFF; - - /** @hide */ - @IntDef( - prefix = "LE_AUDIO_BROADCAST_SINK_ENC_STATE_", - value = { - LE_AUDIO_BROADCAST_SINK_ENC_STATE_NOT_ENCRYPTED, - LE_AUDIO_BROADCAST_SINK_ENC_STATE_CODE_REQUIRED, - LE_AUDIO_BROADCAST_SINK_ENC_STATE_DECRYPTING, - LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE - }) - @Retention(RetentionPolicy.SOURCE) - public @interface LeAudioBroadcastSinkEncryptionState {} - - /** - * Indicates that the Broadcast Sink is synchronized with an unencrypted audio stream. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_NOT_ENCRYPTED = 0; - - /** - * Indicates that the Broadcast Sink needs a Broadcast Code to synchronize with the audio - * stream. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_CODE_REQUIRED = 1; - - /** - * Indicates that the Broadcast Sink is synchronized with an encrypted audio stream. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_DECRYPTING = 2; - - /** - * Indicates that the Broadcast Sink is unable to decrypt an audio stream due to an incorrect - * Broadcast Code - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE = 3; - - /** - * Indicates that the Broadcast Sink encryption state is invalid. - * - * @hide - */ - public static final int LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID = 0xFF; - - /** - * Represents an invalid LE Audio Broadcast Source ID - * - * @hide - */ - public static final byte LE_AUDIO_BROADCAST_SINK_INVALID_SOURCE_ID = (byte) 0x00; - - /** - * Represents an invalid Broadcast ID of a Broadcast Source - * - * @hide - */ - public static final int INVALID_BROADCAST_ID = 0xFFFFFF; - - private byte mSourceId; - private @LeAudioBroadcastSourceAddressType int mSourceAddressType; - private BluetoothDevice mSourceDevice; - private byte mSourceAdvSid; - private int mBroadcastId; - private @LeAudioBroadcastSinkPaSyncState int mPaSyncState; - private @LeAudioBroadcastSinkEncryptionState int mEncryptionStatus; - private @LeAudioBroadcastSinkAudioSyncState int mAudioSyncState; - private byte[] mBadBroadcastCode; - private byte mNumSubGroups; - private Map<Integer, Integer> mSubgroupBisSyncState = new HashMap<Integer, Integer>(); - private Map<Integer, byte[]> mSubgroupMetadata = new HashMap<Integer, byte[]>(); - - private String mBroadcastCode; - private static final int BIS_NO_PREF = 0xFFFFFFFF; - private static final int BROADCAST_CODE_SIZE = 16; - - /** - * Constructor to create an Empty object of {@link BluetoothLeBroadcastSourceInfo } with the - * given Source Id. - * - * <p>This is mainly used to represent the Empty Broadcast Source entries - * - * @param sourceId Source Id for this Broadcast Source info object - * @hide - */ - public BluetoothLeBroadcastSourceInfo(byte sourceId) { - mSourceId = sourceId; - mSourceAddressType = LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID; - mSourceDevice = null; - mSourceAdvSid = (byte) 0x00; - mBroadcastId = INVALID_BROADCAST_ID; - mPaSyncState = LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID; - mAudioSyncState = LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID; - mEncryptionStatus = LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID; - mBadBroadcastCode = null; - mNumSubGroups = 0; - mBroadcastCode = null; - } - - /*package*/ BluetoothLeBroadcastSourceInfo( - byte sourceId, - @LeAudioBroadcastSourceAddressType int addressType, - @NonNull BluetoothDevice device, - byte advSid, - int broadcastId, - @LeAudioBroadcastSinkPaSyncState int paSyncstate, - @LeAudioBroadcastSinkEncryptionState int encryptionStatus, - @LeAudioBroadcastSinkAudioSyncState int audioSyncstate, - @Nullable byte[] badCode, - byte numSubGroups, - @NonNull Map<Integer, Integer> bisSyncState, - @Nullable Map<Integer, byte[]> subgroupMetadata, - @NonNull String broadcastCode) { - mSourceId = sourceId; - mSourceAddressType = addressType; - mSourceDevice = device; - mSourceAdvSid = advSid; - mBroadcastId = broadcastId; - mPaSyncState = paSyncstate; - mEncryptionStatus = encryptionStatus; - mAudioSyncState = audioSyncstate; - - if (badCode != null && badCode.length != 0) { - mBadBroadcastCode = new byte[badCode.length]; - System.arraycopy(badCode, 0, mBadBroadcastCode, 0, badCode.length); - } - mNumSubGroups = numSubGroups; - mSubgroupBisSyncState = new HashMap<Integer, Integer>(bisSyncState); - mSubgroupMetadata = new HashMap<Integer, byte[]>(subgroupMetadata); - mBroadcastCode = broadcastCode; - } - - @Override - public boolean equals(Object o) { - if (o instanceof BluetoothLeBroadcastSourceInfo) { - BluetoothLeBroadcastSourceInfo other = (BluetoothLeBroadcastSourceInfo) o; - return (other.mSourceId == mSourceId - && other.mSourceAddressType == mSourceAddressType - && other.mSourceDevice == mSourceDevice - && other.mSourceAdvSid == mSourceAdvSid - && other.mBroadcastId == mBroadcastId - && other.mPaSyncState == mPaSyncState - && other.mEncryptionStatus == mEncryptionStatus - && other.mAudioSyncState == mAudioSyncState - && Arrays.equals(other.mBadBroadcastCode, mBadBroadcastCode) - && other.mNumSubGroups == mNumSubGroups - && mSubgroupBisSyncState.equals(other.mSubgroupBisSyncState) - && mSubgroupMetadata.equals(other.mSubgroupMetadata) - && other.mBroadcastCode == mBroadcastCode); - } - return false; - } - - /** - * Checks if an instance of {@link BluetoothLeBroadcastSourceInfo} is empty. - * - * @hide - */ - public boolean isEmpty() { - boolean ret = false; - if (mSourceAddressType == LE_AUDIO_BROADCAST_SOURCE_ADDRESS_TYPE_INVALID - && mSourceDevice == null - && mSourceAdvSid == (byte) 0 - && mPaSyncState == LE_AUDIO_BROADCAST_SINK_PA_SYNC_STATE_INVALID - && mEncryptionStatus == LE_AUDIO_BROADCAST_SINK_ENC_STATE_INVALID - && mAudioSyncState == LE_AUDIO_BROADCAST_SINK_AUDIO_SYNC_STATE_INVALID - && mBadBroadcastCode == null - && mNumSubGroups == 0 - && mSubgroupBisSyncState.size() == 0 - && mSubgroupMetadata.size() == 0 - && mBroadcastCode == null) { - ret = true; - } - return ret; - } - - /** - * Compares an instance of {@link BluetoothLeBroadcastSourceInfo} with the provided instance. - * - * @hide - */ - public boolean matches(BluetoothLeBroadcastSourceInfo srcInfo) { - boolean ret = false; - if (srcInfo == null) { - ret = false; - } else { - if (mSourceDevice == null) { - if (mSourceAdvSid == srcInfo.getAdvertisingSid() - && mSourceAddressType == srcInfo.getAdvAddressType()) { - ret = true; - } - } else { - if (mSourceDevice.equals(srcInfo.getSourceDevice()) - && mSourceAdvSid == srcInfo.getAdvertisingSid() - && mSourceAddressType == srcInfo.getAdvAddressType() - && mBroadcastId == srcInfo.getBroadcastId()) { - ret = true; - } - } - } - return ret; - } - - @Override - public int hashCode() { - return Objects.hash( - mSourceId, - mSourceAddressType, - mSourceDevice, - mSourceAdvSid, - mBroadcastId, - mPaSyncState, - mEncryptionStatus, - mAudioSyncState, - mBadBroadcastCode, - mNumSubGroups, - mSubgroupBisSyncState, - mSubgroupMetadata, - mBroadcastCode); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public String toString() { - return "{BluetoothLeBroadcastSourceInfo : mSourceId" - + mSourceId - + " addressType: " - + mSourceAddressType - + " sourceDevice: " - + mSourceDevice - + " mSourceAdvSid:" - + mSourceAdvSid - + " mBroadcastId:" - + mBroadcastId - + " mPaSyncState:" - + mPaSyncState - + " mEncryptionStatus:" - + mEncryptionStatus - + " mAudioSyncState:" - + mAudioSyncState - + " mBadBroadcastCode:" - + mBadBroadcastCode - + " mNumSubGroups:" - + mNumSubGroups - + " mSubgroupBisSyncState:" - + mSubgroupBisSyncState - + " mSubgroupMetadata:" - + mSubgroupMetadata - + " mBroadcastCode:" - + mBroadcastCode - + "}"; - } - - /** - * Get the Source Id - * - * @return byte representing the Source Id, {@link - * #LE_AUDIO_BROADCAST_ASSISTANT_INVALID_SOURCE_ID} if invalid - * @hide - */ - public byte getSourceId() { - return mSourceId; - } - - /** - * Set the Source Id - * - * @param sourceId source Id - * @hide - */ - public void setSourceId(byte sourceId) { - mSourceId = sourceId; - } - - /** - * Set the Broadcast Source device - * - * @param sourceDevice the Broadcast Source BluetoothDevice - * @hide - */ - public void setSourceDevice(@NonNull BluetoothDevice sourceDevice) { - mSourceDevice = sourceDevice; - } - - /** - * Get the Broadcast Source BluetoothDevice - * - * @return Broadcast Source BluetoothDevice - * @hide - */ - public @NonNull BluetoothDevice getSourceDevice() { - return mSourceDevice; - } - - /** - * Set the address type of the Broadcast Source advertisements - * - * @hide - */ - public void setAdvAddressType(@LeAudioBroadcastSourceAddressType int addressType) { - mSourceAddressType = addressType; - } - - /** - * Get the address type used by advertisements from the Broadcast Source. - * BluetoothLeBroadcastSourceInfo Object - * - * @hide - */ - @LeAudioBroadcastSourceAddressType - public int getAdvAddressType() { - return mSourceAddressType; - } - - /** - * Set the advertising SID of the Broadcast Source advertisement. - * - * @param advSid advertising SID of the Broadcast Source - * @hide - */ - public void setAdvertisingSid(byte advSid) { - mSourceAdvSid = advSid; - } - - /** - * Get the advertising SID of the Broadcast Source advertisement. - * - * @return advertising SID of the Broadcast Source - * @hide - */ - public byte getAdvertisingSid() { - return mSourceAdvSid; - } - - /** - * Get the Broadcast ID of the Broadcast Source. - * - * @return broadcast ID - * @hide - */ - public int getBroadcastId() { - return mBroadcastId; - } - - /** - * Set the Periodic Advertising (PA) Sync State. - * - * @hide - */ - /*package*/ void setPaSyncState(@LeAudioBroadcastSinkPaSyncState int paSyncState) { - mPaSyncState = paSyncState; - } - - /** - * Get the Periodic Advertising (PA) Sync State - * - * @hide - */ - public @LeAudioBroadcastSinkPaSyncState int getMetadataSyncState() { - return mPaSyncState; - } - - /** - * Set the audio sync state - * - * @hide - */ - /*package*/ void setAudioSyncState(@LeAudioBroadcastSinkAudioSyncState int audioSyncState) { - mAudioSyncState = audioSyncState; - } - - /** - * Get the audio sync state - * - * @hide - */ - public @LeAudioBroadcastSinkAudioSyncState int getAudioSyncState() { - return mAudioSyncState; - } - - /** - * Set the encryption status - * - * @hide - */ - /*package*/ void setEncryptionStatus( - @LeAudioBroadcastSinkEncryptionState int encryptionStatus) { - mEncryptionStatus = encryptionStatus; - } - - /** - * Get the encryption status - * - * @hide - */ - public @LeAudioBroadcastSinkEncryptionState int getEncryptionStatus() { - return mEncryptionStatus; - } - - /** - * Get the incorrect broadcast code that the Scan delegator used to decrypt the Broadcast Audio - * Stream and failed. - * - * <p>This code is valid only if {@link #getEncryptionStatus} returns {@link - * #LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE} - * - * @return byte array containing bad broadcast value, null if the current encryption status is - * not {@link #LE_AUDIO_BROADCAST_SINK_ENC_STATE_BAD_CODE} - * @hide - */ - public @Nullable byte[] getBadBroadcastCode() { - return mBadBroadcastCode; - } - - /** - * Get the number of subgroups. - * - * @return number of subgroups - * @hide - */ - public byte getNumberOfSubGroups() { - return mNumSubGroups; - } - - public @NonNull Map<Integer, Integer> getSubgroupBisSyncState() { - return mSubgroupBisSyncState; - } - - public void setSubgroupBisSyncState(@NonNull Map<Integer, Integer> bisSyncState) { - mSubgroupBisSyncState = new HashMap<Integer, Integer>(bisSyncState); - } - - /*package*/ void setBroadcastCode(@NonNull String broadcastCode) { - mBroadcastCode = broadcastCode; - } - - /** - * Get the broadcast code - * - * @return - * @hide - */ - public @NonNull String getBroadcastCode() { - return mBroadcastCode; - } - - /** - * Set the broadcast ID - * - * @param broadcastId broadcast ID of the Broadcast Source - * @hide - */ - public void setBroadcastId(int broadcastId) { - mBroadcastId = broadcastId; - } - - private void writeSubgroupBisSyncStateToParcel( - @NonNull Parcel dest, @NonNull Map<Integer, Integer> subgroupBisSyncState) { - dest.writeInt(subgroupBisSyncState.size()); - for (Map.Entry<Integer, Integer> entry : subgroupBisSyncState.entrySet()) { - dest.writeInt(entry.getKey()); - dest.writeInt(entry.getValue()); - } - } - - private static void readSubgroupBisSyncStateFromParcel( - @NonNull Parcel in, @NonNull Map<Integer, Integer> subgroupBisSyncState) { - int size = in.readInt(); - - for (int i = 0; i < size; i++) { - Integer key = in.readInt(); - Integer value = in.readInt(); - subgroupBisSyncState.put(key, value); - } - } - - private void writeSubgroupMetadataToParcel( - @NonNull Parcel dest, @Nullable Map<Integer, byte[]> subgroupMetadata) { - if (subgroupMetadata == null) { - dest.writeInt(0); - return; - } - - dest.writeInt(subgroupMetadata.size()); - for (Map.Entry<Integer, byte[]> entry : subgroupMetadata.entrySet()) { - dest.writeInt(entry.getKey()); - byte[] metadata = entry.getValue(); - if (metadata != null) { - dest.writeInt(metadata.length); - dest.writeByteArray(metadata); - } - } - } - - private static void readSubgroupMetadataFromParcel( - @NonNull Parcel in, @NonNull Map<Integer, byte[]> subgroupMetadata) { - int size = in.readInt(); - - for (int i = 0; i < size; i++) { - Integer key = in.readInt(); - Integer metaDataLen = in.readInt(); - byte[] metadata = null; - if (metaDataLen != 0) { - metadata = new byte[metaDataLen]; - in.readByteArray(metadata); - } - subgroupMetadata.put(key, metadata); - } - } - - public static final @NonNull Parcelable.Creator<BluetoothLeBroadcastSourceInfo> CREATOR = - new Parcelable.Creator<BluetoothLeBroadcastSourceInfo>() { - public @NonNull BluetoothLeBroadcastSourceInfo createFromParcel( - @NonNull Parcel in) { - final byte sourceId = in.readByte(); - final int sourceAddressType = in.readInt(); - final BluetoothDevice sourceDevice = - in.readTypedObject(BluetoothDevice.CREATOR); - final byte sourceAdvSid = in.readByte(); - final int broadcastId = in.readInt(); - final int paSyncState = in.readInt(); - final int audioSyncState = in.readInt(); - final int encryptionStatus = in.readInt(); - final int badBroadcastLen = in.readInt(); - byte[] badBroadcastCode = null; - - if (badBroadcastLen > 0) { - badBroadcastCode = new byte[badBroadcastLen]; - in.readByteArray(badBroadcastCode); - } - final byte numSubGroups = in.readByte(); - final String broadcastCode = in.readString(); - Map<Integer, Integer> subgroupBisSyncState = new HashMap<Integer, Integer>(); - readSubgroupBisSyncStateFromParcel(in, subgroupBisSyncState); - Map<Integer, byte[]> subgroupMetadata = new HashMap<Integer, byte[]>(); - readSubgroupMetadataFromParcel(in, subgroupMetadata); - - BluetoothLeBroadcastSourceInfo srcInfo = - new BluetoothLeBroadcastSourceInfo( - sourceId, - sourceAddressType, - sourceDevice, - sourceAdvSid, - broadcastId, - paSyncState, - encryptionStatus, - audioSyncState, - badBroadcastCode, - numSubGroups, - subgroupBisSyncState, - subgroupMetadata, - broadcastCode); - return srcInfo; - } - - public @NonNull BluetoothLeBroadcastSourceInfo[] newArray(int size) { - return new BluetoothLeBroadcastSourceInfo[size]; - } - }; - - @Override - public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeByte(mSourceId); - out.writeInt(mSourceAddressType); - out.writeTypedObject(mSourceDevice, 0); - out.writeByte(mSourceAdvSid); - out.writeInt(mBroadcastId); - out.writeInt(mPaSyncState); - out.writeInt(mAudioSyncState); - out.writeInt(mEncryptionStatus); - - if (mBadBroadcastCode != null) { - out.writeInt(mBadBroadcastCode.length); - out.writeByteArray(mBadBroadcastCode); - } else { - // zero indicates that there is no "bad broadcast code" - out.writeInt(0); - } - out.writeByte(mNumSubGroups); - out.writeString(mBroadcastCode); - writeSubgroupBisSyncStateToParcel(out, mSubgroupBisSyncState); - writeSubgroupMetadataToParcel(out, mSubgroupMetadata); - } - - private static void log(@NonNull String msg) { - if (DBG) { - Log.d(TAG, msg); - } - } -} -; diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java deleted file mode 100644 index fef6f225dd3b..000000000000 --- a/core/java/android/bluetooth/BluetoothManager.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2013 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.bluetooth; - -import android.annotation.RequiresFeature; -import android.annotation.RequiresNoPermission; -import android.annotation.RequiresPermission; -import android.annotation.SystemService; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.content.AttributionSource; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.RemoteException; -import android.util.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * High level manager used to obtain an instance of an {@link BluetoothAdapter} - * and to conduct overall Bluetooth Management. - * <p> - * Use {@link android.content.Context#getSystemService(java.lang.String)} - * with {@link Context#BLUETOOTH_SERVICE} to create an {@link BluetoothManager}, - * then call {@link #getAdapter} to obtain the {@link BluetoothAdapter}. - * </p> - * <div class="special reference"> - * <h3>Developer Guides</h3> - * <p> - * For more information about using BLUETOOTH, read the <a href= - * "{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer - * guide. - * </p> - * </div> - * - * @see Context#getSystemService - * @see BluetoothAdapter#getDefaultAdapter() - */ -@SystemService(Context.BLUETOOTH_SERVICE) -@RequiresFeature(PackageManager.FEATURE_BLUETOOTH) -public final class BluetoothManager { - private static final String TAG = "BluetoothManager"; - private static final boolean DBG = false; - - private final AttributionSource mAttributionSource; - private final BluetoothAdapter mAdapter; - - /** - * @hide - */ - public BluetoothManager(Context context) { - mAttributionSource = (context != null) ? context.getAttributionSource() : - AttributionSource.myAttributionSource(); - mAdapter = BluetoothAdapter.createAdapter(mAttributionSource); - } - - /** - * Get the BLUETOOTH Adapter for this device. - * - * @return the BLUETOOTH Adapter - */ - @RequiresNoPermission - public BluetoothAdapter getAdapter() { - return mAdapter; - } - - /** - * Get the current connection state of the profile to the remote device. - * - * <p>This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for certain profile. - * This can be used by applications like status bar which would just like - * to know the state of Bluetooth. - * - * @param device Remote bluetooth device. - * @param profile GATT or GATT_SERVER - * @return State of the profile connection. One of {@link BluetoothProfile#STATE_CONNECTED}, - * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED}, - * {@link BluetoothProfile#STATE_DISCONNECTING} - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(BluetoothDevice device, int profile) { - if (DBG) Log.d(TAG, "getConnectionState()"); - - List<BluetoothDevice> connectedDevices = getConnectedDevices(profile); - for (BluetoothDevice connectedDevice : connectedDevices) { - if (device.equals(connectedDevice)) { - return BluetoothProfile.STATE_CONNECTED; - } - } - - return BluetoothProfile.STATE_DISCONNECTED; - } - - /** - * Get connected devices for the specified profile. - * - * <p> Return the set of devices which are in state {@link BluetoothProfile#STATE_CONNECTED} - * - * <p>This is not specific to any application configuration but represents - * the connection state of Bluetooth for this profile. - * This can be used by applications like status bar which would just like - * to know the state of Bluetooth. - * - * @param profile GATT or GATT_SERVER - * @return List of devices. The list will be empty on error. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getConnectedDevices(int profile) { - if (DBG) Log.d(TAG, "getConnectedDevices"); - return getDevicesMatchingConnectionStates(profile, new int[] { - BluetoothProfile.STATE_CONNECTED - }); - } - - /** - * Get a list of devices that match any of the given connection - * states. - * - * <p> If none of the devices match any of the given states, - * an empty list will be returned. - * - * <p>This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. - * - * @param profile GATT or GATT_SERVER - * @param states Array of states. States can be one of {@link BluetoothProfile#STATE_CONNECTED}, - * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED}, - * {@link BluetoothProfile#STATE_DISCONNECTING}, - * @return List of devices. The list will be empty on error. - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int profile, int[] states) { - if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates"); - - if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { - throw new IllegalArgumentException("Profile not supported: " + profile); - } - - List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); - - try { - IBluetoothManager managerService = mAdapter.getBluetoothManager(); - IBluetoothGatt iGatt = managerService.getBluetoothGatt(); - if (iGatt == null) return devices; - devices = Attributable.setAttributionSource( - iGatt.getDevicesMatchingConnectionStates(states, mAttributionSource), - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - - return devices; - } - - /** - * Open a GATT Server - * The callback is used to deliver results to Caller, such as connection status as well - * as the results of any other GATT server operations. - * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer - * to conduct GATT server operations. - * - * @param context App context - * @param callback GATT server callback handler that will receive asynchronous callbacks. - * @return BluetoothGattServer instance - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothGattServer openGattServer(Context context, - BluetoothGattServerCallback callback) { - - return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO)); - } - - /** - * Open a GATT Server - * The callback is used to deliver results to Caller, such as connection status as well - * as the results of any other GATT server operations. - * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer - * to conduct GATT server operations. - * - * @param context App context - * @param callback GATT server callback handler that will receive asynchronous callbacks. - * @param eatt_support idicates if server should use eatt channel for notifications. - * @return BluetoothGattServer instance - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothGattServer openGattServer(Context context, - BluetoothGattServerCallback callback, boolean eatt_support) { - return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO, eatt_support)); - } - - /** - * Open a GATT Server - * The callback is used to deliver results to Caller, such as connection status as well - * as the results of any other GATT server operations. - * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer - * to conduct GATT server operations. - * - * @param context App context - * @param callback GATT server callback handler that will receive asynchronous callbacks. - * @param transport preferred transport for GATT connections to remote dual-mode devices {@link - * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link - * BluetoothDevice#TRANSPORT_LE} - * @return BluetoothGattServer instance - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothGattServer openGattServer(Context context, - BluetoothGattServerCallback callback, int transport) { - return (openGattServer(context, callback, transport, false)); - } - - /** - * Open a GATT Server - * The callback is used to deliver results to Caller, such as connection status as well - * as the results of any other GATT server operations. - * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer - * to conduct GATT server operations. - * - * @param context App context - * @param callback GATT server callback handler that will receive asynchronous callbacks. - * @param transport preferred transport for GATT connections to remote dual-mode devices {@link - * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link - * BluetoothDevice#TRANSPORT_LE} - * @param eatt_support idicates if server should use eatt channel for notifications. - * @return BluetoothGattServer instance - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothGattServer openGattServer(Context context, - BluetoothGattServerCallback callback, int transport, boolean eatt_support) { - if (context == null || callback == null) { - throw new IllegalArgumentException("null parameter: " + context + " " + callback); - } - - // TODO(Bluetooth) check whether platform support BLE - // Do the check here or in GattServer? - - try { - IBluetoothManager managerService = mAdapter.getBluetoothManager(); - IBluetoothGatt iGatt = managerService.getBluetoothGatt(); - if (iGatt == null) { - Log.e(TAG, "Fail to get GATT Server connection"); - return null; - } - BluetoothGattServer mGattServer = - new BluetoothGattServer(iGatt, transport, mAdapter); - Boolean regStatus = mGattServer.registerCallback(callback, eatt_support); - return regStatus ? mGattServer : null; - } catch (RemoteException e) { - Log.e(TAG, "", e); - return null; - } - } -} diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java deleted file mode 100644 index 56e497262421..000000000000 --- a/core/java/android/bluetooth/BluetoothMap.java +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.RequiresNoPermission; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.Context; -import android.os.Build; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.CloseGuard; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This class provides the APIs to control the Bluetooth MAP - * Profile. - * - * @hide - */ -@SystemApi -public final class BluetoothMap implements BluetoothProfile, AutoCloseable { - - private static final String TAG = "BluetoothMap"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - private CloseGuard mCloseGuard; - - /** @hide */ - @SuppressLint("ActionValue") - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; - - /** - * There was an error trying to obtain the state - * - * @hide - */ - public static final int STATE_ERROR = -1; - - /** @hide */ - public static final int RESULT_FAILURE = 0; - /** @hide */ - public static final int RESULT_SUCCESS = 1; - /** - * Connection canceled before completion. - * - * @hide - */ - public static final int RESULT_CANCELED = 2; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothMap> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.MAP, - "BluetoothMap", IBluetoothMap.class.getName()) { - @Override - public IBluetoothMap getServiceInterface(IBinder service) { - return IBluetoothMap.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothMap proxy object. - */ - /* package */ BluetoothMap(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - if (DBG) Log.d(TAG, "Create BluetoothMap proxy object"); - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - mCloseGuard = new CloseGuard(); - mCloseGuard.open("close"); - } - - protected void finalize() { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } - close(); - } - - /** - * Close the connection to the backing service. - * Other public functions of BluetoothMap will return default error - * results once close() has been called. Multiple invocations of close() - * are ok. - * - * @hide - */ - @SystemApi - public void close() { - if (VDBG) log("close()"); - mProfileConnector.disconnect(); - } - - private IBluetoothMap getService() { - return mProfileConnector.getService(); - } - - /** - * Get the current state of the BluetoothMap service. - * - * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not - * connected to the Map service. - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getState() { - if (VDBG) log("getState()"); - final IBluetoothMap service = getService(); - final int defaultValue = BluetoothMap.STATE_ERROR; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getState(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the currently connected remote Bluetooth device (PCE). - * - * @return The remote Bluetooth device, or null if not in connected or connecting state, or if - * this proxy object is not connected to the Map service. - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothDevice getClient() { - if (VDBG) log("getClient()"); - final IBluetoothMap service = getService(); - final BluetoothDevice defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<BluetoothDevice> recv = - new SynchronousResultReceiver(); - service.getClient(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Returns true if the specified Bluetooth device is connected. - * Returns false if not connected, or if this proxy object is not - * currently connected to the Map service. - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isConnected(BluetoothDevice device) { - if (VDBG) log("isConnected(" + device + ")"); - final IBluetoothMap service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isConnected(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate connection. Initiation of outgoing connections is not - * supported for MAP server. - * - * @hide - */ - @RequiresNoPermission - public boolean connect(BluetoothDevice device) { - if (DBG) log("connect(" + device + ")" + "not supported for MAPS"); - return false; - } - - /** - * Initiate disconnect. - * - * @param device Remote Bluetooth Device - * @return false on error, true otherwise - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnect(BluetoothDevice device) { - if (DBG) log("disconnect(" + device + ")"); - final IBluetoothMap service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Check class bits for possible Map support. - * This is a simple heuristic that tries to guess if a device with the - * given class bits might support Map. It is not accurate for all - * devices. It tries to err on the side of false positives. - * - * @return True if this device might support Map. - * - * @hide - */ - public static boolean doesClassMatchSink(BluetoothClass btClass) { - // TODO optimize the rule - switch (btClass.getDeviceClass()) { - case BluetoothClass.Device.COMPUTER_DESKTOP: - case BluetoothClass.Device.COMPUTER_LAPTOP: - case BluetoothClass.Device.COMPUTER_SERVER: - case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: - return true; - default: - return false; - } - } - - /** - * Get the list of connected devices. Currently at most one. - * - * @return list of connected devices - * - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @NonNull List<BluetoothDevice> getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); - final IBluetoothMap service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the list of devices matching specified states. Currently at most one. - * - * @return list of matching devices - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothMap service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get connection state of device - * - * @return device connection state - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothMap service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = - new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothMap service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the priority of the profile. - * - * <p> The priority can be any of: - * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - final IBluetoothMap service = getService(); - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } - - private boolean isEnabled() { - return mAdapter.isEnabled(); - } - - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } -} diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java deleted file mode 100644 index 03536f9aad39..000000000000 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ /dev/null @@ -1,686 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SystemApi; -import android.app.PendingIntent; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.Context; -import android.net.Uri; -import android.os.Build; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This class provides the APIs to control the Bluetooth MAP MCE Profile. - * - * @hide - */ -@SystemApi -public final class BluetoothMapClient implements BluetoothProfile { - - private static final String TAG = "BluetoothMapClient"; - private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); - private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); - - /** @hide */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED"; - /** @hide */ - @RequiresPermission(android.Manifest.permission.RECEIVE_SMS) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_MESSAGE_RECEIVED = - "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED"; - /* Actions to be used for pending intents */ - /** @hide */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY = - "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY"; - /** @hide */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = - "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; - - /** - * Action to notify read status changed - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_MESSAGE_READ_STATUS_CHANGED = - "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED"; - - /** - * Action to notify deleted status changed - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED = - "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED"; - - /** - * Extras used in ACTION_MESSAGE_RECEIVED intent. - * NOTE: HANDLE is only valid for a single session with the device. - */ - /** @hide */ - public static final String EXTRA_MESSAGE_HANDLE = - "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE"; - /** @hide */ - public static final String EXTRA_MESSAGE_TIMESTAMP = - "android.bluetooth.mapmce.profile.extra.MESSAGE_TIMESTAMP"; - /** @hide */ - public static final String EXTRA_MESSAGE_READ_STATUS = - "android.bluetooth.mapmce.profile.extra.MESSAGE_READ_STATUS"; - /** @hide */ - public static final String EXTRA_SENDER_CONTACT_URI = - "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI"; - /** @hide */ - public static final String EXTRA_SENDER_CONTACT_NAME = - "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME"; - - /** - * Used as a boolean extra in ACTION_MESSAGE_DELETED_STATUS_CHANGED - * Contains the MAP message deleted status - * Possible values are: - * true: deleted - * false: undeleted - * - * @hide - */ - public static final String EXTRA_MESSAGE_DELETED_STATUS = - "android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS"; - - /** - * Extra used in ACTION_MESSAGE_READ_STATUS_CHANGED or ACTION_MESSAGE_DELETED_STATUS_CHANGED - * Possible values are: - * 0: failure - * 1: success - * - * @hide - */ - public static final String EXTRA_RESULT_CODE = - "android.bluetooth.device.extra.RESULT_CODE"; - - /** - * There was an error trying to obtain the state - * @hide - */ - public static final int STATE_ERROR = -1; - - /** @hide */ - public static final int RESULT_FAILURE = 0; - /** @hide */ - public static final int RESULT_SUCCESS = 1; - /** - * Connection canceled before completion. - * @hide - */ - public static final int RESULT_CANCELED = 2; - /** @hide */ - private static final int UPLOADING_FEATURE_BITMASK = 0x08; - - /* - * UNREAD, READ, UNDELETED, DELETED are passed as parameters - * to setMessageStatus to indicate the messages new state. - */ - - /** @hide */ - public static final int UNREAD = 0; - /** @hide */ - public static final int READ = 1; - /** @hide */ - public static final int UNDELETED = 2; - /** @hide */ - public static final int DELETED = 3; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT, - "BluetoothMapClient", IBluetoothMapClient.class.getName()) { - @Override - public IBluetoothMapClient getServiceInterface(IBinder service) { - return IBluetoothMapClient.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothMapClient proxy object. - */ - /* package */ BluetoothMapClient(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object"); - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - } - - /** - * Close the connection to the backing service. - * Other public functions of BluetoothMap will return default error - * results once close() has been called. Multiple invocations of close() - * are ok. - * @hide - */ - public void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothMapClient getService() { - return mProfileConnector.getService(); - } - - /** - * Returns true if the specified Bluetooth device is connected. - * Returns false if not connected, or if this proxy object is not - * currently connected to the Map service. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isConnected(BluetoothDevice device) { - if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); - final IBluetoothMapClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isConnected(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate connection. Initiation of outgoing connections is not - * supported for MAP server. - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean connect(BluetoothDevice device) { - if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE"); - final IBluetoothMapClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate disconnect. - * - * @param device Remote Bluetooth Device - * @return false on error, true otherwise - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean disconnect(BluetoothDevice device) { - if (DBG) Log.d(TAG, "disconnect(" + device + ")"); - final IBluetoothMapClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the list of connected devices. Currently at most one. - * - * @return list of connected devices - * @hide - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getConnectedDevices() { - if (DBG) Log.d(TAG, "getConnectedDevices()"); - final IBluetoothMapClient service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the list of devices matching specified states. Currently at most one. - * - * @return list of matching devices - * @hide - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); - final IBluetoothMapClient service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get connection state of device - * - * @return device connection state - * @hide - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(BluetoothDevice device) { - if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); - final IBluetoothMapClient service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver<>(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothMapClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the priority of the profile. - * - * <p> The priority can be any of: - * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public int getPriority(BluetoothDevice device) { - if (VDBG) Log.d(TAG, "getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { - if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")"); - final IBluetoothMapClient service = getService(); - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Send a message. - * - * Send an SMS message to either the contacts primary number or the telephone number specified. - * - * @param device Bluetooth device - * @param contacts Uri Collection of the contacts - * @param message Message to be sent - * @param sentIntent intent issued when message is sent - * @param deliveredIntent intent issued when message is delivered - * @return true if the message is enqueued, false on error - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.SEND_SMS, - }) - public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection<Uri> contacts, - @NonNull String message, @Nullable PendingIntent sentIntent, - @Nullable PendingIntent deliveredIntent) { - return sendMessage(device, contacts.toArray(new Uri[contacts.size()]), message, sentIntent, - deliveredIntent); - } - - /** - * Send a message. - * - * Send an SMS message to either the contacts primary number or the telephone number specified. - * - * @param device Bluetooth device - * @param contacts Uri[] of the contacts - * @param message Message to be sent - * @param sentIntent intent issued when message is sent - * @param deliveredIntent intent issued when message is delivered - * @return true if the message is enqueued, false on error - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.SEND_SMS, - }) - public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, - PendingIntent sentIntent, PendingIntent deliveredIntent) { - if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); - final IBluetoothMapClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.sendMessage(device, contacts, message, sentIntent, deliveredIntent, - mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get unread messages. Unread messages will be published via {@link #ACTION_MESSAGE_RECEIVED}. - * - * @param device Bluetooth device - * @return true if the message is enqueued, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.READ_SMS, - }) - public boolean getUnreadMessages(BluetoothDevice device) { - if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); - final IBluetoothMapClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.getUnreadMessages(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Returns the "Uploading" feature bit value from the SDP record's - * MapSupportedFeatures field (see Bluetooth MAP 1.4 spec, page 114). - * @param device The Bluetooth device to get this value for. - * @return Returns true if the Uploading bit value in SDP record's - * MapSupportedFeatures field is set. False is returned otherwise. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isUploadingSupported(BluetoothDevice device) { - if (DBG) Log.d(TAG, "isUploadingSupported(" + device + ")"); - final IBluetoothMapClient service = getService(); - final int defaultValue = 0; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getSupportedFeatures(device, mAttributionSource, recv); - return (recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue) - & UPLOADING_FEATURE_BITMASK) > 0; - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return false; - } - - /** - * Set message status of message on MSE - * <p> - * When read status changed, the result will be published via - * {@link #ACTION_MESSAGE_READ_STATUS_CHANGED} - * When deleted status changed, the result will be published via - * {@link #ACTION_MESSAGE_DELETED_STATUS_CHANGED} - * - * @param device Bluetooth device - * @param handle message handle - * @param status <code>UNREAD</code> for "unread", <code>READ</code> for - * "read", <code>UNDELETED</code> for "undeleted", <code>DELETED</code> for - * "deleted", otherwise return error - * @return <code>true</code> if request has been sent, <code>false</code> on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.READ_SMS, - }) - public boolean setMessageStatus(BluetoothDevice device, String handle, int status) { - if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")"); - final IBluetoothMapClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) && handle != null && (status == READ - || status == UNREAD || status == UNDELETED || status == DELETED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setMessageStatus(device, handle, status, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - private boolean isEnabled() { - return mAdapter.isEnabled(); - } - - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } -} diff --git a/core/java/android/bluetooth/BluetoothMasInstance.java b/core/java/android/bluetooth/BluetoothMasInstance.java deleted file mode 100644 index eeaf08545146..000000000000 --- a/core/java/android/bluetooth/BluetoothMasInstance.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; - -/** @hide */ -public final class BluetoothMasInstance implements Parcelable { - private final int mId; - private final String mName; - private final int mChannel; - private final int mMsgTypes; - - public BluetoothMasInstance(int id, String name, int channel, int msgTypes) { - mId = id; - mName = name; - mChannel = channel; - mMsgTypes = msgTypes; - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof BluetoothMasInstance) { - return mId == ((BluetoothMasInstance) o).mId; - } - return false; - } - - @Override - public int hashCode() { - return mId + (mChannel << 8) + (mMsgTypes << 16); - } - - @Override - public String toString() { - return Integer.toString(mId) + ":" + mName + ":" + mChannel + ":" - + Integer.toHexString(mMsgTypes); - } - - @Override - public int describeContents() { - return 0; - } - - public static final @android.annotation.NonNull Parcelable.Creator<BluetoothMasInstance> CREATOR = - new Parcelable.Creator<BluetoothMasInstance>() { - public BluetoothMasInstance createFromParcel(Parcel in) { - return new BluetoothMasInstance(in.readInt(), in.readString(), - in.readInt(), in.readInt()); - } - - public BluetoothMasInstance[] newArray(int size) { - return new BluetoothMasInstance[size]; - } - }; - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mId); - out.writeString(mName); - out.writeInt(mChannel); - out.writeInt(mMsgTypes); - } - - public static final class MessageType { - public static final int EMAIL = 0x01; - public static final int SMS_GSM = 0x02; - public static final int SMS_CDMA = 0x04; - public static final int MMS = 0x08; - } - - public int getId() { - return mId; - } - - public String getName() { - return mName; - } - - public int getChannel() { - return mChannel; - } - - public int getMsgTypes() { - return mMsgTypes; - } - - public boolean msgSupported(int msg) { - return (mMsgTypes & msg) != 0; - } -} diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java deleted file mode 100644 index ac2b3edb0eb8..000000000000 --- a/core/java/android/bluetooth/BluetoothOutputStream.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2009 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.bluetooth; - -import android.annotation.SuppressLint; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * BluetoothOutputStream. - * - * Used to read from a Bluetooth socket. - * - * @hide - */ -@SuppressLint("AndroidFrameworkBluetoothPermission") -/*package*/ final class BluetoothOutputStream extends OutputStream { - private BluetoothSocket mSocket; - - /*package*/ BluetoothOutputStream(BluetoothSocket s) { - mSocket = s; - } - - /** - * Close this output stream and the socket associated with it. - */ - public void close() throws IOException { - mSocket.close(); - } - - /** - * Writes a single byte to this stream. Only the least significant byte of - * the integer {@code oneByte} is written to the stream. - * - * @param oneByte the byte to be written. - * @throws IOException if an error occurs while writing to this stream. - * @since Android 1.0 - */ - public void write(int oneByte) throws IOException { - byte[] b = new byte[1]; - b[0] = (byte) oneByte; - mSocket.write(b, 0, 1); - } - - /** - * Writes {@code count} bytes from the byte array {@code buffer} starting - * at position {@code offset} to this stream. - * - * @param b the buffer to be written. - * @param offset the start position in {@code buffer} from where to get bytes. - * @param count the number of bytes from {@code buffer} to write to this stream. - * @throws IOException if an error occurs while writing to this stream. - * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code count < 0}, or if {@code - * offset + count} is bigger than the length of {@code buffer}. - * @since Android 1.0 - */ - public void write(byte[] b, int offset, int count) throws IOException { - if (b == null) { - throw new NullPointerException("buffer is null"); - } - if ((offset | count) < 0 || count > b.length - offset) { - throw new IndexOutOfBoundsException("invalid offset or length"); - } - mSocket.write(b, offset, count); - } -} diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java deleted file mode 100644 index d4ad4ef47acd..000000000000 --- a/core/java/android/bluetooth/BluetoothPan.java +++ /dev/null @@ -1,525 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.Context; -import android.os.Build; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This class provides the APIs to control the Bluetooth Pan - * Profile. - * - * <p>BluetoothPan is a proxy object for controlling the Bluetooth - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothPan proxy object. - * - * <p>Each method is protected with its appropriate permission. - * - * @hide - */ -@SystemApi -public final class BluetoothPan implements BluetoothProfile { - private static final String TAG = "BluetoothPan"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** - * Intent used to broadcast the change in connection state of the Pan - * profile. - * - * <p>This intent will have 4 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * <li> {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is - * bound to. </li> - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - * <p> {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or - * {@link #LOCAL_PANU_ROLE} - */ - @SuppressLint("ActionValue") - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED"; - - /** - * Extra for {@link #ACTION_CONNECTION_STATE_CHANGED} intent - * The local role of the PAN profile that the remote device is bound to. - * It can be one of {@link #LOCAL_NAP_ROLE} or {@link #LOCAL_PANU_ROLE}. - */ - @SuppressLint("ActionValue") - public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE"; - - /** - * Intent used to broadcast the change in tethering state of the Pan - * Profile - * - * <p>This intent will have 1 extra: - * <ul> - * <li> {@link #EXTRA_TETHERING_STATE} - The current state of Bluetooth - * tethering. </li> - * </ul> - * - * <p> {@link #EXTRA_TETHERING_STATE} can be any of {@link #TETHERING_STATE_OFF} or - * {@link #TETHERING_STATE_ON} - */ - @RequiresLegacyBluetoothPermission - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_TETHERING_STATE_CHANGED = - "android.bluetooth.action.TETHERING_STATE_CHANGED"; - - /** - * Extra for {@link #ACTION_TETHERING_STATE_CHANGED} intent - * The tethering state of the PAN profile. - * It can be one of {@link #TETHERING_STATE_OFF} or {@link #TETHERING_STATE_ON}. - */ - public static final String EXTRA_TETHERING_STATE = - "android.bluetooth.extra.TETHERING_STATE"; - - /** @hide */ - @IntDef({PAN_ROLE_NONE, LOCAL_NAP_ROLE, LOCAL_PANU_ROLE}) - @Retention(RetentionPolicy.SOURCE) - public @interface LocalPanRole {} - - public static final int PAN_ROLE_NONE = 0; - /** - * The local device is acting as a Network Access Point. - */ - public static final int LOCAL_NAP_ROLE = 1; - - /** - * The local device is acting as a PAN User. - */ - public static final int LOCAL_PANU_ROLE = 2; - - /** @hide */ - @IntDef({PAN_ROLE_NONE, REMOTE_NAP_ROLE, REMOTE_PANU_ROLE}) - @Retention(RetentionPolicy.SOURCE) - public @interface RemotePanRole {} - - public static final int REMOTE_NAP_ROLE = 1; - - public static final int REMOTE_PANU_ROLE = 2; - - /** @hide **/ - @IntDef({TETHERING_STATE_OFF, TETHERING_STATE_ON}) - @Retention(RetentionPolicy.SOURCE) - public @interface TetheringState{} - - public static final int TETHERING_STATE_OFF = 1; - - public static final int TETHERING_STATE_ON = 2; - /** - * Return codes for the connect and disconnect Bluez / Dbus calls. - * - * @hide - */ - public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000; - - /** - * @hide - */ - public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001; - - /** - * @hide - */ - public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002; - - /** - * @hide - */ - public static final int PAN_OPERATION_GENERIC_FAILURE = 1003; - - /** - * @hide - */ - public static final int PAN_OPERATION_SUCCESS = 1004; - - private final Context mContext; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.PAN, - "BluetoothPan", IBluetoothPan.class.getName()) { - @Override - public IBluetoothPan getServiceInterface(IBinder service) { - return IBluetoothPan.Stub.asInterface(service); - } - }; - - - /** - * Create a BluetoothPan proxy object for interacting with the local - * Bluetooth Service which handles the Pan profile - * - * @hide - */ - @UnsupportedAppUsage - /* package */ BluetoothPan(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mContext = context; - mProfileConnector.connect(context, listener); - } - - /** - * Closes the connection to the service and unregisters callbacks - */ - @UnsupportedAppUsage - void close() { - if (VDBG) log("close()"); - mProfileConnector.disconnect(); - } - - private IBluetoothPan getService() { - return mProfileConnector.getService(); - } - - /** @hide */ - protected void finalize() { - close(); - } - - /** - * Initiate connection to a profile of the remote bluetooth device. - * - * <p> This API returns false in scenarios like the profile on the - * device is already connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that - * connection state intent for the profile will be broadcasted with - * the state. Users can get the connection state of the profile - * from this intent. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean connect(BluetoothDevice device) { - if (DBG) log("connect(" + device + ")"); - final IBluetoothPan service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate disconnection from a profile - * - * <p> This API will return false in scenarios like the profile on the - * Bluetooth device is not in connected state etc. When this API returns, - * true, it is guaranteed that the connection state change - * intent will be broadcasted with the state. Users can get the - * disconnection state of the profile from this intent. - * - * <p> If the disconnection is initiated by a remote device, the state - * will transition from {@link #STATE_CONNECTED} to - * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the - * host (local) device the state will transition from - * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to - * state {@link #STATE_DISCONNECTED}. The transition to - * {@link #STATE_DISCONNECTING} can be used to distinguish between the - * two scenarios. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, true otherwise - * @hide - */ - @UnsupportedAppUsage - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnect(BluetoothDevice device) { - if (DBG) log("disconnect(" + device + ")"); - final IBluetoothPan service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothPan service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - * @hide - */ - @SystemApi - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @NonNull List<BluetoothDevice> getConnectedDevices() { - if (VDBG) log("getConnectedDevices()"); - final IBluetoothPan service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - * @hide - */ - @Override - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothPan service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * {@inheritDoc} - * @hide - */ - @SystemApi - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public int getConnectionState(@NonNull BluetoothDevice device) { - if (VDBG) log("getState(" + device + ")"); - final IBluetoothPan service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Turns on/off bluetooth tethering - * - * @param value is whether to enable or disable bluetooth tethering - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - android.Manifest.permission.TETHER_PRIVILEGED, - }) - public void setBluetoothTethering(boolean value) { - String pkgName = mContext.getOpPackageName(); - if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName); - final IBluetoothPan service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver recv = new SynchronousResultReceiver(); - service.setBluetoothTethering(value, mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Determines whether tethering is enabled - * - * @return true if tethering is on, false if not or some error occurred - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isTetheringOn() { - if (VDBG) log("isTetheringOn()"); - final IBluetoothPan service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isTetheringOn(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - @UnsupportedAppUsage - private boolean isEnabled() { - return mAdapter.getState() == BluetoothAdapter.STATE_ON; - } - - @UnsupportedAppUsage - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - @UnsupportedAppUsage - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java deleted file mode 100644 index de2db9c2ca86..000000000000 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.Context; -import android.os.Build; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Public API for controlling the Bluetooth Pbap Service. This includes - * Bluetooth Phone book Access profile. - * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap - * Service via IPC. - * - * Creating a BluetoothPbap object will create a binding with the - * BluetoothPbap service. Users of this object should call close() when they - * are finished with the BluetoothPbap, so that this proxy object can unbind - * from the service. - * - * This BluetoothPbap object is not immediately bound to the - * BluetoothPbap service. Use the ServiceListener interface to obtain a - * notification when it is bound, this is especially important if you wish to - * immediately call methods on BluetoothPbap after construction. - * - * To get an instance of the BluetoothPbap class, you can call - * {@link BluetoothAdapter#getProfileProxy(Context, ServiceListener, int)} with the final param - * being {@link BluetoothProfile#PBAP}. The ServiceListener should be able to get the instance of - * BluetoothPbap in {@link android.bluetooth.BluetoothProfile.ServiceListener#onServiceConnected}. - * - * Android only supports one connected Bluetooth Pce at a time. - * - * @hide - */ -@SystemApi -public class BluetoothPbap implements BluetoothProfile { - - private static final String TAG = "BluetoothPbap"; - private static final boolean DBG = false; - - /** - * Intent used to broadcast the change in connection state of the PBAP - * profile. - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * <p>{@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE} - * can be any of {@link BluetoothProfile#STATE_DISCONNECTED}, - * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, - * {@link BluetoothProfile#STATE_DISCONNECTING}. - * - * @hide - */ - @SuppressLint("ActionValue") - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; - - private final AttributionSource mAttributionSource; - - /** @hide */ - public static final int RESULT_FAILURE = 0; - /** @hide */ - public static final int RESULT_SUCCESS = 1; - /** - * Connection canceled before completion. - * - * @hide - */ - public static final int RESULT_CANCELED = 2; - - private BluetoothAdapter mAdapter; - private final BluetoothProfileConnector<IBluetoothPbap> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.PBAP, "BluetoothPbap", - IBluetoothPbap.class.getName()) { - @Override - public IBluetoothPbap getServiceInterface(IBinder service) { - return IBluetoothPbap.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothPbap proxy object. - * - * @hide - */ - public BluetoothPbap(Context context, ServiceListener listener, BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - } - - /** @hide */ - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); - } - } - - /** - * Close the connection to the backing service. - * Other public functions of BluetoothPbap will return default error - * results once close() has been called. Multiple invocations of close() - * are ok. - * - * @hide - */ - public synchronized void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothPbap getService() { - return (IBluetoothPbap) mProfileConnector.getService(); - } - - /** - * {@inheritDoc} - * - * @hide - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getConnectedDevices() { - log("getConnectedDevices()"); - final IBluetoothPbap service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - return new ArrayList<BluetoothDevice>(); - } - try { - return Attributable.setAttributionSource( - service.getConnectedDevices(mAttributionSource), mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - return new ArrayList<BluetoothDevice>(); - } - - /** - * {@inheritDoc} - * - * @hide - */ - @SystemApi - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { - log("getConnectionState: device=" + device); - try { - final IBluetoothPbap service = getService(); - if (service != null && isEnabled() && isValidDevice(device)) { - return service.getConnectionState(device, mAttributionSource); - } - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - } - return BluetoothProfile.STATE_DISCONNECTED; - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - return BluetoothProfile.STATE_DISCONNECTED; - } - - /** - * {@inheritDoc} - * - * @hide - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states)); - final IBluetoothPbap service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - return new ArrayList<BluetoothDevice>(); - } - try { - return Attributable.setAttributionSource( - service.getDevicesMatchingConnectionStates(states, mAttributionSource), - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - return new ArrayList<BluetoothDevice>(); - } - - /** - * Set connection policy of the profile and tries to disconnect it if connectionPolicy is - * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} - * - * <p> The device should already be paired. - * Connection policy can be one of: - * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, - * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, - * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - try { - final IBluetoothPbap service = getService(); - if (service != null && isEnabled() - && isValidDevice(device)) { - if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - return false; - } - return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); - } - if (service == null) Log.w(TAG, "Proxy not attached to service"); - return false; - } catch (RemoteException e) { - Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - return false; - } - } - - /** - * Disconnects the current Pbap client (PCE). Currently this call blocks, - * it may soon be made asynchronous. Returns false if this proxy object is - * not currently connected to the Pbap service. - * - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnect(BluetoothDevice device) { - log("disconnect()"); - final IBluetoothPbap service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - return false; - } - try { - service.disconnect(device, mAttributionSource); - return true; - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - return false; - } - - private boolean isEnabled() { - if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; - return false; - } - - private boolean isValidDevice(BluetoothDevice device) { - if (device == null) return false; - - if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; - return false; - } - - private static void log(String msg) { - if (DBG) { - Log.d(TAG, msg); - } - } -} diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java deleted file mode 100644 index e096de8cb829..000000000000 --- a/core/java/android/bluetooth/BluetoothPbapClient.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.content.AttributionSource; -import android.content.Context; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This class provides the APIs to control the Bluetooth PBAP Client Profile. - * - * @hide - */ -public final class BluetoothPbapClient implements BluetoothProfile { - - private static final String TAG = "BluetoothPbapClient"; - private static final boolean DBG = false; - private static final boolean VDBG = false; - - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED"; - - /** There was an error trying to obtain the state */ - public static final int STATE_ERROR = -1; - - public static final int RESULT_FAILURE = 0; - public static final int RESULT_SUCCESS = 1; - /** Connection canceled before completion. */ - public static final int RESULT_CANCELED = 2; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothPbapClient> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.PBAP_CLIENT, - "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) { - @Override - public IBluetoothPbapClient getServiceInterface(IBinder service) { - return IBluetoothPbapClient.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothPbapClient proxy object. - */ - BluetoothPbapClient(Context context, ServiceListener listener, BluetoothAdapter adapter) { - if (DBG) { - Log.d(TAG, "Create BluetoothPbapClient proxy object"); - } - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - } - - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); - } - } - - /** - * Close the connection to the backing service. - * Other public functions of BluetoothPbapClient will return default error - * results once close() has been called. Multiple invocations of close() - * are ok. - */ - public synchronized void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothPbapClient getService() { - return mProfileConnector.getService(); - } - - /** - * Initiate connection. - * Upon successful connection to remote PBAP server the Client will - * attempt to automatically download the users phonebook and call log. - * - * @param device a remote device we want connect to - * @return <code>true</code> if command has been issued successfully; <code>false</code> - * otherwise; - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean connect(BluetoothDevice device) { - if (DBG) { - log("connect(" + device + ") for PBAP Client."); - } - final IBluetoothPbapClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.connect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate disconnect. - * - * @param device Remote Bluetooth Device - * @return false on error, true otherwise - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean disconnect(BluetoothDevice device) { - if (DBG) { - log("disconnect(" + device + ")" + new Exception()); - } - final IBluetoothPbapClient service = getService(); - final boolean defaultValue = true; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnect(device, mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - return true; - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the list of connected devices. - * Currently at most one. - * - * @return list of connected devices - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getConnectedDevices() { - if (DBG) { - log("getConnectedDevices()"); - } - final IBluetoothPbapClient service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the list of devices matching specified states. Currently at most one. - * - * @return list of matching devices - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (DBG) { - log("getDevicesMatchingStates()"); - } - final IBluetoothPbapClient service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get connection state of device - * - * @return device connection state - */ - @Override - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(BluetoothDevice device) { - if (DBG) { - log("getConnectionState(" + device + ")"); - } - final IBluetoothPbapClient service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } - - private boolean isEnabled() { - return mAdapter.isEnabled(); - } - - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) { - log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - } - final IBluetoothPbapClient service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the priority of the profile. - * - * <p> The priority can be any of: - * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { - if (VDBG) { - log("getConnectionPolicy(" + device + ")"); - } - final IBluetoothPbapClient service = getService(); - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } -} diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java deleted file mode 100644 index e047e5d81a9d..000000000000 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2010-2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.IntDef; -import android.annotation.RequiresNoPermission; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.List; - -/** - * Public APIs for the Bluetooth Profiles. - * - * <p> Clients should call {@link BluetoothAdapter#getProfileProxy}, - * to get the Profile Proxy. Each public profile implements this - * interface. - */ -public interface BluetoothProfile { - - /** - * Extra for the connection state intents of the individual profiles. - * - * This extra represents the current connection state of the profile of the - * Bluetooth device. - */ - @SuppressLint("ActionValue") - String EXTRA_STATE = "android.bluetooth.profile.extra.STATE"; - - /** - * Extra for the connection state intents of the individual profiles. - * - * This extra represents the previous connection state of the profile of the - * Bluetooth device. - */ - @SuppressLint("ActionValue") - String EXTRA_PREVIOUS_STATE = - "android.bluetooth.profile.extra.PREVIOUS_STATE"; - - /** The profile is in disconnected state */ - int STATE_DISCONNECTED = 0; - /** The profile is in connecting state */ - int STATE_CONNECTING = 1; - /** The profile is in connected state */ - int STATE_CONNECTED = 2; - /** The profile is in disconnecting state */ - int STATE_DISCONNECTING = 3; - - /** @hide */ - @IntDef({ - STATE_DISCONNECTED, - STATE_CONNECTING, - STATE_CONNECTED, - STATE_DISCONNECTING, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface BtProfileState {} - - /** - * Headset and Handsfree profile - */ - int HEADSET = 1; - - /** - * A2DP profile. - */ - int A2DP = 2; - - /** - * Health Profile - * - * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New - * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt}, - * {@link BluetoothAdapter#listenUsingL2capChannel()}, or - * {@link BluetoothDevice#createL2capChannel(int)} - */ - @Deprecated - int HEALTH = 3; - - /** - * HID Host - * - * @hide - */ - int HID_HOST = 4; - - /** - * PAN Profile - * - * @hide - */ - @SystemApi - int PAN = 5; - - /** - * PBAP - * - * @hide - */ - int PBAP = 6; - - /** - * GATT - */ - int GATT = 7; - - /** - * GATT_SERVER - */ - int GATT_SERVER = 8; - - /** - * MAP Profile - * - * @hide - */ - int MAP = 9; - - /* - * SAP Profile - * @hide - */ - int SAP = 10; - - /** - * A2DP Sink Profile - * - * @hide - */ - @SystemApi - int A2DP_SINK = 11; - - /** - * AVRCP Controller Profile - * - * @hide - */ - @SystemApi - int AVRCP_CONTROLLER = 12; - - /** - * AVRCP Target Profile - * - * @hide - */ - int AVRCP = 13; - - /** - * Headset Client - HFP HF Role - * - * @hide - */ - @SystemApi - int HEADSET_CLIENT = 16; - - /** - * PBAP Client - * - * @hide - */ - @SystemApi - int PBAP_CLIENT = 17; - - /** - * MAP Messaging Client Equipment (MCE) - * - * @hide - */ - @SystemApi - int MAP_CLIENT = 18; - - /** - * HID Device - */ - int HID_DEVICE = 19; - - /** - * Object Push Profile (OPP) - * - * @hide - */ - int OPP = 20; - - /** - * Hearing Aid Device - * - */ - int HEARING_AID = 21; - - /** - * LE Audio Device - * - */ - int LE_AUDIO = 22; - - /** - * Volume Control profile - * - * @hide - */ - @SystemApi - int VOLUME_CONTROL = 23; - - /** - * @hide - * Media Control Profile server - * - */ - int MCP_SERVER = 24; - - /** - * Coordinated Set Identification Profile set coordinator - * - */ - int CSIP_SET_COORDINATOR = 25; - - /** - * LE Audio Broadcast Source - * - * @hide - */ - int LE_AUDIO_BROADCAST = 26; - - /** - * 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; - - /** - * Default priority for devices that we try to auto-connect to and - * and allow incoming connections for the profile - * - * @hide - **/ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - int PRIORITY_AUTO_CONNECT = 1000; - - /** - * Default priority for devices that allow incoming - * and outgoing connections for the profile - * - * @hide - * @deprecated Replaced with {@link #CONNECTION_POLICY_ALLOWED} - **/ - @Deprecated - @SystemApi - int PRIORITY_ON = 100; - - /** - * Default priority for devices that does not allow incoming - * connections and outgoing connections for the profile. - * - * @hide - * @deprecated Replaced with {@link #CONNECTION_POLICY_FORBIDDEN} - **/ - @Deprecated - @SystemApi - int PRIORITY_OFF = 0; - - /** - * Default priority when not set or when the device is unpaired - * - * @hide - */ - @UnsupportedAppUsage - int PRIORITY_UNDEFINED = -1; - - /** @hide */ - @IntDef(prefix = "CONNECTION_POLICY_", value = {CONNECTION_POLICY_ALLOWED, - CONNECTION_POLICY_FORBIDDEN, CONNECTION_POLICY_UNKNOWN}) - @Retention(RetentionPolicy.SOURCE) - public @interface ConnectionPolicy{} - - /** - * Default connection policy for devices that allow incoming and outgoing connections - * for the profile - * - * @hide - **/ - @SystemApi - int CONNECTION_POLICY_ALLOWED = 100; - - /** - * Default connection policy for devices that do not allow incoming or outgoing connections - * for the profile. - * - * @hide - **/ - @SystemApi - int CONNECTION_POLICY_FORBIDDEN = 0; - - /** - * Default connection policy when not set or when the device is unpaired - * - * @hide - */ - @SystemApi - int CONNECTION_POLICY_UNKNOWN = -1; - - /** - * Get connected devices for this specific profile. - * - * <p> Return the set of devices which are in state {@link #STATE_CONNECTED} - * - * @return List of devices. The list will be empty on error. - */ - public List<BluetoothDevice> getConnectedDevices(); - - /** - * Get a list of devices that match any of the given connection - * states. - * - * <p> If none of the devices match any of the given states, - * an empty list will be returned. - * - * @param states Array of states. States can be one of {@link #STATE_CONNECTED}, {@link - * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, - * @return List of devices. The list will be empty on error. - */ - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states); - - /** - * Get the current connection state of the profile - * - * @param device Remote bluetooth device. - * @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link - * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} - */ - @BtProfileState int getConnectionState(BluetoothDevice device); - - /** - * An interface for notifying BluetoothProfile IPC clients when they have - * been connected or disconnected to the service. - */ - public interface ServiceListener { - /** - * Called to notify the client when the proxy object has been - * connected to the service. - * - * @param profile - One of {@link #HEADSET} or {@link #A2DP} - * @param proxy - One of {@link BluetoothHeadset} or {@link BluetoothA2dp} - */ - @RequiresNoPermission - public void onServiceConnected(int profile, BluetoothProfile proxy); - - /** - * Called to notify the client that this proxy object has been - * disconnected from the service. - * - * @param profile - One of {@link #HEADSET} or {@link #A2DP} - */ - @RequiresNoPermission - public void onServiceDisconnected(int profile); - } - - /** - * Convert an integer value of connection state into human readable string - * - * @param connectionState - One of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, or {@link #STATE_DISCONNECTED} - * @return a string representation of the connection state, STATE_UNKNOWN if the state - * is not defined - * @hide - */ - static String getConnectionStateName(int connectionState) { - switch (connectionState) { - case STATE_DISCONNECTED: - return "STATE_DISCONNECTED"; - case STATE_CONNECTING: - return "STATE_CONNECTING"; - case STATE_CONNECTED: - return "STATE_CONNECTED"; - case STATE_DISCONNECTING: - return "STATE_DISCONNECTING"; - default: - return "STATE_UNKNOWN"; - } - } - - /** - * Convert an integer value of profile ID into human readable string - * - * @param profile profile ID - * @return profile name as String, UNKOWN_PROFILE if the profile ID is not defined. - * @hide - */ - static String getProfileName(int profile) { - switch(profile) { - case HEADSET: - return "HEADSET"; - case A2DP: - return "A2DP"; - case HID_HOST: - return "HID_HOST"; - case PAN: - return "PAN"; - case PBAP: - return "PBAP"; - case GATT: - return "GATT"; - case GATT_SERVER: - return "GATT_SERVER"; - case MAP: - return "MAP"; - case SAP: - return "SAP"; - case A2DP_SINK: - return "A2DP_SINK"; - case AVRCP_CONTROLLER: - return "AVRCP_CONTROLLER"; - case AVRCP: - return "AVRCP"; - case HEADSET_CLIENT: - return "HEADSET_CLIENT"; - case PBAP_CLIENT: - return "PBAP_CLIENT"; - case MAP_CLIENT: - return "MAP_CLIENT"; - case HID_DEVICE: - return "HID_DEVICE"; - case OPP: - return "OPP"; - case HEARING_AID: - return "HEARING_AID"; - case LE_AUDIO: - return "LE_AUDIO"; - default: - return "UNKNOWN_PROFILE"; - } - } -} diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java deleted file mode 100644 index 79373f1a32ec..000000000000 --- a/core/java/android/bluetooth/BluetoothProfileConnector.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SuppressLint; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.Build; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.UserHandle; -import android.util.CloseGuard; -import android.util.Log; - -import java.util.List; -/** - * Connector for Bluetooth profile proxies to bind manager service and - * profile services - * @param <T> The Bluetooth profile interface for this connection. - * @hide - */ -@SuppressLint("AndroidFrameworkBluetoothPermission") -public abstract class BluetoothProfileConnector<T> { - private final CloseGuard mCloseGuard = new CloseGuard(); - private final int mProfileId; - private BluetoothProfile.ServiceListener mServiceListener; - private final BluetoothProfile mProfileProxy; - private Context mContext; - private final String mProfileName; - private final String mServiceName; - private volatile T mService; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (up) { - doBind(); - } else { - doUnbind(); - } - } - }; - - private @Nullable ComponentName resolveSystemService(@NonNull Intent intent, - @NonNull PackageManager pm, @PackageManager.ComponentInfoFlags int flags) { - List<ResolveInfo> results = pm.queryIntentServices(intent, flags); - if (results == null) { - return null; - } - ComponentName comp = null; - for (int i = 0; i < results.size(); i++) { - ResolveInfo ri = results.get(i); - if ((ri.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { - continue; - } - ComponentName foundComp = new ComponentName(ri.serviceInfo.applicationInfo.packageName, - ri.serviceInfo.name); - if (comp != null) { - throw new IllegalStateException("Multiple system services handle " + intent - + ": " + comp + ", " + foundComp); - } - comp = foundComp; - } - return comp; - } - - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - logDebug("Proxy object connected"); - mService = getServiceInterface(service); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(mProfileId, mProfileProxy); - } - } - - public void onServiceDisconnected(ComponentName className) { - logDebug("Proxy object disconnected"); - doUnbind(); - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(mProfileId); - } - } - }; - - BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName, - String serviceName) { - mProfileId = profileId; - mProfileProxy = profile; - mProfileName = profileName; - mServiceName = serviceName; - } - - /** {@hide} */ - @Override - public void finalize() { - mCloseGuard.warnIfOpen(); - doUnbind(); - } - - @SuppressLint("AndroidFrameworkRequiresPermission") - private boolean doBind() { - synchronized (mConnection) { - if (mService == null) { - logDebug("Binding service..."); - mCloseGuard.open("doUnbind"); - try { - Intent intent = new Intent(mServiceName); - ComponentName comp = resolveSystemService(intent, mContext.getPackageManager(), - 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT)) { - logError("Could not bind to Bluetooth Service with " + intent); - return false; - } - } catch (SecurityException se) { - logError("Failed to bind service. " + se); - return false; - } - } - } - return true; - } - - private void doUnbind() { - synchronized (mConnection) { - if (mService != null) { - logDebug("Unbinding service..."); - mCloseGuard.close(); - try { - mContext.unbindService(mConnection); - } catch (IllegalArgumentException ie) { - logError("Unable to unbind service: " + ie); - } finally { - mService = null; - } - } - } - } - - void connect(Context context, BluetoothProfile.ServiceListener listener) { - mContext = context; - mServiceListener = listener; - IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); - - // Preserve legacy compatibility where apps were depending on - // registerStateChangeCallback() performing a permissions check which - // has been relaxed in modern platform versions - if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R - && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Need BLUETOOTH permission"); - } - - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - logError("Failed to register state change callback. " + re); - } - } - doBind(); - } - - void disconnect() { - mServiceListener = null; - IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - logError("Failed to unregister state change callback" + re); - } - } - doUnbind(); - } - - T getService() { - return mService; - } - - /** - * This abstract function is used to implement method to get the - * connected Bluetooth service interface. - * @param service the connected binder service. - * @return T the binder interface of {@code service}. - * @hide - */ - public abstract T getServiceInterface(IBinder service); - - private void logDebug(String log) { - Log.d(mProfileName, log); - } - - private void logError(String log) { - Log.e(mProfileName, log); - } -} diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java deleted file mode 100644 index 808fa3913316..000000000000 --- a/core/java/android/bluetooth/BluetoothSap.java +++ /dev/null @@ -1,491 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.Manifest; -import android.annotation.RequiresNoPermission; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; -import android.content.Context; -import android.os.Build; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This class provides the APIs to control the Bluetooth SIM - * Access Profile (SAP). - * - * <p>BluetoothSap is a proxy object for controlling the Bluetooth - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothSap proxy object. - * - * <p>Each method is protected with its appropriate permission. - * - * @hide - */ -public final class BluetoothSap implements BluetoothProfile { - - private static final String TAG = "BluetoothSap"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** - * Intent used to broadcast the change in connection state of the profile. - * - * <p>This intent will have 4 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; - - /** - * There was an error trying to obtain the state. - * - * @hide - */ - public static final int STATE_ERROR = -1; - - /** - * Connection state change succceeded. - * - * @hide - */ - public static final int RESULT_SUCCESS = 1; - - /** - * Connection canceled before completion. - * - * @hide - */ - public static final int RESULT_CANCELED = 2; - - private final BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothSap> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.SAP, - "BluetoothSap", IBluetoothSap.class.getName()) { - @Override - public IBluetoothSap getServiceInterface(IBinder service) { - return IBluetoothSap.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothSap proxy object. - */ - /* package */ BluetoothSap(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - if (DBG) Log.d(TAG, "Create BluetoothSap proxy object"); - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - } - - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); - } - } - - /** - * Close the connection to the backing service. - * Other public functions of BluetoothSap will return default error - * results once close() has been called. Multiple invocations of close() - * are ok. - * - * @hide - */ - public synchronized void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothSap getService() { - return mProfileConnector.getService(); - } - - /** - * Get the current state of the BluetoothSap service. - * - * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not - * connected to the Sap service. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getState() { - if (VDBG) log("getState()"); - final IBluetoothSap service = getService(); - final int defaultValue = BluetoothSap.STATE_ERROR; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getState(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the currently connected remote Bluetooth device (PCE). - * - * @return The remote Bluetooth device, or null if not in connected or connecting state, or if - * this proxy object is not connected to the Sap service. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public BluetoothDevice getClient() { - if (VDBG) log("getClient()"); - final IBluetoothSap service = getService(); - final BluetoothDevice defaultValue = null; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<BluetoothDevice> recv = - new SynchronousResultReceiver(); - service.getClient(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Returns true if the specified Bluetooth device is connected. - * Returns false if not connected, or if this proxy object is not - * currently connected to the Sap service. - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isConnected(BluetoothDevice device) { - if (VDBG) log("isConnected(" + device + ")"); - final IBluetoothSap service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isConnected(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Initiate connection. Initiation of outgoing connections is not - * supported for SAP server. - * - * @hide - */ - @RequiresNoPermission - public boolean connect(BluetoothDevice device) { - if (DBG) log("connect(" + device + ")" + "not supported for SAPS"); - return false; - } - - /** - * Initiate disconnect. - * - * @param device Remote Bluetooth Device - * @return false on error, true otherwise - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnect(BluetoothDevice device) { - if (DBG) log("disconnect(" + device + ")"); - final IBluetoothSap service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.disconnect(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the list of connected devices. Currently at most one. - * - * @return list of connected devices - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); - final IBluetoothSap service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the list of devices matching specified states. Currently at most one. - * - * @return list of matching devices - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothSap service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get connection state of device - * - * @return device connection state - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothSap service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}, - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setPriority(BluetoothDevice device, int priority) { - if (DBG) log("setPriority(" + device + ", " + priority + ")"); - return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothSap service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the priority of the profile. - * - * <p> The priority can be any of: - * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public int getPriority(BluetoothDevice device) { - if (VDBG) log("getPriority(" + device + ")"); - return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - final IBluetoothSap service = getService(); - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } - - private boolean isEnabled() { - return mAdapter.isEnabled(); - } - - private static boolean isValidDevice(BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } -} diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java deleted file mode 100644 index bb4e35483fea..000000000000 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (C) 2009 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.bluetooth; - -import android.annotation.SuppressLint; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Handler; -import android.os.ParcelUuid; -import android.util.Log; - -import java.io.Closeable; -import java.io.IOException; - -/** - * A listening Bluetooth socket. - * - * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets: - * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server - * side, use a {@link BluetoothServerSocket} to create a listening server - * socket. When a connection is accepted by the {@link BluetoothServerSocket}, - * it will return a new {@link BluetoothSocket} to manage the connection. - * On the client side, use a single {@link BluetoothSocket} to both initiate - * an outgoing connection and to manage the connection. - * - * <p>For Bluetooth BR/EDR, the most common type of socket is RFCOMM, which is the type supported by - * the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth BR/EDR. It - * is also known as the Serial Port Profile (SPP). To create a listening - * {@link BluetoothServerSocket} that's ready for incoming Bluetooth BR/EDR connections, use {@link - * BluetoothAdapter#listenUsingRfcommWithServiceRecord - * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. - * - * <p>For Bluetooth LE, the socket uses LE Connection-oriented Channel (CoC). LE CoC is a - * connection-oriented, streaming transport over Bluetooth LE and has a credit-based flow control. - * Correspondingly, use {@link BluetoothAdapter#listenUsingL2capChannel - * BluetoothAdapter.listenUsingL2capChannel()} to create a listening {@link BluetoothServerSocket} - * that's ready for incoming Bluetooth LE CoC connections. For LE CoC, you can use {@link #getPsm()} - * to get the protocol/service multiplexer (PSM) value that the peer needs to use to connect to your - * socket. - * - * <p> After the listening {@link BluetoothServerSocket} is created, call {@link #accept()} to - * listen for incoming connection requests. This call will block until a connection is established, - * at which point, it will return a {@link BluetoothSocket} to manage the connection. Once the - * {@link BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on the {@link - * BluetoothServerSocket} when it's no longer needed for accepting - * connections. Closing the {@link BluetoothServerSocket} will <em>not</em> close the returned - * {@link BluetoothSocket}. - * - * <p>{@link BluetoothServerSocket} is thread - * safe. In particular, {@link #close} will always immediately abort ongoing - * operations and close the server socket. - * - * <div class="special reference"> - * <h3>Developer Guides</h3> - * <p>For more information about using Bluetooth, read the - * <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p> - * </div> - * - * {@see BluetoothSocket} - */ -@SuppressLint("AndroidFrameworkBluetoothPermission") -public final class BluetoothServerSocket implements Closeable { - - private static final String TAG = "BluetoothServerSocket"; - private static final boolean DBG = false; - @UnsupportedAppUsage(publicAlternatives = "Use public {@link BluetoothServerSocket} API " - + "instead.") - /*package*/ final BluetoothSocket mSocket; - private Handler mHandler; - private int mMessage; - private int mChannel; - - /** - * Construct a socket for incoming connections. - * - * @param type type of socket - * @param auth require the remote device to be authenticated - * @param encrypt require the connection to be encrypted - * @param port remote port - * @throws IOException On error, for example Bluetooth not available, or insufficient - * privileges - */ - /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port) - throws IOException { - mChannel = port; - mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null); - if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - mSocket.setExcludeSdp(true); - } - } - - /** - * Construct a socket for incoming connections. - * - * @param type type of socket - * @param auth require the remote device to be authenticated - * @param encrypt require the connection to be encrypted - * @param port remote port - * @param mitm enforce person-in-the-middle protection for authentication. - * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection - * @throws IOException On error, for example Bluetooth not available, or insufficient - * privileges - */ - /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port, - boolean mitm, boolean min16DigitPin) - throws IOException { - mChannel = port; - mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null, mitm, - min16DigitPin); - if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - mSocket.setExcludeSdp(true); - } - } - - /** - * Construct a socket for incoming connections. - * - * @param type type of socket - * @param auth require the remote device to be authenticated - * @param encrypt require the connection to be encrypted - * @param uuid uuid - * @throws IOException On error, for example Bluetooth not available, or insufficient - * privileges - */ - /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid) - throws IOException { - mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, -1, uuid); - // TODO: This is the same as mChannel = -1 - is this intentional? - mChannel = mSocket.getPort(); - } - - - /** - * Block until a connection is established. - * <p>Returns a connected {@link BluetoothSocket} on successful connection. - * <p>Once this call returns, it can be called again to accept subsequent - * incoming connections. - * <p>{@link #close} can be used to abort this call from another thread. - * - * @return a connected {@link BluetoothSocket} - * @throws IOException on error, for example this call was aborted, or timeout - */ - public BluetoothSocket accept() throws IOException { - return accept(-1); - } - - /** - * Block until a connection is established, with timeout. - * <p>Returns a connected {@link BluetoothSocket} on successful connection. - * <p>Once this call returns, it can be called again to accept subsequent - * incoming connections. - * <p>{@link #close} can be used to abort this call from another thread. - * - * @return a connected {@link BluetoothSocket} - * @throws IOException on error, for example this call was aborted, or timeout - */ - public BluetoothSocket accept(int timeout) throws IOException { - return mSocket.accept(timeout); - } - - /** - * Immediately close this socket, and release all associated resources. - * <p>Causes blocked calls on this socket in other threads to immediately - * throw an IOException. - * <p>Closing the {@link BluetoothServerSocket} will <em>not</em> - * close any {@link BluetoothSocket} received from {@link #accept()}. - */ - public void close() throws IOException { - if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel); - synchronized (this) { - if (mHandler != null) { - mHandler.obtainMessage(mMessage).sendToTarget(); - } - } - mSocket.close(); - } - - /*package*/ - synchronized void setCloseHandler(Handler handler, int message) { - mHandler = handler; - mMessage = message; - } - - /*package*/ void setServiceName(String serviceName) { - mSocket.setServiceName(serviceName); - } - - /** - * Returns the channel on which this socket is bound. - * - * @hide - */ - public int getChannel() { - return mChannel; - } - - /** - * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP - * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the - * {@link BluetoothAdapter#listenUsingL2capChannel()} or {@link - * BluetoothAdapter#listenUsingInsecureL2capChannel()}. The returned value is undefined if this - * method is called on non-L2CAP server sockets. - * - * @return the assigned PSM or LE_PSM value depending on transport - */ - public int getPsm() { - return mChannel; - } - - /** - * Sets the channel on which future sockets are bound. - * Currently used only when a channel is auto generated. - */ - /*package*/ void setChannel(int newChannel) { - /* TODO: From a design/architecture perspective this is wrong. - * The bind operation should be conducted through this class - * and the resulting port should be kept in mChannel, and - * not set from BluetoothAdapter. */ - if (mSocket != null) { - if (mSocket.getPort() != newChannel) { - Log.w(TAG, "The port set is different that the underlying port. mSocket.getPort(): " - + mSocket.getPort() + " requested newChannel: " + newChannel); - } - } - mChannel = newChannel; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("ServerSocket: Type: "); - switch (mSocket.getConnectionType()) { - case BluetoothSocket.TYPE_RFCOMM: { - sb.append("TYPE_RFCOMM"); - break; - } - case BluetoothSocket.TYPE_L2CAP: { - sb.append("TYPE_L2CAP"); - break; - } - case BluetoothSocket.TYPE_L2CAP_LE: { - sb.append("TYPE_L2CAP_LE"); - break; - } - case BluetoothSocket.TYPE_SCO: { - sb.append("TYPE_SCO"); - break; - } - } - sb.append(" Channel: ").append(mChannel); - return sb.toString(); - } -} diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java deleted file mode 100644 index db5b75148e88..000000000000 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ /dev/null @@ -1,809 +0,0 @@ -/* - * Copyright (C) 2012 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.bluetooth; - -import android.annotation.RequiresNoPermission; -import android.annotation.RequiresPermission; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.compat.annotation.UnsupportedAppUsage; -import android.net.LocalSocket; -import android.os.Build; -import android.os.ParcelFileDescriptor; -import android.os.ParcelUuid; -import android.os.RemoteException; -import android.util.Log; - -import java.io.Closeable; -import java.io.FileDescriptor; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; -import java.util.Locale; -import java.util.UUID; - -/** - * A connected or connecting Bluetooth socket. - * - * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets: - * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server - * side, use a {@link BluetoothServerSocket} to create a listening server - * socket. When a connection is accepted by the {@link BluetoothServerSocket}, - * it will return a new {@link BluetoothSocket} to manage the connection. - * On the client side, use a single {@link BluetoothSocket} to both initiate - * an outgoing connection and to manage the connection. - * - * <p>The most common type of Bluetooth socket is RFCOMM, which is the type - * supported by the Android APIs. RFCOMM is a connection-oriented, streaming - * transport over Bluetooth. It is also known as the Serial Port Profile (SPP). - * - * <p>To create a {@link BluetoothSocket} for connecting to a known device, use - * {@link BluetoothDevice#createRfcommSocketToServiceRecord - * BluetoothDevice.createRfcommSocketToServiceRecord()}. - * Then call {@link #connect()} to attempt a connection to the remote device. - * This call will block until a connection is established or the connection - * fails. - * - * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the - * {@link BluetoothServerSocket} documentation. - * - * <p>Once the socket is connected, whether initiated as a client or accepted - * as a server, open the IO streams by calling {@link #getInputStream} and - * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream} - * and {@link java.io.OutputStream} objects, respectively, which are - * automatically connected to the socket. - * - * <p>{@link BluetoothSocket} is thread - * safe. In particular, {@link #close} will always immediately abort ongoing - * operations and close the socket. - * - * <div class="special reference"> - * <h3>Developer Guides</h3> - * <p>For more information about using Bluetooth, read the - * <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p> - * </div> - * - * {@see BluetoothServerSocket} - * {@see java.io.InputStream} - * {@see java.io.OutputStream} - */ -public final class BluetoothSocket implements Closeable { - private static final String TAG = "BluetoothSocket"; - private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); - private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); - - /** @hide */ - public static final int MAX_RFCOMM_CHANNEL = 30; - /*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF; - - /** RFCOMM socket */ - public static final int TYPE_RFCOMM = 1; - - /** SCO socket */ - public static final int TYPE_SCO = 2; - - /** L2CAP socket */ - public static final int TYPE_L2CAP = 3; - - /** L2CAP socket on BR/EDR transport - * @hide - */ - public static final int TYPE_L2CAP_BREDR = TYPE_L2CAP; - - /** L2CAP socket on LE transport - * @hide - */ - public static final int TYPE_L2CAP_LE = 4; - - /*package*/ static final int EBADFD = 77; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - /*package*/ static final int EADDRINUSE = 98; - - /*package*/ static final int SEC_FLAG_ENCRYPT = 1; - /*package*/ static final int SEC_FLAG_AUTH = 1 << 1; - /*package*/ static final int BTSOCK_FLAG_NO_SDP = 1 << 2; - /*package*/ static final int SEC_FLAG_AUTH_MITM = 1 << 3; - /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT = 1 << 4; - - private final int mType; /* one of TYPE_RFCOMM etc */ - private BluetoothDevice mDevice; /* remote device */ - private String mAddress; /* remote address */ - private final boolean mAuth; - private final boolean mEncrypt; - private final BluetoothInputStream mInputStream; - private final BluetoothOutputStream mOutputStream; - private final ParcelUuid mUuid; - /** when true no SPP SDP record will be created */ - private boolean mExcludeSdp = false; - /** when true Person-in-the-middle protection will be enabled */ - private boolean mAuthMitm = false; - /** Minimum 16 digit pin for sec mode 2 connections */ - private boolean mMin16DigitPin = false; - @UnsupportedAppUsage(publicAlternatives = "Use {@link BluetoothSocket} public API instead.") - private ParcelFileDescriptor mPfd; - @UnsupportedAppUsage - private LocalSocket mSocket; - private InputStream mSocketIS; - private OutputStream mSocketOS; - @UnsupportedAppUsage - private int mPort; /* RFCOMM channel or L2CAP psm */ - private int mFd; - private String mServiceName; - private static final int PROXY_CONNECTION_TIMEOUT = 5000; - - private static final int SOCK_SIGNAL_SIZE = 20; - - private ByteBuffer mL2capBuffer = null; - private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer. - private int mMaxRxPacketSize = 0; // The l2cap maximum packet size that can be received. - - private enum SocketState { - INIT, - CONNECTED, - LISTENING, - CLOSED, - } - - /** prevents all native calls after destroyNative() */ - private volatile SocketState mSocketState; - - /** protects mSocketState */ - //private final ReentrantReadWriteLock mLock; - - /** - * Construct a BluetoothSocket. - * - * @param type type of socket - * @param fd fd to use for connected socket, or -1 for a new socket - * @param auth require the remote device to be authenticated - * @param encrypt require the connection to be encrypted - * @param device remote device that this socket can connect to - * @param port remote port - * @param uuid SDP uuid - * @throws IOException On error, for example Bluetooth not available, or insufficient - * privileges - */ - /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, - BluetoothDevice device, int port, ParcelUuid uuid) throws IOException { - this(type, fd, auth, encrypt, device, port, uuid, false, false); - } - - /** - * Construct a BluetoothSocket. - * - * @param type type of socket - * @param fd fd to use for connected socket, or -1 for a new socket - * @param auth require the remote device to be authenticated - * @param encrypt require the connection to be encrypted - * @param device remote device that this socket can connect to - * @param port remote port - * @param uuid SDP uuid - * @param mitm enforce person-in-the-middle protection. - * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection - * @throws IOException On error, for example Bluetooth not available, or insufficient - * privileges - */ - /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, - BluetoothDevice device, int port, ParcelUuid uuid, boolean mitm, boolean min16DigitPin) - throws IOException { - if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type); - if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1 - && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - if (port < 1 || port > MAX_RFCOMM_CHANNEL) { - throw new IOException("Invalid RFCOMM channel: " + port); - } - } - if (uuid != null) { - mUuid = uuid; - } else { - mUuid = new ParcelUuid(new UUID(0, 0)); - } - mType = type; - mAuth = auth; - mAuthMitm = mitm; - mMin16DigitPin = min16DigitPin; - mEncrypt = encrypt; - mDevice = device; - mPort = port; - mFd = fd; - - mSocketState = SocketState.INIT; - - if (device == null) { - // Server socket - mAddress = BluetoothAdapter.getDefaultAdapter().getAddress(); - } else { - // Remote socket - mAddress = device.getAddress(); - } - mInputStream = new BluetoothInputStream(this); - mOutputStream = new BluetoothOutputStream(this); - } - - private BluetoothSocket(BluetoothSocket s) { - if (VDBG) Log.d(TAG, "Creating new Private BluetoothSocket of type: " + s.mType); - mUuid = s.mUuid; - mType = s.mType; - mAuth = s.mAuth; - mEncrypt = s.mEncrypt; - mPort = s.mPort; - mInputStream = new BluetoothInputStream(this); - mOutputStream = new BluetoothOutputStream(this); - mMaxRxPacketSize = s.mMaxRxPacketSize; - mMaxTxPacketSize = s.mMaxTxPacketSize; - - mServiceName = s.mServiceName; - mExcludeSdp = s.mExcludeSdp; - mAuthMitm = s.mAuthMitm; - mMin16DigitPin = s.mMin16DigitPin; - } - - private BluetoothSocket acceptSocket(String remoteAddr) throws IOException { - BluetoothSocket as = new BluetoothSocket(this); - as.mSocketState = SocketState.CONNECTED; - FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors(); - if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + Arrays.toString(fds)); - if (fds == null || fds.length != 1) { - Log.e(TAG, "socket fd passed from stack failed, fds: " + Arrays.toString(fds)); - as.close(); - throw new IOException("bt socket acept failed"); - } - - as.mPfd = ParcelFileDescriptor.dup(fds[0]); - as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]); - as.mSocketIS = as.mSocket.getInputStream(); - as.mSocketOS = as.mSocket.getOutputStream(); - as.mAddress = remoteAddr; - as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr); - return as; - } - - /** - * Construct a BluetoothSocket from address. Used by native code. - * - * @param type type of socket - * @param fd fd to use for connected socket, or -1 for a new socket - * @param auth require the remote device to be authenticated - * @param encrypt require the connection to be encrypted - * @param address remote device that this socket can connect to - * @param port remote port - * @throws IOException On error, for example Bluetooth not available, or insufficient - * privileges - */ - private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address, - int port) throws IOException { - this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null, false, false); - } - - /** @hide */ - @Override - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); - } - } - - private int getSecurityFlags() { - int flags = 0; - if (mAuth) { - flags |= SEC_FLAG_AUTH; - } - if (mEncrypt) { - flags |= SEC_FLAG_ENCRYPT; - } - if (mExcludeSdp) { - flags |= BTSOCK_FLAG_NO_SDP; - } - if (mAuthMitm) { - flags |= SEC_FLAG_AUTH_MITM; - } - if (mMin16DigitPin) { - flags |= SEC_FLAG_AUTH_16_DIGIT; - } - return flags; - } - - /** - * Get the remote device this socket is connecting, or connected, to. - * - * @return remote device - */ - @RequiresNoPermission - public BluetoothDevice getRemoteDevice() { - return mDevice; - } - - /** - * Get the input stream associated with this socket. - * <p>The input stream will be returned even if the socket is not yet - * connected, but operations on that stream will throw IOException until - * the associated socket is connected. - * - * @return InputStream - */ - @RequiresNoPermission - public InputStream getInputStream() throws IOException { - return mInputStream; - } - - /** - * Get the output stream associated with this socket. - * <p>The output stream will be returned even if the socket is not yet - * connected, but operations on that stream will throw IOException until - * the associated socket is connected. - * - * @return OutputStream - */ - @RequiresNoPermission - public OutputStream getOutputStream() throws IOException { - return mOutputStream; - } - - /** - * Get the connection status of this socket, ie, whether there is an active connection with - * remote device. - * - * @return true if connected false if not connected - */ - @RequiresNoPermission - public boolean isConnected() { - return mSocketState == SocketState.CONNECTED; - } - - /*package*/ void setServiceName(String name) { - mServiceName = name; - } - - /** - * Attempt to connect to a remote device. - * <p>This method will block until a connection is made or the connection - * fails. If this method returns without an exception then this socket - * is now connected. - * <p>Creating new connections to - * remote Bluetooth devices should not be attempted while device discovery - * is in progress. Device discovery is a heavyweight procedure on the - * Bluetooth adapter and will significantly slow a device connection. - * Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing - * discovery. Discovery is not managed by the Activity, - * but is run as a system service, so an application should always call - * {@link BluetoothAdapter#cancelDiscovery()} even if it - * did not directly request a discovery, just to be sure. - * <p>{@link #close} can be used to abort this call from another thread. - * - * @throws IOException on error, for example connection failure - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void connect() throws IOException { - if (mDevice == null) throw new IOException("Connect is called on null device"); - - try { - if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); - IBluetooth bluetoothProxy = - BluetoothAdapter.getDefaultAdapter().getBluetoothService(); - if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); - mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType, - mUuid, mPort, getSecurityFlags()); - synchronized (this) { - if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd); - if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); - if (mPfd == null) throw new IOException("bt socket connect failed"); - FileDescriptor fd = mPfd.getFileDescriptor(); - mSocket = LocalSocket.createConnectedLocalSocket(fd); - mSocketIS = mSocket.getInputStream(); - mSocketOS = mSocket.getOutputStream(); - } - int channel = readInt(mSocketIS); - if (channel <= 0) { - throw new IOException("bt socket connect failed"); - } - mPort = channel; - waitSocketSignal(mSocketIS); - synchronized (this) { - if (mSocketState == SocketState.CLOSED) { - throw new IOException("bt socket closed"); - } - mSocketState = SocketState.CONNECTED; - } - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - throw new IOException("unable to send RPC: " + e.getMessage()); - } - } - - /** - * Currently returns unix errno instead of throwing IOException, - * so that BluetoothAdapter can check the error code for EADDRINUSE - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - /*package*/ int bindListen() { - int ret; - if (mSocketState == SocketState.CLOSED) return EBADFD; - IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(); - if (bluetoothProxy == null) { - Log.e(TAG, "bindListen fail, reason: bluetooth is off"); - return -1; - } - try { - if (DBG) Log.d(TAG, "bindListen(): mPort=" + mPort + ", mType=" + mType); - mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName, - mUuid, mPort, getSecurityFlags()); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - return -1; - } - - // read out port number - try { - synchronized (this) { - if (DBG) { - Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + mPfd); - } - if (mSocketState != SocketState.INIT) return EBADFD; - if (mPfd == null) return -1; - FileDescriptor fd = mPfd.getFileDescriptor(); - if (fd == null) { - Log.e(TAG, "bindListen(), null file descriptor"); - return -1; - } - - if (DBG) Log.d(TAG, "bindListen(), Create LocalSocket"); - mSocket = LocalSocket.createConnectedLocalSocket(fd); - if (DBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream()"); - mSocketIS = mSocket.getInputStream(); - mSocketOS = mSocket.getOutputStream(); - } - if (DBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS); - int channel = readInt(mSocketIS); - synchronized (this) { - if (mSocketState == SocketState.INIT) { - mSocketState = SocketState.LISTENING; - } - } - if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort); - if (mPort <= -1) { - mPort = channel; - } // else ASSERT(mPort == channel) - ret = 0; - } catch (IOException e) { - if (mPfd != null) { - try { - mPfd.close(); - } catch (IOException e1) { - Log.e(TAG, "bindListen, close mPfd: " + e1); - } - mPfd = null; - } - Log.e(TAG, "bindListen, fail to get port number, exception: " + e); - return -1; - } - return ret; - } - - /*package*/ BluetoothSocket accept(int timeout) throws IOException { - BluetoothSocket acceptedSocket; - if (mSocketState != SocketState.LISTENING) { - throw new IOException("bt socket is not in listen state"); - } - if (timeout > 0) { - Log.d(TAG, "accept() set timeout (ms):" + timeout); - mSocket.setSoTimeout(timeout); - } - String RemoteAddr = waitSocketSignal(mSocketIS); - if (timeout > 0) { - mSocket.setSoTimeout(0); - } - synchronized (this) { - if (mSocketState != SocketState.LISTENING) { - throw new IOException("bt socket is not in listen state"); - } - acceptedSocket = acceptSocket(RemoteAddr); - //quick drop the reference of the file handle - } - return acceptedSocket; - } - - /*package*/ int available() throws IOException { - if (VDBG) Log.d(TAG, "available: " + mSocketIS); - return mSocketIS.available(); - } - - /*package*/ int read(byte[] b, int offset, int length) throws IOException { - int ret = 0; - if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); - if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) { - int bytesToRead = length; - if (VDBG) { - Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length - + "mL2capBuffer= " + mL2capBuffer); - } - if (mL2capBuffer == null) { - createL2capRxBuffer(); - } - if (mL2capBuffer.remaining() == 0) { - if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling..."); - if (fillL2capRxBuffer() == -1) { - return -1; - } - } - if (bytesToRead > mL2capBuffer.remaining()) { - bytesToRead = mL2capBuffer.remaining(); - } - if (VDBG) { - Log.v(TAG, "get(): offset: " + offset - + " bytesToRead: " + bytesToRead); - } - mL2capBuffer.get(b, offset, bytesToRead); - ret = bytesToRead; - } else { - if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length); - ret = mSocketIS.read(b, offset, length); - } - if (ret < 0) { - throw new IOException("bt socket closed, read return: " + ret); - } - if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); - return ret; - } - - /*package*/ int write(byte[] b, int offset, int length) throws IOException { - - //TODO: Since bindings can exist between the SDU size and the - // protocol, we might need to throw an exception instead of just - // splitting the write into multiple smaller writes. - // Rfcomm uses dynamic allocation, and should not have any bindings - // to the actual message length. - if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); - if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) { - if (length <= mMaxTxPacketSize) { - mSocketOS.write(b, offset, length); - } else { - if (DBG) { - Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n" - + "Packet will be divided into SDU packets of size " - + mMaxTxPacketSize); - } - int tmpOffset = offset; - int bytesToWrite = length; - while (bytesToWrite > 0) { - int tmpLength = (bytesToWrite > mMaxTxPacketSize) - ? mMaxTxPacketSize - : bytesToWrite; - mSocketOS.write(b, tmpOffset, tmpLength); - tmpOffset += tmpLength; - bytesToWrite -= tmpLength; - } - } - } else { - mSocketOS.write(b, offset, length); - } - // There is no good way to confirm since the entire process is asynchronous anyway - if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length); - return length; - } - - @Override - public void close() throws IOException { - Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS - + ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket + ", mSocketState: " - + mSocketState); - if (mSocketState == SocketState.CLOSED) { - return; - } else { - synchronized (this) { - if (mSocketState == SocketState.CLOSED) { - return; - } - mSocketState = SocketState.CLOSED; - if (mSocket != null) { - if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket); - mSocket.shutdownInput(); - mSocket.shutdownOutput(); - mSocket.close(); - mSocket = null; - } - if (mPfd != null) { - mPfd.close(); - mPfd = null; - } - } - } - } - - /*package */ void removeChannel() { - } - - /*package */ int getPort() { - return mPort; - } - - /** - * Get the maximum supported Transmit packet size for the underlying transport. - * Use this to optimize the writes done to the output socket, to avoid sending - * half full packets. - * - * @return the maximum supported Transmit packet size for the underlying transport. - */ - @RequiresNoPermission - public int getMaxTransmitPacketSize() { - return mMaxTxPacketSize; - } - - /** - * Get the maximum supported Receive packet size for the underlying transport. - * Use this to optimize the reads done on the input stream, as any call to read - * will return a maximum of this amount of bytes - or for some transports a - * multiple of this value. - * - * @return the maximum supported Receive packet size for the underlying transport. - */ - @RequiresNoPermission - public int getMaxReceivePacketSize() { - return mMaxRxPacketSize; - } - - /** - * Get the type of the underlying connection. - * - * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP} - */ - @RequiresNoPermission - public int getConnectionType() { - if (mType == TYPE_L2CAP_LE) { - // Treat the LE CoC to be the same type as L2CAP. - return TYPE_L2CAP; - } - return mType; - } - - /** - * Change if a SDP entry should be automatically created. - * Must be called before calling .bind, for the call to have any effect. - * - * @param excludeSdp <li>TRUE - do not auto generate SDP record. <li>FALSE - default - auto - * generate SPP SDP record. - * @hide - */ - @RequiresNoPermission - public void setExcludeSdp(boolean excludeSdp) { - mExcludeSdp = excludeSdp; - } - - /** - * Set the LE Transmit Data Length to be the maximum that the BT Controller is capable of. This - * parameter is used by the BT Controller to set the maximum transmission packet size on this - * connection. This function is currently used for testing only. - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public void requestMaximumTxDataLength() throws IOException { - if (mDevice == null) { - throw new IOException("requestMaximumTxDataLength is called on null device"); - } - - try { - if (mSocketState == SocketState.CLOSED) { - throw new IOException("socket closed"); - } - IBluetooth bluetoothProxy = - BluetoothAdapter.getDefaultAdapter().getBluetoothService(); - if (bluetoothProxy == null) { - throw new IOException("Bluetooth is off"); - } - - if (DBG) Log.d(TAG, "requestMaximumTxDataLength"); - bluetoothProxy.getSocketManager().requestMaximumTxDataLength(mDevice); - } catch (RemoteException e) { - Log.e(TAG, Log.getStackTraceString(new Throwable())); - throw new IOException("unable to send RPC: " + e.getMessage()); - } - } - - private String convertAddr(final byte[] addr) { - return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", - addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); - } - - private String waitSocketSignal(InputStream is) throws IOException { - byte[] sig = new byte[SOCK_SIGNAL_SIZE]; - int ret = readAll(is, sig); - if (VDBG) { - Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE + " bytes signal ret: " + ret); - } - ByteBuffer bb = ByteBuffer.wrap(sig); - /* the struct in native is decorated with __attribute__((packed)), hence this is possible */ - bb.order(ByteOrder.nativeOrder()); - int size = bb.getShort(); - if (size != SOCK_SIGNAL_SIZE) { - throw new IOException("Connection failure, wrong signal size: " + size); - } - byte[] addr = new byte[6]; - bb.get(addr); - int channel = bb.getInt(); - int status = bb.getInt(); - mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value - mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value - String RemoteAddr = convertAddr(addr); - if (VDBG) { - Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: " - + RemoteAddr + ", channel: " + channel + ", status: " + status - + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize); - } - if (status != 0) { - throw new IOException("Connection failure, status: " + status); - } - return RemoteAddr; - } - - private void createL2capRxBuffer() { - if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) { - // Allocate the buffer to use for reads. - if (VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize); - mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]); - if (VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining()); - mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request - if (VDBG) { - Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" + mL2capBuffer.remaining()); - } - } - } - - private int readAll(InputStream is, byte[] b) throws IOException { - int left = b.length; - while (left > 0) { - int ret = is.read(b, b.length - left, left); - if (ret <= 0) { - throw new IOException("read failed, socket might closed or timeout, read ret: " - + ret); - } - left -= ret; - if (left != 0) { - Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) - + ", expect size: " + b.length); - } - } - return b.length; - } - - private int readInt(InputStream is) throws IOException { - byte[] ibytes = new byte[4]; - int ret = readAll(is, ibytes); - if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret); - ByteBuffer bb = ByteBuffer.wrap(ibytes); - bb.order(ByteOrder.nativeOrder()); - return bb.getInt(); - } - - private int fillL2capRxBuffer() throws IOException { - mL2capBuffer.rewind(); - int ret = mSocketIS.read(mL2capBuffer.array()); - if (ret == -1) { - // reached end of stream - return -1 - mL2capBuffer.limit(0); - return -1; - } - mL2capBuffer.limit(ret); - return ret; - } - - -} diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java deleted file mode 100644 index 9dafa073ab3f..000000000000 --- a/core/java/android/bluetooth/BluetoothStatusCodes.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * 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.bluetooth; - -import android.annotation.SystemApi; - -/** - * A class with constants representing possible return values for Bluetooth APIs. General return - * values occupy the range 0 to 99. Profile-specific return values occupy the range 100-999. - * API-specific return values start at 1000. The exception to this is the "UNKNOWN" error code which - * occupies the max integer value. - */ -public final class BluetoothStatusCodes { - - private BluetoothStatusCodes() {} - - /** - * Indicates that the API call was successful - */ - public static final int SUCCESS = 0; - - /** - * Error code indicating that Bluetooth is not enabled - */ - public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; - - /** - * Error code indicating that the API call was initiated by neither the system nor the active - * Zuser - */ - public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; - - /** - * Error code indicating that the Bluetooth Device specified is not bonded - */ - public static final int ERROR_DEVICE_NOT_BONDED = 3; - - /** - * Error code indicating that the Bluetooth Device specified is not connected, but is bonded - * - * @hide - */ - public static final int ERROR_DEVICE_NOT_CONNECTED = 4; - - /** - * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission - * - * @hide - */ - public static final int ERROR_MISSING_BLUETOOTH_ADVERTISE_PERMISSION = 5; - - /** - * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission - */ - public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; - - /** - * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_SCAN} permission - * - * @hide - */ - public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7; - - /** - * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission - */ - public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; - - /** - * Error code indicating that the profile service is not bound. You can bind a profile service - * by calling {@link BluetoothAdapter#getProfileProxy} - */ - public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; - - /** - * Error code indicating that the feature is not supported. - */ - public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; - - /** - * A GATT writeCharacteristic request is not permitted on the remote device. - */ - public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; - - /** - * A GATT writeCharacteristic request is issued to a busy remote device. - */ - public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; - - /** - * If another application has already requested {@link OobData} then another fetch will be - * disallowed until the callback is removed. - * - * @hide - */ - @SystemApi - public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; - - /** - * Indicates that the ACL disconnected due to an explicit request from the local device. - * <p> - * Example cause: This is a normal disconnect reason, e.g., user/app initiates - * disconnection. - * - * @hide - */ - public static final int ERROR_DISCONNECT_REASON_LOCAL_REQUEST = 1100; - - /** - * Indicates that the ACL disconnected due to an explicit request from the remote device. - * <p> - * Example cause: This is a normal disconnect reason, e.g., user/app initiates - * disconnection. - * <p> - * Example solution: The app can also prompt the user to check their remote device. - * - * @hide - */ - public static final int ERROR_DISCONNECT_REASON_REMOTE_REQUEST = 1101; - - /** - * Generic disconnect reason indicating the ACL disconnected due to an error on the local - * device. - * <p> - * Example solution: Prompt the user to check their local device (e.g., phone, car - * headunit). - * - * @hide - */ - public static final int ERROR_DISCONNECT_REASON_LOCAL = 1102; - - /** - * Generic disconnect reason indicating the ACL disconnected due to an error on the remote - * device. - * <p> - * Example solution: Prompt the user to check their remote device (e.g., headset, car - * headunit, watch). - * - * @hide - */ - public static final int ERROR_DISCONNECT_REASON_REMOTE = 1103; - - /** - * Indicates that the ACL disconnected due to a timeout. - * <p> - * Example cause: remote device might be out of range. - * <p> - * Example solution: Prompt user to verify their remote device is on or in - * connection/pairing mode. - * - * @hide - */ - public static final int ERROR_DISCONNECT_REASON_TIMEOUT = 1104; - - /** - * Indicates that the ACL disconnected due to link key issues. - * <p> - * Example cause: Devices are either unpaired or remote device is refusing our pairing - * request. - * <p> - * Example solution: Prompt user to unpair and pair again. - * - * @hide - */ - public static final int ERROR_DISCONNECT_REASON_SECURITY = 1105; - - /** - * Indicates that the ACL disconnected due to the local device's system policy. - * <p> - * Example cause: privacy policy, power management policy, permissions, etc. - * <p> - * Example solution: Prompt the user to check settings, or check with their system - * administrator (e.g. some corp-managed devices do not allow OPP connection). - * - * @hide - */ - public static final int ERROR_DISCONNECT_REASON_SYSTEM_POLICY = 1106; - - /** - * Indicates that the ACL disconnected due to resource constraints, either on the local - * device or the remote device. - * <p> - * Example cause: controller is busy, memory limit reached, maximum number of connections - * reached. - * <p> - * Example solution: The app should wait and try again. If still failing, prompt the user - * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device. - * - * @hide - */ - public static final int ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED = 1107; - - /** - * Indicates that the ACL disconnected because another ACL connection already exists. - * - * @hide - */ - public static final int ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS = 1108; - - /** - * Indicates that the ACL disconnected due to incorrect parameters passed in from the app. - * <p> - * Example solution: Change parameters and try again. If error persists, the app can report - * telemetry and/or log the error in a bugreport. - * - * @hide - */ - public static final int ERROR_DISCONNECT_REASON_BAD_PARAMETERS = 1109; - - /** - * Indicates that setting the LE Audio Broadcast mode failed. - * <p> - * Example solution: Change parameters and try again. If error persists, the app can report - * telemetry and/or log the error in a bugreport. - * - * @hide - */ - public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED = 1110; - - /** - * Indicates that setting a new encryption key for Bluetooth LE Audio Broadcast Source failed. - * <p> - * Example solution: Change parameters and try again. If error persists, the app can report - * telemetry and/or log the error in a bugreport. - * - * @hide - */ - public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED = 1111; - - /** - * Indicates that connecting to a remote Broadcast Audio Scan Service failed. - * <p> - * Example solution: Change parameters and try again. If error persists, the app can report - * telemetry and/or log the error in a bugreport. - * - * @hide - */ - public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_CONNECT_FAILED = 1112; - - /** - * Indicates that disconnecting from a remote Broadcast Audio Scan Service failed. - * <p> - * Example solution: Change parameters and try again. If error persists, the app can report - * telemetry and/or log the error in a bugreport. - * - * @hide - */ - public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_DISCONNECT_FAILED = 1113; - - /** - * Indicates that enabling LE Audio Broadcast encryption failed - * <p> - * Example solution: Change parameters and try again. If error persists, the app can report - * telemetry and/or log the error in a bugreport. - * - * @hide - */ - public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED = 1114; - - /** - * Indicates that disabling LE Audio Broadcast encryption failed - * <p> - * Example solution: Change parameters and try again. If error persists, the app can report - * telemetry and/or log the error in a bugreport. - * - * @hide - */ - public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED = 1115; - - /** - * Indicates that an unknown error has occurred has occurred. - */ - public static final int ERROR_UNKNOWN = Integer.MAX_VALUE; -} diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java deleted file mode 100644 index 2a8ff5185085..000000000000 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ /dev/null @@ -1,394 +0,0 @@ -/* - * Copyright (C) 2009 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.bluetooth; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.ParcelUuid; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; -import java.util.HashSet; -import java.util.UUID; - -/** - * Static helper methods and constants to decode the ParcelUuid of remote devices. - * - * @hide - */ -@SystemApi -@SuppressLint("AndroidFrameworkBluetoothPermission") -public final class BluetoothUuid { - - /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs - * for the various services. - * - * The following 128 bit values are calculated as: - * uuid * 2^96 + BASE_UUID - */ - - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid A2DP_SINK = - ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid A2DP_SOURCE = - ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid ADV_AUDIO_DIST = - ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid HSP = - ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid HSP_AG = - ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid HFP = - ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid HFP_AG = - ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid AVRCP_CONTROLLER = - ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid AVRCP_TARGET = - ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid OBEX_OBJECT_PUSH = - ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid HID = - ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid HOGP = - ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid PANU = - ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid NAP = - ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid BNEP = - ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid PBAP_PCE = - ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid PBAP_PSE = - ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid MAP = - ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid MNS = - ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid MAS = - ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid SAP = - ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid HEARING_AID = - ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid LE_AUDIO = - ParcelUuid.fromString("0000184E-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid DIP = - ParcelUuid.fromString("00001200-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid VOLUME_CONTROL = - ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid GENERIC_MEDIA_CONTROL = - ParcelUuid.fromString("00001849-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid MEDIA_CONTROL = - ParcelUuid.fromString("00001848-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid COORDINATED_SET = - ParcelUuid.fromString("00001846-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid CAP = - ParcelUuid.fromString("00001853-0000-1000-8000-00805F9B34FB"); - /** @hide */ - @NonNull - @SystemApi - public static final ParcelUuid BASE_UUID = - ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); - - /** - * Length of bytes for 16 bit UUID - * - * @hide - */ - @SystemApi - public static final int UUID_BYTES_16_BIT = 2; - /** - * Length of bytes for 32 bit UUID - * - * @hide - */ - @SystemApi - public static final int UUID_BYTES_32_BIT = 4; - /** - * Length of bytes for 128 bit UUID - * - * @hide - */ - @SystemApi - public static final int UUID_BYTES_128_BIT = 16; - - /** - * Returns true if there any common ParcelUuids in uuidA and uuidB. - * - * @param uuidA - List of ParcelUuids - * @param uuidB - List of ParcelUuids - * - * @hide - */ - @SystemApi - public static boolean containsAnyUuid(@Nullable ParcelUuid[] uuidA, - @Nullable ParcelUuid[] uuidB) { - if (uuidA == null && uuidB == null) return true; - - if (uuidA == null) { - return uuidB.length == 0; - } - - if (uuidB == null) { - return uuidA.length == 0; - } - - HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid>(Arrays.asList(uuidA)); - for (ParcelUuid uuid : uuidB) { - if (uuidSet.contains(uuid)) return true; - } - return false; - } - - /** - * Extract the Service Identifier or the actual uuid from the Parcel Uuid. - * For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid, - * this function will return 110B - * - * @param parcelUuid - * @return the service identifier. - */ - private static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) { - UUID uuid = parcelUuid.getUuid(); - long value = (uuid.getMostSignificantBits() & 0xFFFFFFFF00000000L) >>> 32; - return (int) value; - } - - /** - * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID, - * but the returned UUID is always in 128-bit format. - * Note UUID is little endian in Bluetooth. - * - * @param uuidBytes Byte representation of uuid. - * @return {@link ParcelUuid} parsed from bytes. - * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed. - * - * @hide - */ - @NonNull - @SystemApi - public static ParcelUuid parseUuidFrom(@Nullable byte[] uuidBytes) { - if (uuidBytes == null) { - throw new IllegalArgumentException("uuidBytes cannot be null"); - } - int length = uuidBytes.length; - if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT - && length != UUID_BYTES_128_BIT) { - throw new IllegalArgumentException("uuidBytes length invalid - " + length); - } - - // Construct a 128 bit UUID. - if (length == UUID_BYTES_128_BIT) { - ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN); - long msb = buf.getLong(8); - long lsb = buf.getLong(0); - return new ParcelUuid(new UUID(msb, lsb)); - } - - // For 16 bit and 32 bit UUID we need to convert them to 128 bit value. - // 128_bit_value = uuid * 2^96 + BASE_UUID - long shortUuid; - if (length == UUID_BYTES_16_BIT) { - shortUuid = uuidBytes[0] & 0xFF; - shortUuid += (uuidBytes[1] & 0xFF) << 8; - } else { - shortUuid = uuidBytes[0] & 0xFF; - shortUuid += (uuidBytes[1] & 0xFF) << 8; - shortUuid += (uuidBytes[2] & 0xFF) << 16; - shortUuid += (uuidBytes[3] & 0xFF) << 24; - } - long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32); - long lsb = BASE_UUID.getUuid().getLeastSignificantBits(); - return new ParcelUuid(new UUID(msb, lsb)); - } - - /** - * Parse UUID to bytes. The returned value is shortest representation, a 16-bit, 32-bit or - * 128-bit UUID, Note returned value is little endian (Bluetooth). - * - * @param uuid uuid to parse. - * @return shortest representation of {@code uuid} as bytes. - * @throws IllegalArgumentException If the {@code uuid} is null. - * - * @hide - */ - public static byte[] uuidToBytes(ParcelUuid uuid) { - if (uuid == null) { - throw new IllegalArgumentException("uuid cannot be null"); - } - - if (is16BitUuid(uuid)) { - byte[] uuidBytes = new byte[UUID_BYTES_16_BIT]; - int uuidVal = getServiceIdentifierFromParcelUuid(uuid); - uuidBytes[0] = (byte) (uuidVal & 0xFF); - uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8); - return uuidBytes; - } - - if (is32BitUuid(uuid)) { - byte[] uuidBytes = new byte[UUID_BYTES_32_BIT]; - int uuidVal = getServiceIdentifierFromParcelUuid(uuid); - uuidBytes[0] = (byte) (uuidVal & 0xFF); - uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8); - uuidBytes[2] = (byte) ((uuidVal & 0xFF0000) >> 16); - uuidBytes[3] = (byte) ((uuidVal & 0xFF000000) >> 24); - return uuidBytes; - } - - // Construct a 128 bit UUID. - long msb = uuid.getUuid().getMostSignificantBits(); - long lsb = uuid.getUuid().getLeastSignificantBits(); - - byte[] uuidBytes = new byte[UUID_BYTES_128_BIT]; - ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN); - buf.putLong(8, msb); - buf.putLong(0, lsb); - return uuidBytes; - } - - /** - * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. - * - * @param parcelUuid - * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. - * - * @hide - */ - @UnsupportedAppUsage - public static boolean is16BitUuid(ParcelUuid parcelUuid) { - UUID uuid = parcelUuid.getUuid(); - if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { - return false; - } - return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); - } - - - /** - * Check whether the given parcelUuid can be converted to 32 bit bluetooth uuid. - * - * @param parcelUuid - * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise. - * - * @hide - */ - @UnsupportedAppUsage - public static boolean is32BitUuid(ParcelUuid parcelUuid) { - UUID uuid = parcelUuid.getUuid(); - if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { - return false; - } - if (is16BitUuid(parcelUuid)) { - return false; - } - return ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L); - } - - private BluetoothUuid() {} -} diff --git a/core/java/android/bluetooth/BluetoothVolumeControl.java b/core/java/android/bluetooth/BluetoothVolumeControl.java deleted file mode 100644 index 27532aabc3fc..000000000000 --- a/core/java/android/bluetooth/BluetoothVolumeControl.java +++ /dev/null @@ -1,337 +0,0 @@ -/* - * 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 static android.bluetooth.BluetoothUtils.getSyncTimeout; - -import android.Manifest; -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.bluetooth.annotations.RequiresBluetoothConnectPermission; -import android.content.AttributionSource; -import android.content.Context; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.CloseGuard; -import android.util.Log; - -import com.android.modules.utils.SynchronousResultReceiver; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This class provides the public APIs to control the Bluetooth Volume Control service. - * - * <p>BluetoothVolumeControl is a proxy object for controlling the Bluetooth VC - * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothVolumeControl proxy object. - * @hide - */ -@SystemApi -public final class BluetoothVolumeControl implements BluetoothProfile, AutoCloseable { - private static final String TAG = "BluetoothVolumeControl"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - private CloseGuard mCloseGuard; - - /** - * Intent used to broadcast the change in connection state of the Volume Control - * profile. - * - * <p>This intent will have 3 extras: - * <ul> - * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> - * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> - * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> - * </ul> - * - * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of - * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - * @hide - */ - @SystemApi - @SuppressLint("ActionValue") - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED"; - - private BluetoothAdapter mAdapter; - private final AttributionSource mAttributionSource; - private final BluetoothProfileConnector<IBluetoothVolumeControl> mProfileConnector = - new BluetoothProfileConnector(this, BluetoothProfile.VOLUME_CONTROL, TAG, - IBluetoothVolumeControl.class.getName()) { - @Override - public IBluetoothVolumeControl getServiceInterface(IBinder service) { - return IBluetoothVolumeControl.Stub.asInterface(service); - } - }; - - /** - * Create a BluetoothVolumeControl proxy object for interacting with the local - * Bluetooth Volume Control service. - */ - /*package*/ BluetoothVolumeControl(Context context, ServiceListener listener, - BluetoothAdapter adapter) { - mAdapter = adapter; - mAttributionSource = adapter.getAttributionSource(); - mProfileConnector.connect(context, listener); - mCloseGuard = new CloseGuard(); - mCloseGuard.open("close"); - } - - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - protected void finalize() { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } - close(); - } - - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public void close() { - mProfileConnector.disconnect(); - } - - private IBluetoothVolumeControl getService() { return mProfileConnector.getService(); } - - /** - * Get the list of connected devices. Currently at most one. - * - * @return list of connected devices - * - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @NonNull List<BluetoothDevice> getConnectedDevices() { - if (DBG) log("getConnectedDevices()"); - final IBluetoothVolumeControl service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getConnectedDevices(mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the list of devices matching specified states. Currently at most one. - * - * @return list of matching devices - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothVolumeControl service = getService(); - final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<List<BluetoothDevice>> recv = - new SynchronousResultReceiver(); - service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv); - return Attributable.setAttributionSource( - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), - mAttributionSource); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get connection state of device - * - * @return device connection state - * - * @hide - */ - @RequiresBluetoothConnectPermission - @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) - public int getConnectionState(BluetoothDevice device) { - if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothVolumeControl service = getService(); - final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionState(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Tells remote device to set an absolute volume. - * - * @param volume Absolute volume to be set on remote device. - * Minimum value is 0 and maximum value is 255 - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public void setVolume(@Nullable BluetoothDevice device, - @IntRange(from = 0, to = 255) int volume) { - if (DBG) log("setVolume(" + volume + ")"); - final IBluetoothVolumeControl service = getService(); - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver recv = new SynchronousResultReceiver(); - service.setVolume(device, volume, mAttributionSource, recv); - recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - } - - /** - * Set connection policy of the profile - * - * <p> The device should already be paired. - * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, - * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile - * @return true if connectionPolicy is set, false on error - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public boolean setConnectionPolicy(@NonNull BluetoothDevice device, - @ConnectionPolicy int connectionPolicy) { - if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); - final IBluetoothVolumeControl service = getService(); - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device) - && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN - || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - /** - * Get the connection policy of the profile. - * - * <p> The connection policy can be any of: - * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, - * {@link #CONNECTION_POLICY_UNKNOWN} - * - * @param device Bluetooth device - * @return connection policy of the device - * @hide - */ - @SystemApi - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { - if (VDBG) log("getConnectionPolicy(" + device + ")"); - final IBluetoothVolumeControl service = getService(); - final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled() && isValidDevice(device)) { - try { - final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); - service.getConnectionPolicy(device, mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } - - private boolean isEnabled() { - return mAdapter.getState() == BluetoothAdapter.STATE_ON; - } - - private static boolean isValidDevice(@Nullable BluetoothDevice device) { - return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} diff --git a/core/java/android/bluetooth/BufferConstraint.java b/core/java/android/bluetooth/BufferConstraint.java deleted file mode 100644 index cbffc788c35d..000000000000 --- a/core/java/android/bluetooth/BufferConstraint.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Stores a codec's constraints on buffering length in milliseconds. - * - * {@hide} - */ -@SystemApi -public final class BufferConstraint implements Parcelable { - - private static final String TAG = "BufferConstraint"; - private int mDefaultMillis; - private int mMaxMillis; - private int mMinMillis; - - public BufferConstraint(int defaultMillis, int maxMillis, - int minMillis) { - mDefaultMillis = defaultMillis; - mMaxMillis = maxMillis; - mMinMillis = minMillis; - } - - BufferConstraint(Parcel in) { - mDefaultMillis = in.readInt(); - mMaxMillis = in.readInt(); - mMinMillis = in.readInt(); - } - - public static final @NonNull Parcelable.Creator<BufferConstraint> CREATOR = - new Parcelable.Creator<BufferConstraint>() { - public BufferConstraint createFromParcel(Parcel in) { - return new BufferConstraint(in); - } - - public BufferConstraint[] newArray(int size) { - return new BufferConstraint[size]; - } - }; - - @Override - public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeInt(mDefaultMillis); - out.writeInt(mMaxMillis); - out.writeInt(mMinMillis); - } - - @Override - public int describeContents() { - return 0; - } - - /** - * Get the default buffer millis - * - * @return default buffer millis - * @hide - */ - @SystemApi - public int getDefaultMillis() { - return mDefaultMillis; - } - - /** - * Get the maximum buffer millis - * - * @return maximum buffer millis - * @hide - */ - @SystemApi - public int getMaxMillis() { - return mMaxMillis; - } - - /** - * Get the minimum buffer millis - * - * @return minimum buffer millis - * @hide - */ - @SystemApi - public int getMinMillis() { - return mMinMillis; - } -} diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java deleted file mode 100644 index 97d97232b7a6..000000000000 --- a/core/java/android/bluetooth/BufferConstraints.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - - -/** - * A parcelable collection of buffer constraints by codec type. - * - * {@hide} - */ -@SystemApi -public final class BufferConstraints implements Parcelable { - public static final int BUFFER_CODEC_MAX_NUM = 32; - - private static final String TAG = "BufferConstraints"; - - private Map<Integer, BufferConstraint> mBufferConstraints; - private List<BufferConstraint> mBufferConstraintList; - - public BufferConstraints(@NonNull List<BufferConstraint> - bufferConstraintList) { - - mBufferConstraintList = new ArrayList<BufferConstraint>(bufferConstraintList); - mBufferConstraints = new HashMap<Integer, BufferConstraint>(); - for (int i = 0; i < BUFFER_CODEC_MAX_NUM; i++) { - mBufferConstraints.put(i, bufferConstraintList.get(i)); - } - } - - BufferConstraints(Parcel in) { - mBufferConstraintList = new ArrayList<BufferConstraint>(); - mBufferConstraints = new HashMap<Integer, BufferConstraint>(); - in.readList(mBufferConstraintList, BufferConstraint.class.getClassLoader()); - for (int i = 0; i < mBufferConstraintList.size(); i++) { - mBufferConstraints.put(i, mBufferConstraintList.get(i)); - } - } - - public static final @NonNull Parcelable.Creator<BufferConstraints> CREATOR = - new Parcelable.Creator<BufferConstraints>() { - public BufferConstraints createFromParcel(Parcel in) { - return new BufferConstraints(in); - } - - public BufferConstraints[] newArray(int size) { - return new BufferConstraints[size]; - } - }; - - @Override - public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeList(mBufferConstraintList); - } - - @Override - public int describeContents() { - return 0; - } - - /** - * Get the buffer constraints by codec type. - * - * @param codec Audio codec - * @return buffer constraints by codec type. - * @hide - */ - @SystemApi - public @Nullable BufferConstraint forCodec(@BluetoothCodecConfig.SourceCodecType int codec) { - return mBufferConstraints.get(codec); - } -} diff --git a/core/java/android/bluetooth/OWNERS b/core/java/android/bluetooth/OWNERS deleted file mode 100644 index fbee57773173..000000000000 --- a/core/java/android/bluetooth/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -# Bug component: 27441 - -rahulsabnis@google.com -sattiraju@google.com -siyuanh@google.com -zachoverflow@google.com diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java deleted file mode 100644 index bb0b95649b17..000000000000 --- a/core/java/android/bluetooth/OobData.java +++ /dev/null @@ -1,958 +0,0 @@ -/** - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import static java.util.Objects.requireNonNull; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Out Of Band Data for Bluetooth device pairing. - * - * <p>This object represents optional data obtained from a remote device through - * an out-of-band channel (eg. NFC, QR). - * - * <p>References: - * NFC AD Forum SSP 1.1 (AD) - * {@link https://members.nfc-forum.org//apps/group_public/download.php/24620/NFCForum-AD-BTSSP_1_1.pdf} - * Core Specification Supplement (CSS) V9 - * - * <p>There are several BR/EDR Examples - * - * <p>Negotiated Handover: - * Bluetooth Carrier Configuration Record: - * - OOB Data Length - * - Device Address - * - Class of Device - * - Simple Pairing Hash C - * - Simple Pairing Randomizer R - * - Service Class UUID - * - Bluetooth Local Name - * - * <p>Static Handover: - * Bluetooth Carrier Configuration Record: - * - OOB Data Length - * - Device Address - * - Class of Device - * - Service Class UUID - * - Bluetooth Local Name - * - * <p>Simplified Tag Format for Single BT Carrier: - * Bluetooth OOB Data Record: - * - OOB Data Length - * - Device Address - * - Class of Device - * - Service Class UUID - * - Bluetooth Local Name - * - * @hide - */ -@SystemApi -public final class OobData implements Parcelable { - - private static final String TAG = "OobData"; - /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */ - @SystemApi - public static final int OOB_LENGTH_OCTETS = 2; - /** - * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1). - * (AD 3.1.2) (CSS 1.6.2) - * @hide - */ - @SystemApi - public static final int DEVICE_ADDRESS_OCTETS = 7; - /** The Class of Device is 3 octets. (AD 3.1.3) (CSS 1.6.2) @hide */ - @SystemApi - public static final int CLASS_OF_DEVICE_OCTETS = 3; - /** The Confirmation data must be 16 octets. (AD 3.2.2) (CSS 1.6.2) @hide */ - @SystemApi - public static final int CONFIRMATION_OCTETS = 16; - /** The Randomizer data must be 16 octets. (AD 3.2.3) (CSS 1.6.2) @hide */ - @SystemApi - public static final int RANDOMIZER_OCTETS = 16; - /** The LE Device Role length is 1 octet. (AD 3.3.2) (CSS 1.17) @hide */ - @SystemApi - public static final int LE_DEVICE_ROLE_OCTETS = 1; - /** The {@link OobData#mLeTemporaryKey} length. (3.4.1) @hide */ - @SystemApi - public static final int LE_TK_OCTETS = 16; - /** The {@link OobData#mLeAppearance} length. (3.4.1) @hide */ - @SystemApi - public static final int LE_APPEARANCE_OCTETS = 2; - /** The {@link OobData#mLeFlags} length. (3.4.1) @hide */ - @SystemApi - public static final int LE_DEVICE_FLAG_OCTETS = 1; // 1 octet to hold the 0-4 value. - - // Le Roles - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - prefix = { "LE_DEVICE_ROLE_" }, - value = { - LE_DEVICE_ROLE_PERIPHERAL_ONLY, - LE_DEVICE_ROLE_CENTRAL_ONLY, - LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL, - LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL - } - ) - public @interface LeRole {} - - /** @hide */ - @SystemApi - public static final int LE_DEVICE_ROLE_PERIPHERAL_ONLY = 0x00; - /** @hide */ - @SystemApi - public static final int LE_DEVICE_ROLE_CENTRAL_ONLY = 0x01; - /** @hide */ - @SystemApi - public static final int LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL = 0x02; - /** @hide */ - @SystemApi - public static final int LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL = 0x03; - - // Le Flags - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - prefix = { "LE_FLAG_" }, - value = { - LE_FLAG_LIMITED_DISCOVERY_MODE, - LE_FLAG_GENERAL_DISCOVERY_MODE, - LE_FLAG_BREDR_NOT_SUPPORTED, - LE_FLAG_SIMULTANEOUS_CONTROLLER, - LE_FLAG_SIMULTANEOUS_HOST - } - ) - public @interface LeFlag {} - - /** @hide */ - @SystemApi - public static final int LE_FLAG_LIMITED_DISCOVERY_MODE = 0x00; - /** @hide */ - @SystemApi - public static final int LE_FLAG_GENERAL_DISCOVERY_MODE = 0x01; - /** @hide */ - @SystemApi - public static final int LE_FLAG_BREDR_NOT_SUPPORTED = 0x02; - /** @hide */ - @SystemApi - public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 0x03; - /** @hide */ - @SystemApi - public static final int LE_FLAG_SIMULTANEOUS_HOST = 0x04; - - /** - * Builds an {@link OobData} object and validates that the required combination - * of values are present to create the LE specific OobData type. - * - * @hide - */ - @SystemApi - public static final class LeBuilder { - - /** - * It is recommended that this Hash C is generated anew for each - * pairing. - * - * <p>It should be noted that on passive NFC this isn't possible as the data is static - * and immutable. - */ - private byte[] mConfirmationHash = null; - - /** - * Optional, but adds more validity to the pairing. - * - * <p>If not present a value of 0 is assumed. - */ - private byte[] mRandomizerHash = new byte[] { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }; - - /** - * The Bluetooth Device user-friendly name presented over Bluetooth Technology. - * - * <p>This is the name that may be displayed to the device user as part of the UI. - */ - private byte[] mDeviceName = null; - - /** - * Sets the Bluetooth Device name to be used for UI purposes. - * - * <p>Optional attribute. - * - * @param deviceName byte array representing the name, may be 0 in length, not null. - * - * @return {@link OobData#ClassicBuilder} - * - * @throws NullPointerException if deviceName is null. - * - * @hide - */ - @NonNull - @SystemApi - public LeBuilder setDeviceName(@NonNull byte[] deviceName) { - requireNonNull(deviceName); - this.mDeviceName = deviceName; - return this; - } - - /** - * The Bluetooth Device Address is the address to which the OOB data belongs. - * - * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets. - * - * <p> Address is encoded in Little Endian order. - * - * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00 - */ - private final byte[] mDeviceAddressWithType; - - /** - * During an LE connection establishment, one must be in the Peripheral mode and the other - * in the Central role. - * - * <p>Possible Values: - * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported - * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported - * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; - * Peripheral Preferred - * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred - * 0x04 - 0xFF Reserved - */ - private final @LeRole int mLeDeviceRole; - - /** - * Temporary key value from the Security Manager. - * - * <p> Must be {@link LE_TK_OCTETS} in size - */ - private byte[] mLeTemporaryKey = null; - - /** - * Defines the representation of the external appearance of the device. - * - * <p>For example, a mouse, remote control, or keyboard. - * - * <p>Used for visual on discovering device to represent icon/string/etc... - */ - private byte[] mLeAppearance = null; - - /** - * Contains which discoverable mode to use, BR/EDR support and capability. - * - * <p>Possible LE Flags: - * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode. - * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. - * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of - * LMP Feature Mask Definitions. - * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to - * Same Device Capable (Controller). - * Bit 49 of LMP Feature Mask Definitions. - * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to - * Same Device Capable (Host). - * Bit 55 of LMP Feature Mask Definitions. - * <b>0x05- 0x07 Reserved</b> - */ - private @LeFlag int mLeFlags = LE_FLAG_GENERAL_DISCOVERY_MODE; // Invalid default - - /** - * Main creation method for creating a LE version of {@link OobData}. - * - * <p>This object will allow the caller to call {@link LeBuilder#build()} - * to build the data object or add any option information to the builder. - * - * @param deviceAddressWithType the LE device address plus the address type (7 octets); - * not null. - * @param leDeviceRole whether the device supports Peripheral, Central, - * Both including preference; not null. (1 octet) - * @param confirmationHash Array consisting of {@link OobData#CONFIRMATION_OCTETS} octets - * of data. Data is derived from controller/host stack and is - * required for pairing OOB. - * - * <p>Possible Values: - * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported - * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported - * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; - * Peripheral Preferred - * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred - * 0x04 - 0xFF Reserved - * - * @throws IllegalArgumentException if any of the values fail to be set. - * @throws NullPointerException if any argument is null. - * - * @hide - */ - @SystemApi - public LeBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] deviceAddressWithType, - @LeRole int leDeviceRole) { - requireNonNull(confirmationHash); - requireNonNull(deviceAddressWithType); - if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) { - throw new IllegalArgumentException("confirmationHash must be " - + OobData.CONFIRMATION_OCTETS + " octets in length."); - } - this.mConfirmationHash = confirmationHash; - if (deviceAddressWithType.length != OobData.DEVICE_ADDRESS_OCTETS) { - throw new IllegalArgumentException("confirmationHash must be " - + OobData.DEVICE_ADDRESS_OCTETS+ " octets in length."); - } - this.mDeviceAddressWithType = deviceAddressWithType; - if (leDeviceRole < LE_DEVICE_ROLE_PERIPHERAL_ONLY - || leDeviceRole > LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL) { - throw new IllegalArgumentException("leDeviceRole must be a valid value."); - } - this.mLeDeviceRole = leDeviceRole; - } - - /** - * Sets the Temporary Key value to be used by the LE Security Manager during - * LE pairing. - * - * @param leTemporaryKey byte array that shall be 16 bytes. Please see Bluetooth CSSv6, - * Part A 1.8 for a detailed description. - * - * @return {@link OobData#Builder} - * - * @throws IllegalArgumentException if the leTemporaryKey is an invalid format. - * @throws NullinterException if leTemporaryKey is null. - * - * @hide - */ - @NonNull - @SystemApi - public LeBuilder setLeTemporaryKey(@NonNull byte[] leTemporaryKey) { - requireNonNull(leTemporaryKey); - if (leTemporaryKey.length != LE_TK_OCTETS) { - throw new IllegalArgumentException("leTemporaryKey must be " - + LE_TK_OCTETS + " octets in length."); - } - this.mLeTemporaryKey = leTemporaryKey; - return this; - } - - /** - * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets - * of data. Data is derived from controller/host stack and is required for pairing OOB. - * Also, randomizerHash may be all 0s or null in which case it becomes all 0s. - * - * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed. - * @throws NullPointerException if randomizerHash is null. - * - * @hide - */ - @NonNull - @SystemApi - public LeBuilder setRandomizerHash(@NonNull byte[] randomizerHash) { - requireNonNull(randomizerHash); - if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) { - throw new IllegalArgumentException("randomizerHash must be " - + OobData.RANDOMIZER_OCTETS + " octets in length."); - } - this.mRandomizerHash = randomizerHash; - return this; - } - - /** - * Sets the LE Flags necessary for the pairing scenario or discovery mode. - * - * @param leFlags enum value representing the 1 octet of data about discovery modes. - * - * <p>Possible LE Flags: - * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode. - * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. - * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of - * LMP Feature Mask Definitions. - * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to - * Same Device Capable (Controller) Bit 49 of LMP Feature Mask Definitions. - * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to - * Same Device Capable (Host). - * Bit 55 of LMP Feature Mask Definitions. - * 0x05- 0x07 Reserved - * - * @throws IllegalArgumentException for invalid flag - * @hide - */ - @NonNull - @SystemApi - public LeBuilder setLeFlags(@LeFlag int leFlags) { - if (leFlags < LE_FLAG_LIMITED_DISCOVERY_MODE || leFlags > LE_FLAG_SIMULTANEOUS_HOST) { - throw new IllegalArgumentException("leFlags must be a valid value."); - } - this.mLeFlags = leFlags; - return this; - } - - /** - * Validates and builds the {@link OobData} object for LE Security. - * - * @return {@link OobData} with given builder values - * - * @throws IllegalStateException if either of the 2 required fields were not set. - * - * @hide - */ - @NonNull - @SystemApi - public OobData build() { - final OobData oob = - new OobData(this.mDeviceAddressWithType, this.mLeDeviceRole, - this.mConfirmationHash); - - // If we have values, set them, otherwise use default - oob.mLeTemporaryKey = - (this.mLeTemporaryKey != null) ? this.mLeTemporaryKey : oob.mLeTemporaryKey; - oob.mLeAppearance = (this.mLeAppearance != null) - ? this.mLeAppearance : oob.mLeAppearance; - oob.mLeFlags = (this.mLeFlags != 0xF) ? this.mLeFlags : oob.mLeFlags; - oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName; - oob.mRandomizerHash = this.mRandomizerHash; - return oob; - } - } - - /** - * Builds an {@link OobData} object and validates that the required combination - * of values are present to create the Classic specific OobData type. - * - * @hide - */ - @SystemApi - public static final class ClassicBuilder { - // Used by both Classic and LE - /** - * It is recommended that this Hash C is generated anew for each - * pairing. - * - * <p>It should be noted that on passive NFC this isn't possible as the data is static - * and immutable. - * - * @hide - */ - private byte[] mConfirmationHash = null; - - /** - * Optional, but adds more validity to the pairing. - * - * <p>If not present a value of 0 is assumed. - * - * @hide - */ - private byte[] mRandomizerHash = new byte[] { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }; - - /** - * The Bluetooth Device user-friendly name presented over Bluetooth Technology. - * - * <p>This is the name that may be displayed to the device user as part of the UI. - * - * @hide - */ - private byte[] mDeviceName = null; - - /** - * This length value provides the absolute length of total OOB data block used for - * Bluetooth BR/EDR - * - * <p>OOB communication, which includes the length field itself and the Bluetooth - * Device Address. - * - * <p>The minimum length that may be represented in this field is 8. - * - * @hide - */ - private final byte[] mClassicLength; - - /** - * The Bluetooth Device Address is the address to which the OOB data belongs. - * - * <p>The length MUST be {@link OobData#DEVICE_ADDRESS_OCTETS} octets. - * - * <p> Address is encoded in Little Endian order. - * - * <p>e.g. 00:01:02:03:04:05 would be x05x04x03x02x01x00 - * - * @hide - */ - private final byte[] mDeviceAddressWithType; - - /** - * Class of Device information is to be used to provide a graphical representation - * to the user as part of UI involving operations. - * - * <p>This is not to be used to determine a particular service can be used. - * - * <p>The length MUST be {@link OobData#CLASS_OF_DEVICE_OCTETS} octets. - * - * @hide - */ - private byte[] mClassOfDevice = null; - - /** - * Main creation method for creating a Classic version of {@link OobData}. - * - * <p>This object will allow the caller to call {@link ClassicBuilder#build()} - * to build the data object or add any option information to the builder. - * - * @param confirmationHash byte array consisting of {@link OobData#CONFIRMATION_OCTETS} - * octets of data. Data is derived from controller/host stack and is required for pairing - * OOB. - * @param classicLength byte array representing the length of data from 8-65535 across 2 - * octets (0xXXXX). - * @param deviceAddressWithType byte array representing the Bluetooth Address of the device - * that owns the OOB data. (i.e. the originator) [6 octets] - * - * @throws IllegalArgumentException if any of the values fail to be set. - * @throws NullPointerException if any argument is null. - * - * @hide - */ - @SystemApi - public ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength, - @NonNull byte[] deviceAddressWithType) { - requireNonNull(confirmationHash); - requireNonNull(classicLength); - requireNonNull(deviceAddressWithType); - if (confirmationHash.length != OobData.CONFIRMATION_OCTETS) { - throw new IllegalArgumentException("confirmationHash must be " - + OobData.CONFIRMATION_OCTETS + " octets in length."); - } - this.mConfirmationHash = confirmationHash; - if (classicLength.length != OOB_LENGTH_OCTETS) { - throw new IllegalArgumentException("classicLength must be " - + OOB_LENGTH_OCTETS + " octets in length."); - } - this.mClassicLength = classicLength; - if (deviceAddressWithType.length != DEVICE_ADDRESS_OCTETS) { - throw new IllegalArgumentException("deviceAddressWithType must be " - + DEVICE_ADDRESS_OCTETS + " octets in length."); - } - this.mDeviceAddressWithType = deviceAddressWithType; - } - - /** - * @param randomizerHash byte array consisting of {@link OobData#RANDOMIZER_OCTETS} octets - * of data. Data is derived from controller/host stack and is required for pairing OOB. - * Also, randomizerHash may be all 0s or null in which case it becomes all 0s. - * - * @throws IllegalArgumentException if null or incorrect length randomizerHash was passed. - * @throws NullPointerException if randomizerHash is null. - * - * @hide - */ - @NonNull - @SystemApi - public ClassicBuilder setRandomizerHash(@NonNull byte[] randomizerHash) { - requireNonNull(randomizerHash); - if (randomizerHash.length != OobData.RANDOMIZER_OCTETS) { - throw new IllegalArgumentException("randomizerHash must be " - + OobData.RANDOMIZER_OCTETS + " octets in length."); - } - this.mRandomizerHash = randomizerHash; - return this; - } - - /** - * Sets the Bluetooth Device name to be used for UI purposes. - * - * <p>Optional attribute. - * - * @param deviceName byte array representing the name, may be 0 in length, not null. - * - * @return {@link OobData#ClassicBuilder} - * - * @throws NullPointerException if deviceName is null - * - * @hide - */ - @NonNull - @SystemApi - public ClassicBuilder setDeviceName(@NonNull byte[] deviceName) { - requireNonNull(deviceName); - this.mDeviceName = deviceName; - return this; - } - - /** - * Sets the Bluetooth Class of Device; used for UI purposes only. - * - * <p>Not an indicator of available services! - * - * <p>Optional attribute. - * - * @param classOfDevice byte array of {@link OobData#CLASS_OF_DEVICE_OCTETS} octets. - * - * @return {@link OobData#ClassicBuilder} - * - * @throws IllegalArgumentException if length is not equal to - * {@link OobData#CLASS_OF_DEVICE_OCTETS} octets. - * @throws NullPointerException if classOfDevice is null. - * - * @hide - */ - @NonNull - @SystemApi - public ClassicBuilder setClassOfDevice(@NonNull byte[] classOfDevice) { - requireNonNull(classOfDevice); - if (classOfDevice.length != OobData.CLASS_OF_DEVICE_OCTETS) { - throw new IllegalArgumentException("classOfDevice must be " - + OobData.CLASS_OF_DEVICE_OCTETS + " octets in length."); - } - this.mClassOfDevice = classOfDevice; - return this; - } - - /** - * Validates and builds the {@link OobDat object for Classic Security. - * - * @return {@link OobData} with previously given builder values. - * - * @hide - */ - @NonNull - @SystemApi - public OobData build() { - final OobData oob = - new OobData(this.mClassicLength, this.mDeviceAddressWithType, - this.mConfirmationHash); - // If we have values, set them, otherwise use default - oob.mDeviceName = (this.mDeviceName != null) ? this.mDeviceName : oob.mDeviceName; - oob.mClassOfDevice = (this.mClassOfDevice != null) - ? this.mClassOfDevice : oob.mClassOfDevice; - oob.mRandomizerHash = this.mRandomizerHash; - return oob; - } - } - - // Members (Defaults for Optionals must be set or Parceling fails on NPE) - // Both - private final byte[] mDeviceAddressWithType; - private final byte[] mConfirmationHash; - private byte[] mRandomizerHash = new byte[] { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }; - // Default the name to "Bluetooth Device" - private byte[] mDeviceName = new byte[] { - // Bluetooth - 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, - // <space>Device - 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65 - }; - - // Classic - private final byte[] mClassicLength; - private byte[] mClassOfDevice = new byte[CLASS_OF_DEVICE_OCTETS]; - - // LE - private final @LeRole int mLeDeviceRole; - private byte[] mLeTemporaryKey = new byte[LE_TK_OCTETS]; - private byte[] mLeAppearance = new byte[LE_APPEARANCE_OCTETS]; - private @LeFlag int mLeFlags = LE_FLAG_LIMITED_DISCOVERY_MODE; - - /** - * @return byte array representing the MAC address of a bluetooth device. - * The Address is 6 octets long with a 1 octet address type associated with the address. - * - * <p>For classic this will be 6 byte address plus the default of PUBLIC_ADDRESS Address Type. - * For LE there are more choices for Address Type. - * - * @hide - */ - @NonNull - @SystemApi - public byte[] getDeviceAddressWithType() { - return mDeviceAddressWithType; - } - - /** - * @return byte array representing the confirmationHash value - * which is used to confirm the identity to the controller. - * - * @hide - */ - @NonNull - @SystemApi - public byte[] getConfirmationHash() { - return mConfirmationHash; - } - - /** - * @return byte array representing the randomizerHash value - * which is used to verify the identity of the controller. - * - * @hide - */ - @NonNull - @SystemApi - public byte[] getRandomizerHash() { - return mRandomizerHash; - } - - /** - * @return Device Name used for displaying name in UI. - * - * <p>Also, this will be populated with the LE Local Name if the data is for LE. - * - * @hide - */ - @Nullable - @SystemApi - public byte[] getDeviceName() { - return mDeviceName; - } - - /** - * @return byte array representing the oob data length which is the length - * of all of the data including these octets. - * - * @hide - */ - @NonNull - @SystemApi - public byte[] getClassicLength() { - return mClassicLength; - } - - /** - * @return byte array representing the class of device for UI display. - * - * <p>Does not indicate services available; for display only. - * - * @hide - */ - @NonNull - @SystemApi - public byte[] getClassOfDevice() { - return mClassOfDevice; - } - - /** - * @return Temporary Key used for LE pairing. - * - * @hide - */ - @Nullable - @SystemApi - public byte[] getLeTemporaryKey() { - return mLeTemporaryKey; - } - - /** - * @return Appearance used for LE pairing. For use in UI situations - * when determining what sort of icons or text to display regarding - * the device. - * - * @hide - */ - @Nullable - @SystemApi - public byte[] getLeAppearance() { - return mLeAppearance; - } - - /** - * @return Flags used to determing discoverable mode to use, BR/EDR Support, and Capability. - * - * <p>Possible LE Flags: - * {@link LE_FLAG_LIMITED_DISCOVERY_MODE} LE Limited Discoverable Mode. - * {@link LE_FLAG_GENERAL_DISCOVERY_MODE} LE General Discoverable Mode. - * {@link LE_FLAG_BREDR_NOT_SUPPORTED} BR/EDR Not Supported. Bit 37 of - * LMP Feature Mask Definitions. - * {@link LE_FLAG_SIMULTANEOUS_CONTROLLER} Simultaneous LE and BR/EDR to - * Same Device Capable (Controller). - * Bit 49 of LMP Feature Mask Definitions. - * {@link LE_FLAG_SIMULTANEOUS_HOST} Simultaneous LE and BR/EDR to - * Same Device Capable (Host). - * Bit 55 of LMP Feature Mask Definitions. - * <b>0x05- 0x07 Reserved</b> - * - * @hide - */ - @NonNull - @SystemApi - @LeFlag - public int getLeFlags() { - return mLeFlags; - } - - /** - * @return the supported and preferred roles of the LE device. - * - * <p>Possible Values: - * {@link LE_DEVICE_ROLE_PERIPHERAL_ONLY} Only Peripheral supported - * {@link LE_DEVICE_ROLE_CENTRAL_ONLY} Only Central supported - * {@link LE_DEVICE_ROLE_BOTH_PREFER_PERIPHERAL} Central & Peripheral supported; - * Peripheral Preferred - * {@link LE_DEVICE_ROLE_BOTH_PREFER_CENTRAL} Only peripheral supported; Central Preferred - * 0x04 - 0xFF Reserved - * - * @hide - */ - @NonNull - @SystemApi - @LeRole - public int getLeDeviceRole() { - return mLeDeviceRole; - } - - /** - * Classic Security Constructor - */ - private OobData(@NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType, - @NonNull byte[] confirmationHash) { - mClassicLength = classicLength; - mDeviceAddressWithType = deviceAddressWithType; - mConfirmationHash = confirmationHash; - mLeDeviceRole = -1; // Satisfy final - } - - /** - * LE Security Constructor - */ - private OobData(@NonNull byte[] deviceAddressWithType, @LeRole int leDeviceRole, - @NonNull byte[] confirmationHash) { - mDeviceAddressWithType = deviceAddressWithType; - mLeDeviceRole = leDeviceRole; - mConfirmationHash = confirmationHash; - mClassicLength = new byte[OOB_LENGTH_OCTETS]; // Satisfy final - } - - private OobData(Parcel in) { - // Both - mDeviceAddressWithType = in.createByteArray(); - mConfirmationHash = in.createByteArray(); - mRandomizerHash = in.createByteArray(); - mDeviceName = in.createByteArray(); - - // Classic - mClassicLength = in.createByteArray(); - mClassOfDevice = in.createByteArray(); - - // LE - mLeDeviceRole = in.readInt(); - mLeTemporaryKey = in.createByteArray(); - mLeAppearance = in.createByteArray(); - mLeFlags = in.readInt(); - } - - /** - * @hide - */ - @Override - public int describeContents() { - return 0; - } - - /** - * @hide - */ - @Override - public void writeToParcel(@NonNull Parcel out, int flags) { - // Both - // Required - out.writeByteArray(mDeviceAddressWithType); - // Required - out.writeByteArray(mConfirmationHash); - // Optional - out.writeByteArray(mRandomizerHash); - // Optional - out.writeByteArray(mDeviceName); - - // Classic - // Required - out.writeByteArray(mClassicLength); - // Optional - out.writeByteArray(mClassOfDevice); - - // LE - // Required - out.writeInt(mLeDeviceRole); - // Required - out.writeByteArray(mLeTemporaryKey); - // Optional - out.writeByteArray(mLeAppearance); - // Optional - out.writeInt(mLeFlags); - } - - // For Parcelable - public static final @android.annotation.NonNull Parcelable.Creator<OobData> CREATOR = - new Parcelable.Creator<OobData>() { - public OobData createFromParcel(Parcel in) { - return new OobData(in); - } - - public OobData[] newArray(int size) { - return new OobData[size]; - } - }; - - /** - * @return a {@link String} representation of the OobData object. - * - * @hide - */ - @Override - @NonNull - public String toString() { - return "OobData: \n\t" - // Both - + "Device Address With Type: " + toHexString(mDeviceAddressWithType) + "\n\t" - + "Confirmation: " + toHexString(mConfirmationHash) + "\n\t" - + "Randomizer: " + toHexString(mRandomizerHash) + "\n\t" - + "Device Name: " + toHexString(mDeviceName) + "\n\t" - // Classic - + "OobData Length: " + toHexString(mClassicLength) + "\n\t" - + "Class of Device: " + toHexString(mClassOfDevice) + "\n\t" - // LE - + "LE Device Role: " + toHexString(mLeDeviceRole) + "\n\t" - + "LE Temporary Key: " + toHexString(mLeTemporaryKey) + "\n\t" - + "LE Appearance: " + toHexString(mLeAppearance) + "\n\t" - + "LE Flags: " + toHexString(mLeFlags) + "\n\t"; - } - - @NonNull - private String toHexString(int b) { - return toHexString(new byte[] {(byte) b}); - } - - @NonNull - private String toHexString(byte b) { - return toHexString(new byte[] {b}); - } - - @NonNull - private String toHexString(byte[] array) { - if (array == null) return "null"; - StringBuilder builder = new StringBuilder(array.length * 2); - for (byte b: array) { - builder.append(String.format("%02x", b)); - } - return builder.toString(); - } -} diff --git a/core/java/android/bluetooth/SdpDipRecord.java b/core/java/android/bluetooth/SdpDipRecord.java deleted file mode 100644 index 84b0eef0593e..000000000000 --- a/core/java/android/bluetooth/SdpDipRecord.java +++ /dev/null @@ -1,104 +0,0 @@ -/* -* Copyright (C) 2015 Samsung System LSI -* 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 java.util.Arrays; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Data representation of a Object Push Profile Server side SDP record. - */ -/** @hide */ -public class SdpDipRecord implements Parcelable { - private final int mSpecificationId; - private final int mVendorId; - private final int mVendorIdSource; - private final int mProductId; - private final int mVersion; - private final boolean mPrimaryRecord; - - public SdpDipRecord(int specificationId, - int vendorId, int vendorIdSource, - int productId, int version, - boolean primaryRecord) { - super(); - this.mSpecificationId = specificationId; - this.mVendorId = vendorId; - this.mVendorIdSource = vendorIdSource; - this.mProductId = productId; - this.mVersion = version; - this.mPrimaryRecord = primaryRecord; - } - - public SdpDipRecord(Parcel in) { - this.mSpecificationId = in.readInt(); - this.mVendorId = in.readInt(); - this.mVendorIdSource = in.readInt(); - this.mProductId = in.readInt(); - this.mVersion = in.readInt(); - this.mPrimaryRecord = in.readBoolean(); - } - - public int getSpecificationId() { - return mSpecificationId; - } - - public int getVendorId() { - return mVendorId; - } - - public int getVendorIdSource() { - return mVendorIdSource; - } - - public int getProductId() { - return mProductId; - } - - public int getVersion() { - return mVersion; - } - - public boolean getPrimaryRecord() { - return mPrimaryRecord; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mSpecificationId); - dest.writeInt(mVendorId); - dest.writeInt(mVendorIdSource); - dest.writeInt(mProductId); - dest.writeInt(mVersion); - dest.writeBoolean(mPrimaryRecord); - } - - @Override - public int describeContents() { - /* No special objects */ - return 0; - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public SdpDipRecord createFromParcel(Parcel in) { - return new SdpDipRecord(in); - } - public SdpDipRecord[] newArray(int size) { - return new SdpDipRecord[size]; - } - }; -} diff --git a/core/java/android/bluetooth/SdpMasRecord.java b/core/java/android/bluetooth/SdpMasRecord.java deleted file mode 100644 index 72d49380b713..000000000000 --- a/core/java/android/bluetooth/SdpMasRecord.java +++ /dev/null @@ -1,150 +0,0 @@ -/* -* Copyright (C) 2015 Samsung System LSI -* 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.os.Parcel; -import android.os.Parcelable; - -/** @hide */ -public class SdpMasRecord implements Parcelable { - private final int mMasInstanceId; - private final int mL2capPsm; - private final int mRfcommChannelNumber; - private final int mProfileVersion; - private final int mSupportedFeatures; - private final int mSupportedMessageTypes; - private final String mServiceName; - - /** Message type */ - public static final class MessageType { - public static final int EMAIL = 0x01; - public static final int SMS_GSM = 0x02; - public static final int SMS_CDMA = 0x04; - public static final int MMS = 0x08; - } - - public SdpMasRecord(int masInstanceId, - int l2capPsm, - int rfcommChannelNumber, - int profileVersion, - int supportedFeatures, - int supportedMessageTypes, - String serviceName) { - mMasInstanceId = masInstanceId; - mL2capPsm = l2capPsm; - mRfcommChannelNumber = rfcommChannelNumber; - mProfileVersion = profileVersion; - mSupportedFeatures = supportedFeatures; - mSupportedMessageTypes = supportedMessageTypes; - mServiceName = serviceName; - } - - public SdpMasRecord(Parcel in) { - mMasInstanceId = in.readInt(); - mL2capPsm = in.readInt(); - mRfcommChannelNumber = in.readInt(); - mProfileVersion = in.readInt(); - mSupportedFeatures = in.readInt(); - mSupportedMessageTypes = in.readInt(); - mServiceName = in.readString(); - } - - @Override - public int describeContents() { - // TODO Auto-generated method stub - return 0; - } - - public int getMasInstanceId() { - return mMasInstanceId; - } - - public int getL2capPsm() { - return mL2capPsm; - } - - public int getRfcommCannelNumber() { - return mRfcommChannelNumber; - } - - public int getProfileVersion() { - return mProfileVersion; - } - - public int getSupportedFeatures() { - return mSupportedFeatures; - } - - public int getSupportedMessageTypes() { - return mSupportedMessageTypes; - } - - public boolean msgSupported(int msg) { - return (mSupportedMessageTypes & msg) != 0; - } - - public String getServiceName() { - return mServiceName; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mMasInstanceId); - dest.writeInt(mL2capPsm); - dest.writeInt(mRfcommChannelNumber); - dest.writeInt(mProfileVersion); - dest.writeInt(mSupportedFeatures); - dest.writeInt(mSupportedMessageTypes); - dest.writeString(mServiceName); - } - - @Override - public String toString() { - String ret = "Bluetooth MAS SDP Record:\n"; - - if (mMasInstanceId != -1) { - ret += "Mas Instance Id: " + mMasInstanceId + "\n"; - } - if (mRfcommChannelNumber != -1) { - ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; - } - if (mL2capPsm != -1) { - ret += "L2CAP PSM: " + mL2capPsm + "\n"; - } - if (mServiceName != null) { - ret += "Service Name: " + mServiceName + "\n"; - } - if (mProfileVersion != -1) { - ret += "Profile version: " + mProfileVersion + "\n"; - } - if (mSupportedMessageTypes != -1) { - ret += "Supported msg types: " + mSupportedMessageTypes + "\n"; - } - if (mSupportedFeatures != -1) { - ret += "Supported features: " + mSupportedFeatures + "\n"; - } - return ret; - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public SdpMasRecord createFromParcel(Parcel in) { - return new SdpMasRecord(in); - } - - public SdpRecord[] newArray(int size) { - return new SdpRecord[size]; - } - }; -} diff --git a/core/java/android/bluetooth/SdpMnsRecord.java b/core/java/android/bluetooth/SdpMnsRecord.java deleted file mode 100644 index a781d5df7dd0..000000000000 --- a/core/java/android/bluetooth/SdpMnsRecord.java +++ /dev/null @@ -1,114 +0,0 @@ -/* -* Copyright (C) 2015 Samsung System LSI -* 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.os.Parcel; -import android.os.Parcelable; - -/** @hide */ -public class SdpMnsRecord implements Parcelable { - private final int mL2capPsm; - private final int mRfcommChannelNumber; - private final int mSupportedFeatures; - private final int mProfileVersion; - private final String mServiceName; - - public SdpMnsRecord(int l2capPsm, - int rfcommChannelNumber, - int profileVersion, - int supportedFeatures, - String serviceName) { - mL2capPsm = l2capPsm; - mRfcommChannelNumber = rfcommChannelNumber; - mSupportedFeatures = supportedFeatures; - mServiceName = serviceName; - mProfileVersion = profileVersion; - } - - public SdpMnsRecord(Parcel in) { - mRfcommChannelNumber = in.readInt(); - mL2capPsm = in.readInt(); - mServiceName = in.readString(); - mSupportedFeatures = in.readInt(); - mProfileVersion = in.readInt(); - } - - @Override - public int describeContents() { - // TODO Auto-generated method stub - return 0; - } - - - public int getL2capPsm() { - return mL2capPsm; - } - - public int getRfcommChannelNumber() { - return mRfcommChannelNumber; - } - - public int getSupportedFeatures() { - return mSupportedFeatures; - } - - public String getServiceName() { - return mServiceName; - } - - public int getProfileVersion() { - return mProfileVersion; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mRfcommChannelNumber); - dest.writeInt(mL2capPsm); - dest.writeString(mServiceName); - dest.writeInt(mSupportedFeatures); - dest.writeInt(mProfileVersion); - } - - public String toString() { - String ret = "Bluetooth MNS SDP Record:\n"; - - if (mRfcommChannelNumber != -1) { - ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; - } - if (mL2capPsm != -1) { - ret += "L2CAP PSM: " + mL2capPsm + "\n"; - } - if (mServiceName != null) { - ret += "Service Name: " + mServiceName + "\n"; - } - if (mSupportedFeatures != -1) { - ret += "Supported features: " + mSupportedFeatures + "\n"; - } - if (mProfileVersion != -1) { - ret += "Profile_version: " + mProfileVersion + "\n"; - } - return ret; - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public SdpMnsRecord createFromParcel(Parcel in) { - return new SdpMnsRecord(in); - } - - public SdpMnsRecord[] newArray(int size) { - return new SdpMnsRecord[size]; - } - }; -} diff --git a/core/java/android/bluetooth/SdpOppOpsRecord.java b/core/java/android/bluetooth/SdpOppOpsRecord.java deleted file mode 100644 index e30745b89821..000000000000 --- a/core/java/android/bluetooth/SdpOppOpsRecord.java +++ /dev/null @@ -1,121 +0,0 @@ -/* -* Copyright (C) 2015 Samsung System LSI -* 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.os.Parcel; -import android.os.Parcelable; - -import java.util.Arrays; - -/** - * Data representation of a Object Push Profile Server side SDP record. - */ - -/** @hide */ -public class SdpOppOpsRecord implements Parcelable { - - private final String mServiceName; - private final int mRfcommChannel; - private final int mL2capPsm; - private final int mProfileVersion; - private final byte[] mFormatsList; - - public SdpOppOpsRecord(String serviceName, int rfcommChannel, - int l2capPsm, int version, byte[] formatsList) { - super(); - mServiceName = serviceName; - mRfcommChannel = rfcommChannel; - mL2capPsm = l2capPsm; - mProfileVersion = version; - mFormatsList = formatsList; - } - - public String getServiceName() { - return mServiceName; - } - - public int getRfcommChannel() { - return mRfcommChannel; - } - - public int getL2capPsm() { - return mL2capPsm; - } - - public int getProfileVersion() { - return mProfileVersion; - } - - public byte[] getFormatsList() { - return mFormatsList; - } - - @Override - public int describeContents() { - /* No special objects */ - return 0; - } - - public SdpOppOpsRecord(Parcel in) { - mRfcommChannel = in.readInt(); - mL2capPsm = in.readInt(); - mProfileVersion = in.readInt(); - mServiceName = in.readString(); - int arrayLength = in.readInt(); - if (arrayLength > 0) { - byte[] bytes = new byte[arrayLength]; - in.readByteArray(bytes); - mFormatsList = bytes; - } else { - mFormatsList = null; - } - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mRfcommChannel); - dest.writeInt(mL2capPsm); - dest.writeInt(mProfileVersion); - dest.writeString(mServiceName); - if (mFormatsList != null && mFormatsList.length > 0) { - dest.writeInt(mFormatsList.length); - dest.writeByteArray(mFormatsList); - } else { - dest.writeInt(0); - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("Bluetooth OPP Server SDP Record:\n"); - sb.append(" RFCOMM Chan Number: ").append(mRfcommChannel); - sb.append("\n L2CAP PSM: ").append(mL2capPsm); - sb.append("\n Profile version: ").append(mProfileVersion); - sb.append("\n Service Name: ").append(mServiceName); - sb.append("\n Formats List: ").append(Arrays.toString(mFormatsList)); - return sb.toString(); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public SdpOppOpsRecord createFromParcel(Parcel in) { - return new SdpOppOpsRecord(in); - } - - public SdpOppOpsRecord[] newArray(int size) { - return new SdpOppOpsRecord[size]; - } - }; - -} diff --git a/core/java/android/bluetooth/SdpPseRecord.java b/core/java/android/bluetooth/SdpPseRecord.java deleted file mode 100644 index 72249d0585c6..000000000000 --- a/core/java/android/bluetooth/SdpPseRecord.java +++ /dev/null @@ -1,129 +0,0 @@ -/* -* Copyright (C) 2015 Samsung System LSI -* 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.os.Parcel; -import android.os.Parcelable; - -/** @hide */ -public class SdpPseRecord implements Parcelable { - private final int mL2capPsm; - private final int mRfcommChannelNumber; - private final int mProfileVersion; - private final int mSupportedFeatures; - private final int mSupportedRepositories; - private final String mServiceName; - - public SdpPseRecord(int l2capPsm, - int rfcommChannelNumber, - int profileVersion, - int supportedFeatures, - int supportedRepositories, - String serviceName) { - mL2capPsm = l2capPsm; - mRfcommChannelNumber = rfcommChannelNumber; - mProfileVersion = profileVersion; - mSupportedFeatures = supportedFeatures; - mSupportedRepositories = supportedRepositories; - mServiceName = serviceName; - } - - public SdpPseRecord(Parcel in) { - mRfcommChannelNumber = in.readInt(); - mL2capPsm = in.readInt(); - mProfileVersion = in.readInt(); - mSupportedFeatures = in.readInt(); - mSupportedRepositories = in.readInt(); - mServiceName = in.readString(); - } - - @Override - public int describeContents() { - // TODO Auto-generated method stub - return 0; - } - - public int getL2capPsm() { - return mL2capPsm; - } - - public int getRfcommChannelNumber() { - return mRfcommChannelNumber; - } - - public int getSupportedFeatures() { - return mSupportedFeatures; - } - - public String getServiceName() { - return mServiceName; - } - - public int getProfileVersion() { - return mProfileVersion; - } - - public int getSupportedRepositories() { - return mSupportedRepositories; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mRfcommChannelNumber); - dest.writeInt(mL2capPsm); - dest.writeInt(mProfileVersion); - dest.writeInt(mSupportedFeatures); - dest.writeInt(mSupportedRepositories); - dest.writeString(mServiceName); - - } - - @Override - public String toString() { - String ret = "Bluetooth MNS SDP Record:\n"; - - if (mRfcommChannelNumber != -1) { - ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; - } - if (mL2capPsm != -1) { - ret += "L2CAP PSM: " + mL2capPsm + "\n"; - } - if (mProfileVersion != -1) { - ret += "profile version: " + mProfileVersion + "\n"; - } - if (mServiceName != null) { - ret += "Service Name: " + mServiceName + "\n"; - } - if (mSupportedFeatures != -1) { - ret += "Supported features: " + mSupportedFeatures + "\n"; - } - if (mSupportedRepositories != -1) { - ret += "Supported repositories: " + mSupportedRepositories + "\n"; - } - - return ret; - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public SdpPseRecord createFromParcel(Parcel in) { - return new SdpPseRecord(in); - } - - public SdpPseRecord[] newArray(int size) { - return new SdpPseRecord[size]; - } - }; -} diff --git a/core/java/android/bluetooth/SdpRecord.java b/core/java/android/bluetooth/SdpRecord.java deleted file mode 100644 index 730862ec6f91..000000000000 --- a/core/java/android/bluetooth/SdpRecord.java +++ /dev/null @@ -1,77 +0,0 @@ -/* -* Copyright (C) 2015 Samsung System LSI -* 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.os.Parcel; -import android.os.Parcelable; - -import java.util.Arrays; - -/** @hide */ -public class SdpRecord implements Parcelable { - - private final byte[] mRawData; - private final int mRawSize; - - @Override - public String toString() { - return "BluetoothSdpRecord [rawData=" + Arrays.toString(mRawData) - + ", rawSize=" + mRawSize + "]"; - } - - public SdpRecord(int sizeRecord, byte[] record) { - mRawData = record; - mRawSize = sizeRecord; - } - - public SdpRecord(Parcel in) { - mRawSize = in.readInt(); - mRawData = new byte[mRawSize]; - in.readByteArray(mRawData); - - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mRawSize); - dest.writeByteArray(mRawData); - - - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public SdpRecord createFromParcel(Parcel in) { - return new SdpRecord(in); - } - - public SdpRecord[] newArray(int size) { - return new SdpRecord[size]; - } - }; - - public byte[] getRawData() { - return mRawData; - } - - public int getRawSize() { - return mRawSize; - } -} diff --git a/core/java/android/bluetooth/SdpSapsRecord.java b/core/java/android/bluetooth/SdpSapsRecord.java deleted file mode 100644 index a1e2f7b51f35..000000000000 --- a/core/java/android/bluetooth/SdpSapsRecord.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2015 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.bluetooth; - -import android.os.Parcel; -import android.os.Parcelable; - -/** @hide */ -public class SdpSapsRecord implements Parcelable { - private final int mRfcommChannelNumber; - private final int mProfileVersion; - private final String mServiceName; - - public SdpSapsRecord(int rfcommChannelNumber, int profileVersion, String serviceName) { - mRfcommChannelNumber = rfcommChannelNumber; - mProfileVersion = profileVersion; - mServiceName = serviceName; - } - - public SdpSapsRecord(Parcel in) { - mRfcommChannelNumber = in.readInt(); - mProfileVersion = in.readInt(); - mServiceName = in.readString(); - } - - @Override - public int describeContents() { - return 0; - } - - public int getRfcommCannelNumber() { - return mRfcommChannelNumber; - } - - public int getProfileVersion() { - return mProfileVersion; - } - - public String getServiceName() { - return mServiceName; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mRfcommChannelNumber); - dest.writeInt(mProfileVersion); - dest.writeString(mServiceName); - - } - - @Override - public String toString() { - String ret = "Bluetooth MAS SDP Record:\n"; - - if (mRfcommChannelNumber != -1) { - ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n"; - } - if (mServiceName != null) { - ret += "Service Name: " + mServiceName + "\n"; - } - if (mProfileVersion != -1) { - ret += "Profile version: " + mProfileVersion + "\n"; - } - return ret; - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public SdpSapsRecord createFromParcel(Parcel in) { - return new SdpSapsRecord(in); - } - - public SdpRecord[] newArray(int size) { - return new SdpRecord[size]; - } - }; -} diff --git a/core/java/android/bluetooth/UidTraffic.java b/core/java/android/bluetooth/UidTraffic.java deleted file mode 100644 index 9982fa6121e4..000000000000 --- a/core/java/android/bluetooth/UidTraffic.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2015 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.bluetooth; - -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Record of data traffic (in bytes) by an application identified by its UID. - * - * @hide - */ -@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) -public final class UidTraffic implements Cloneable, Parcelable { - private final int mAppUid; - private long mRxBytes; - private long mTxBytes; - - /** @hide */ - public UidTraffic(int appUid, long rx, long tx) { - mAppUid = appUid; - mRxBytes = rx; - mTxBytes = tx; - } - - /** @hide */ - private UidTraffic(Parcel in) { - mAppUid = in.readInt(); - mRxBytes = in.readLong(); - mTxBytes = in.readLong(); - } - - /** @hide */ - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mAppUid); - dest.writeLong(mRxBytes); - dest.writeLong(mTxBytes); - } - - /** @hide */ - public void setRxBytes(long bytes) { - mRxBytes = bytes; - } - - /** @hide */ - public void setTxBytes(long bytes) { - mTxBytes = bytes; - } - - /** @hide */ - public void addRxBytes(long bytes) { - mRxBytes += bytes; - } - - /** @hide */ - public void addTxBytes(long bytes) { - mTxBytes += bytes; - } - - /** - * @return corresponding app Uid - */ - public int getUid() { - return mAppUid; - } - - /** - * @return rx bytes count - */ - public long getRxBytes() { - return mRxBytes; - } - - /** - * @return tx bytes count - */ - public long getTxBytes() { - return mTxBytes; - } - - /** @hide */ - @Override - public int describeContents() { - return 0; - } - - /** @hide */ - @Override - public UidTraffic clone() { - return new UidTraffic(mAppUid, mRxBytes, mTxBytes); - } - - /** @hide */ - @Override - public String toString() { - return "UidTraffic{mAppUid=" + mAppUid + ", mRxBytes=" + mRxBytes + ", mTxBytes=" - + mTxBytes + '}'; - } - - public static final @android.annotation.NonNull Creator<UidTraffic> CREATOR = new Creator<UidTraffic>() { - @Override - public UidTraffic createFromParcel(Parcel source) { - return new UidTraffic(source); - } - - @Override - public UidTraffic[] newArray(int size) { - return new UidTraffic[size]; - } - }; -} diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java deleted file mode 100644 index c508c2c9ca0b..000000000000 --- a/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.bluetooth.annotations; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import android.Manifest; -import android.os.Build; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher, - * this requires the {@link Manifest.permission#BLUETOOTH_ADVERTISE} - * permission which can be gained with - * {@link android.app.Activity#requestPermissions(String[], int)}. - * @hide - */ -@Retention(SOURCE) -@Target({METHOD, FIELD}) -public @interface RequiresBluetoothAdvertisePermission { -} diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java deleted file mode 100644 index e159eaafe2e4..000000000000 --- a/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.bluetooth.annotations; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import android.Manifest; -import android.os.Build; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher, - * this requires the {@link Manifest.permission#BLUETOOTH_CONNECT} - * permission which can be gained with - * {@link android.app.Activity#requestPermissions(String[], int)}. - * @hide - */ -@Retention(SOURCE) -@Target({METHOD, FIELD}) -public @interface RequiresBluetoothConnectPermission { -} diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java deleted file mode 100644 index 2bb320413941..000000000000 --- a/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.bluetooth.annotations; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import android.Manifest; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * @memberDoc In addition, this requires either the - * {@link Manifest.permission#ACCESS_FINE_LOCATION} - * permission or a strong assertion that you will never derive the - * physical location of the device. You can make this assertion by - * declaring {@code usesPermissionFlags="neverForLocation"} on the - * relevant {@code <uses-permission>} manifest tag, but it may - * restrict the types of Bluetooth devices you can interact with. - * @hide - */ -@Retention(SOURCE) -@Target({METHOD, FIELD}) -public @interface RequiresBluetoothLocationPermission { -} diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java deleted file mode 100644 index 800ff39933f2..000000000000 --- a/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.bluetooth.annotations; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import android.Manifest; -import android.os.Build; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher, - * this requires the {@link Manifest.permission#BLUETOOTH_SCAN} - * permission which can be gained with - * {@link android.app.Activity#requestPermissions(String[], int)}. - * @hide - */ -@Retention(SOURCE) -@Target({METHOD, FIELD}) -public @interface RequiresBluetoothScanPermission { -} diff --git a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java b/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java deleted file mode 100644 index 9adf695cde0f..000000000000 --- a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.bluetooth.annotations; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import android.Manifest; -import android.os.Build; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this - * requires the {@link Manifest.permission#BLUETOOTH_ADMIN} - * permission which can be gained with a simple - * {@code <uses-permission>} manifest tag. - * @hide - */ -@Retention(SOURCE) -@Target({METHOD, FIELD}) -public @interface RequiresLegacyBluetoothAdminPermission { -} diff --git a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java b/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java deleted file mode 100644 index 79621c366f59..000000000000 --- a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.bluetooth.annotations; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import android.Manifest; -import android.os.Build; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this - * requires the {@link Manifest.permission#BLUETOOTH} permission - * which can be gained with a simple {@code <uses-permission>} - * manifest tag. - * @hide - */ -@Retention(SOURCE) -@Target({METHOD, FIELD}) -public @interface RequiresLegacyBluetoothPermission { -} diff --git a/core/java/android/bluetooth/le/AdvertiseCallback.java b/core/java/android/bluetooth/le/AdvertiseCallback.java deleted file mode 100644 index 4fa8c4f2f539..000000000000 --- a/core/java/android/bluetooth/le/AdvertiseCallback.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -/** - * Bluetooth LE advertising callbacks, used to deliver advertising operation status. - */ -public abstract class AdvertiseCallback { - - /** - * The requested operation was successful. - * - * @hide - */ - public static final int ADVERTISE_SUCCESS = 0; - - /** - * Failed to start advertising as the advertise data to be broadcasted is larger than 31 bytes. - */ - public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1; - - /** - * Failed to start advertising because no advertising instance is available. - */ - public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; - - /** - * Failed to start advertising as the advertising is already started. - */ - public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; - - /** - * Operation failed due to an internal error. - */ - public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4; - - /** - * This feature is not supported on this platform. - */ - public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; - - /** - * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertising} indicating - * that the advertising has been started successfully. - * - * @param settingsInEffect The actual settings used for advertising, which may be different from - * what has been requested. - */ - public void onStartSuccess(AdvertiseSettings settingsInEffect) { - } - - /** - * Callback when advertising could not be started. - * - * @param errorCode Error code (see ADVERTISE_FAILED_* constants) for advertising start - * failures. - */ - public void onStartFailure(int errorCode) { - } -} diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java deleted file mode 100644 index fdf62ec3a647..000000000000 --- a/core/java/android/bluetooth/le/AdvertiseData.java +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; -import android.util.ArrayMap; -import android.util.SparseArray; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * Advertise data packet container for Bluetooth LE advertising. This represents the data to be - * advertised as well as the scan response data for active scans. - * <p> - * Use {@link AdvertiseData.Builder} to create an instance of {@link AdvertiseData} to be - * advertised. - * - * @see BluetoothLeAdvertiser - * @see ScanRecord - */ -public final class AdvertiseData implements Parcelable { - - @Nullable - private final List<ParcelUuid> mServiceUuids; - - @NonNull - private final List<ParcelUuid> mServiceSolicitationUuids; - - @Nullable - private final List<TransportDiscoveryData> mTransportDiscoveryData; - - private final SparseArray<byte[]> mManufacturerSpecificData; - private final Map<ParcelUuid, byte[]> mServiceData; - private final boolean mIncludeTxPowerLevel; - private final boolean mIncludeDeviceName; - - private AdvertiseData(List<ParcelUuid> serviceUuids, - List<ParcelUuid> serviceSolicitationUuids, - List<TransportDiscoveryData> transportDiscoveryData, - SparseArray<byte[]> manufacturerData, - Map<ParcelUuid, byte[]> serviceData, - boolean includeTxPowerLevel, - boolean includeDeviceName) { - mServiceUuids = serviceUuids; - mServiceSolicitationUuids = serviceSolicitationUuids; - mTransportDiscoveryData = transportDiscoveryData; - mManufacturerSpecificData = manufacturerData; - mServiceData = serviceData; - mIncludeTxPowerLevel = includeTxPowerLevel; - mIncludeDeviceName = includeDeviceName; - } - - /** - * Returns a list of service UUIDs within the advertisement that are used to identify the - * Bluetooth GATT services. - */ - public List<ParcelUuid> getServiceUuids() { - return mServiceUuids; - } - - /** - * Returns a list of service solicitation UUIDs within the advertisement that we invite to connect. - */ - @NonNull - public List<ParcelUuid> getServiceSolicitationUuids() { - return mServiceSolicitationUuids; - } - - /** - * Returns a list of {@link TransportDiscoveryData} within the advertisement. - */ - @NonNull - public List<TransportDiscoveryData> getTransportDiscoveryData() { - if (mTransportDiscoveryData == null) { - return Collections.emptyList(); - } - return mTransportDiscoveryData; - } - - /** - * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The - * manufacturer id is a non-negative number assigned by Bluetooth SIG. - */ - public SparseArray<byte[]> getManufacturerSpecificData() { - return mManufacturerSpecificData; - } - - /** - * Returns a map of 16-bit UUID and its corresponding service data. - */ - public Map<ParcelUuid, byte[]> getServiceData() { - return mServiceData; - } - - /** - * Whether the transmission power level will be included in the advertisement packet. - */ - public boolean getIncludeTxPowerLevel() { - return mIncludeTxPowerLevel; - } - - /** - * Whether the device name will be included in the advertisement packet. - */ - public boolean getIncludeDeviceName() { - return mIncludeDeviceName; - } - - /** - * @hide - */ - @Override - public int hashCode() { - return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mTransportDiscoveryData, - mManufacturerSpecificData, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); - } - - /** - * @hide - */ - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - AdvertiseData other = (AdvertiseData) obj; - return Objects.equals(mServiceUuids, other.mServiceUuids) - && Objects.equals(mServiceSolicitationUuids, other.mServiceSolicitationUuids) - && Objects.equals(mTransportDiscoveryData, other.mTransportDiscoveryData) - && BluetoothLeUtils.equals(mManufacturerSpecificData, - other.mManufacturerSpecificData) - && BluetoothLeUtils.equals(mServiceData, other.mServiceData) - && mIncludeDeviceName == other.mIncludeDeviceName - && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; - } - - @Override - public String toString() { - return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mServiceSolicitationUuids=" - + mServiceSolicitationUuids + ", mTransportDiscoveryData=" - + mTransportDiscoveryData + ", mManufacturerSpecificData=" - + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData=" - + BluetoothLeUtils.toString(mServiceData) - + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" - + mIncludeDeviceName + "]"; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeTypedArray(mServiceUuids.toArray(new ParcelUuid[mServiceUuids.size()]), flags); - dest.writeTypedArray(mServiceSolicitationUuids.toArray( - new ParcelUuid[mServiceSolicitationUuids.size()]), flags); - - dest.writeTypedList(mTransportDiscoveryData); - - // mManufacturerSpecificData could not be null. - dest.writeInt(mManufacturerSpecificData.size()); - for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { - dest.writeInt(mManufacturerSpecificData.keyAt(i)); - dest.writeByteArray(mManufacturerSpecificData.valueAt(i)); - } - dest.writeInt(mServiceData.size()); - for (ParcelUuid uuid : mServiceData.keySet()) { - dest.writeTypedObject(uuid, flags); - dest.writeByteArray(mServiceData.get(uuid)); - } - dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); - dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); - } - - public static final @android.annotation.NonNull Parcelable.Creator<AdvertiseData> CREATOR = - new Creator<AdvertiseData>() { - @Override - public AdvertiseData[] newArray(int size) { - return new AdvertiseData[size]; - } - - @Override - public AdvertiseData createFromParcel(Parcel in) { - Builder builder = new Builder(); - ArrayList<ParcelUuid> uuids = in.createTypedArrayList(ParcelUuid.CREATOR); - for (ParcelUuid uuid : uuids) { - builder.addServiceUuid(uuid); - } - - ArrayList<ParcelUuid> solicitationUuids = in.createTypedArrayList(ParcelUuid.CREATOR); - for (ParcelUuid uuid : solicitationUuids) { - builder.addServiceSolicitationUuid(uuid); - } - - List<TransportDiscoveryData> transportDiscoveryData = - in.createTypedArrayList(TransportDiscoveryData.CREATOR); - for (TransportDiscoveryData tdd : transportDiscoveryData) { - builder.addTransportDiscoveryData(tdd); - } - - int manufacturerSize = in.readInt(); - for (int i = 0; i < manufacturerSize; ++i) { - int manufacturerId = in.readInt(); - byte[] manufacturerData = in.createByteArray(); - builder.addManufacturerData(manufacturerId, manufacturerData); - } - int serviceDataSize = in.readInt(); - for (int i = 0; i < serviceDataSize; ++i) { - ParcelUuid serviceDataUuid = in.readTypedObject(ParcelUuid.CREATOR); - byte[] serviceData = in.createByteArray(); - builder.addServiceData(serviceDataUuid, serviceData); - } - builder.setIncludeTxPowerLevel(in.readByte() == 1); - builder.setIncludeDeviceName(in.readByte() == 1); - return builder.build(); - } - }; - - /** - * Builder for {@link AdvertiseData}. - */ - public static final class Builder { - @Nullable - private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>(); - @NonNull - private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>(); - @Nullable - private List<TransportDiscoveryData> mTransportDiscoveryData = - new ArrayList<TransportDiscoveryData>(); - private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>(); - private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>(); - private boolean mIncludeTxPowerLevel; - private boolean mIncludeDeviceName; - - /** - * Add a service UUID to advertise data. - * - * @param serviceUuid A service UUID to be advertised. - * @throws IllegalArgumentException If the {@code serviceUuid} is null. - */ - public Builder addServiceUuid(ParcelUuid serviceUuid) { - if (serviceUuid == null) { - throw new IllegalArgumentException("serviceUuid is null"); - } - mServiceUuids.add(serviceUuid); - return this; - } - - /** - * Add a service solicitation UUID to advertise data. - * - * @param serviceSolicitationUuid A service solicitation UUID to be advertised. - * @throws IllegalArgumentException If the {@code serviceSolicitationUuid} is null. - */ - @NonNull - public Builder addServiceSolicitationUuid(@NonNull ParcelUuid serviceSolicitationUuid) { - if (serviceSolicitationUuid == null) { - throw new IllegalArgumentException("serviceSolicitationUuid is null"); - } - mServiceSolicitationUuids.add(serviceSolicitationUuid); - return this; - } - - /** - * Add service data to advertise data. - * - * @param serviceDataUuid 16-bit UUID of the service the data is associated with - * @param serviceData Service data - * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is - * empty. - */ - public Builder addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { - if (serviceDataUuid == null || serviceData == null) { - throw new IllegalArgumentException( - "serviceDataUuid or serviceDataUuid is null"); - } - mServiceData.put(serviceDataUuid, serviceData); - return this; - } - - /** - * Add Transport Discovery Data to advertise data. - * - * @param transportDiscoveryData Transport Discovery Data, consisting of one or more - * Transport Blocks. Transport Discovery Data AD Type Code is already included. - * @throws IllegalArgumentException If the {@code transportDiscoveryData} is empty - */ - @NonNull - public Builder addTransportDiscoveryData( - @NonNull TransportDiscoveryData transportDiscoveryData) { - if (transportDiscoveryData == null) { - throw new IllegalArgumentException("transportDiscoveryData is null"); - } - mTransportDiscoveryData.add(transportDiscoveryData); - return this; - } - - /** - * Add manufacturer specific data. - * <p> - * Please refer to the Bluetooth Assigned Numbers document provided by the <a - * href="https://www.bluetooth.org">Bluetooth SIG</a> for a list of existing company - * identifiers. - * - * @param manufacturerId Manufacturer ID assigned by Bluetooth SIG. - * @param manufacturerSpecificData Manufacturer specific data - * @throws IllegalArgumentException If the {@code manufacturerId} is negative or {@code - * manufacturerSpecificData} is null. - */ - public Builder addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) { - if (manufacturerId < 0) { - throw new IllegalArgumentException( - "invalid manufacturerId - " + manufacturerId); - } - if (manufacturerSpecificData == null) { - throw new IllegalArgumentException("manufacturerSpecificData is null"); - } - mManufacturerSpecificData.put(manufacturerId, manufacturerSpecificData); - return this; - } - - /** - * Whether the transmission power level should be included in the advertise packet. Tx power - * level field takes 3 bytes in advertise packet. - */ - public Builder setIncludeTxPowerLevel(boolean includeTxPowerLevel) { - mIncludeTxPowerLevel = includeTxPowerLevel; - return this; - } - - /** - * Set whether the device name should be included in advertise packet. - */ - public Builder setIncludeDeviceName(boolean includeDeviceName) { - mIncludeDeviceName = includeDeviceName; - return this; - } - - /** - * Build the {@link AdvertiseData}. - */ - public AdvertiseData build() { - return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids, - mTransportDiscoveryData, mManufacturerSpecificData, mServiceData, - mIncludeTxPowerLevel, mIncludeDeviceName); - } - } -} diff --git a/core/java/android/bluetooth/le/AdvertiseSettings.java b/core/java/android/bluetooth/le/AdvertiseSettings.java deleted file mode 100644 index c52a6ee33989..000000000000 --- a/core/java/android/bluetooth/le/AdvertiseSettings.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.bluetooth.le.AdvertisingSetParameters.AddressTypeStatus; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * The {@link AdvertiseSettings} provide a way to adjust advertising preferences for each - * Bluetooth LE advertisement instance. Use {@link AdvertiseSettings.Builder} to create an - * instance of this class. - */ -public final class AdvertiseSettings implements Parcelable { - /** - * Perform Bluetooth LE advertising in low power mode. This is the default and preferred - * advertising mode as it consumes the least power. - */ - public static final int ADVERTISE_MODE_LOW_POWER = 0; - - /** - * Perform Bluetooth LE advertising in balanced power mode. This is balanced between advertising - * frequency and power consumption. - */ - public static final int ADVERTISE_MODE_BALANCED = 1; - - /** - * Perform Bluetooth LE advertising in low latency, high power mode. This has the highest power - * consumption and should not be used for continuous background advertising. - */ - public static final int ADVERTISE_MODE_LOW_LATENCY = 2; - - /** - * Advertise using the lowest transmission (TX) power level. Low transmission power can be used - * to restrict the visibility range of advertising packets. - */ - public static final int ADVERTISE_TX_POWER_ULTRA_LOW = 0; - - /** - * Advertise using low TX power level. - */ - public static final int ADVERTISE_TX_POWER_LOW = 1; - - /** - * Advertise using medium TX power level. - */ - public static final int ADVERTISE_TX_POWER_MEDIUM = 2; - - /** - * Advertise using high TX power level. This corresponds to largest visibility range of the - * advertising packet. - */ - public static final int ADVERTISE_TX_POWER_HIGH = 3; - - /** - * The maximum limited advertisement duration as specified by the Bluetooth SIG - */ - 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, - @AddressTypeStatus int ownAddressType) { - mAdvertiseMode = advertiseMode; - mAdvertiseTxPowerLevel = advertiseTxPowerLevel; - mAdvertiseConnectable = advertiseConnectable; - mAdvertiseTimeoutMillis = advertiseTimeout; - mOwnAddressType = ownAddressType; - } - - private AdvertiseSettings(Parcel in) { - mAdvertiseMode = in.readInt(); - mAdvertiseTxPowerLevel = in.readInt(); - mAdvertiseConnectable = in.readInt() != 0; - mAdvertiseTimeoutMillis = in.readInt(); - mOwnAddressType = in.readInt(); - } - - /** - * Returns the advertise mode. - */ - public int getMode() { - return mAdvertiseMode; - } - - /** - * Returns the TX power level for advertising. - */ - public int getTxPowerLevel() { - return mAdvertiseTxPowerLevel; - } - - /** - * Returns whether the advertisement will indicate connectable. - */ - public boolean isConnectable() { - return mAdvertiseConnectable; - } - - /** - * Returns the advertising time limit in milliseconds. - */ - public int getTimeout() { - 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 - + ", mOwnAddressType=" + mOwnAddressType + "]"; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mAdvertiseMode); - dest.writeInt(mAdvertiseTxPowerLevel); - dest.writeInt(mAdvertiseConnectable ? 1 : 0); - dest.writeInt(mAdvertiseTimeoutMillis); - dest.writeInt(mOwnAddressType); - } - - public static final @android.annotation.NonNull Parcelable.Creator<AdvertiseSettings> CREATOR = - new Creator<AdvertiseSettings>() { - @Override - public AdvertiseSettings[] newArray(int size) { - return new AdvertiseSettings[size]; - } - - @Override - public AdvertiseSettings createFromParcel(Parcel in) { - return new AdvertiseSettings(in); - } - }; - - /** - * Builder class for {@link AdvertiseSettings}. - */ - public static final class Builder { - private int mMode = ADVERTISE_MODE_LOW_POWER; - 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. - * - * @param advertiseMode Bluetooth LE Advertising mode, can only be one of {@link - * AdvertiseSettings#ADVERTISE_MODE_LOW_POWER}, - * {@link AdvertiseSettings#ADVERTISE_MODE_BALANCED}, - * or {@link AdvertiseSettings#ADVERTISE_MODE_LOW_LATENCY}. - * @throws IllegalArgumentException If the advertiseMode is invalid. - */ - public Builder setAdvertiseMode(int advertiseMode) { - if (advertiseMode < ADVERTISE_MODE_LOW_POWER - || advertiseMode > ADVERTISE_MODE_LOW_LATENCY) { - throw new IllegalArgumentException("unknown mode " + advertiseMode); - } - mMode = advertiseMode; - return this; - } - - /** - * Set advertise TX power level to control the transmission power level for the advertising. - * - * @param txPowerLevel Transmission power of Bluetooth LE Advertising, can only be one of - * {@link AdvertiseSettings#ADVERTISE_TX_POWER_ULTRA_LOW}, {@link - * AdvertiseSettings#ADVERTISE_TX_POWER_LOW}, - * {@link AdvertiseSettings#ADVERTISE_TX_POWER_MEDIUM} - * or {@link AdvertiseSettings#ADVERTISE_TX_POWER_HIGH}. - * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. - */ - public Builder setTxPowerLevel(int txPowerLevel) { - if (txPowerLevel < ADVERTISE_TX_POWER_ULTRA_LOW - || txPowerLevel > ADVERTISE_TX_POWER_HIGH) { - throw new IllegalArgumentException("unknown tx power level " + txPowerLevel); - } - mTxPowerLevel = txPowerLevel; - return this; - } - - /** - * Set whether the advertisement type should be connectable or non-connectable. - * - * @param connectable Controls whether the advertisment type will be connectable (true) or - * non-connectable (false). - */ - public Builder setConnectable(boolean connectable) { - mConnectable = connectable; - return this; - } - - /** - * Limit advertising to a given amount of time. - * - * @param timeoutMillis Advertising time limit. May not exceed 180000 milliseconds. A value - * of 0 will disable the time limit. - * @throws IllegalArgumentException If the provided timeout is over 180000 ms. - */ - public Builder setTimeout(int timeoutMillis) { - if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) { - throw new IllegalArgumentException("timeoutMillis invalid (must be 0-" - + LIMITED_ADVERTISING_MAX_MILLIS + " milliseconds)"); - } - mTimeoutMillis = timeoutMillis; - return this; - } - - /** - * 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, - mOwnAddressType); - } - } -} diff --git a/core/java/android/bluetooth/le/AdvertisingSet.java b/core/java/android/bluetooth/le/AdvertisingSet.java deleted file mode 100644 index bbdb6953afd1..000000000000 --- a/core/java/android/bluetooth/le/AdvertisingSet.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.RequiresNoPermission; -import android.annotation.RequiresPermission; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.IBluetoothGatt; -import android.bluetooth.IBluetoothManager; -import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.content.AttributionSource; -import android.os.RemoteException; -import android.util.Log; - -/** - * This class provides a way to control single Bluetooth LE advertising instance. - * <p> - * To get an instance of {@link AdvertisingSet}, call the - * {@link BluetoothLeAdvertiser#startAdvertisingSet} method. - * - * @see AdvertiseData - */ -public final class AdvertisingSet { - private static final String TAG = "AdvertisingSet"; - - private final IBluetoothGatt mGatt; - private int mAdvertiserId; - private AttributionSource mAttributionSource; - - /* package */ AdvertisingSet(int advertiserId, IBluetoothManager bluetoothManager, - AttributionSource attributionSource) { - mAdvertiserId = advertiserId; - mAttributionSource = attributionSource; - try { - mGatt = bluetoothManager.getBluetoothGatt(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get Bluetooth gatt - ", e); - throw new IllegalStateException("Failed to get Bluetooth"); - } - } - - /* package */ void setAdvertiserId(int advertiserId) { - mAdvertiserId = advertiserId; - } - - /** - * Enables Advertising. This method returns immediately, the operation status is - * delivered through {@code callback.onAdvertisingEnabled()}. - * - * @param enable whether the advertising should be enabled (true), or disabled (false) - * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535 - * (655,350 ms) - * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the - * controller shall attempt to send prior to terminating the extended advertising, even if the - * duration has not expired. Valid range is from 1 to 255. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void enableAdvertising(boolean enable, int duration, - int maxExtendedAdvertisingEvents) { - try { - mGatt.enableAdvertisingSet(mAdvertiserId, enable, duration, - maxExtendedAdvertisingEvents, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "remote exception - ", e); - } - } - - /** - * Set/update data being Advertised. Make sure that data doesn't exceed the size limit for - * specified AdvertisingSetParameters. This method returns immediately, the operation status is - * delivered through {@code callback.onAdvertisingDataSet()}. - * <p> - * Advertising data must be empty if non-legacy scannable advertising is used. - * - * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link - * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable, - * three bytes will be added for flags. If the update takes place when the advertising set is - * enabled, the data can be maximum 251 bytes long. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void setAdvertisingData(AdvertiseData advertiseData) { - try { - mGatt.setAdvertisingData(mAdvertiserId, advertiseData, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "remote exception - ", e); - } - } - - /** - * Set/update scan response data. Make sure that data doesn't exceed the size limit for - * specified AdvertisingSetParameters. This method returns immediately, the operation status - * is delivered through {@code callback.onScanResponseDataSet()}. - * - * @param scanResponse Scan response associated with the advertisement data. Size must not - * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place - * when the advertising set is enabled, the data can be maximum 251 bytes long. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void setScanResponseData(AdvertiseData scanResponse) { - try { - mGatt.setScanResponseData(mAdvertiserId, scanResponse, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "remote exception - ", e); - } - } - - /** - * Update advertising parameters associated with this AdvertisingSet. Must be called when - * advertising is not active. This method returns immediately, the operation status is delivered - * through {@code callback.onAdvertisingParametersUpdated}. - * - * @param parameters advertising set parameters. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void setAdvertisingParameters(AdvertisingSetParameters parameters) { - try { - mGatt.setAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "remote exception - ", e); - } - } - - /** - * Update periodic advertising parameters associated with this set. Must be called when - * periodic advertising is not enabled. This method returns immediately, the operation - * status is delivered through {@code callback.onPeriodicAdvertisingParametersUpdated()}. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) { - try { - mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "remote exception - ", e); - } - } - - /** - * Used to set periodic advertising data, must be called after setPeriodicAdvertisingParameters, - * or after advertising was started with periodic advertising data set. This method returns - * immediately, the operation status is delivered through - * {@code callback.onPeriodicAdvertisingDataSet()}. - * - * @param periodicData Periodic advertising data. Size must not exceed {@link - * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place when the - * periodic advertising is enabled for this set, the data can be maximum 251 bytes long. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void setPeriodicAdvertisingData(AdvertiseData periodicData) { - try { - mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "remote exception - ", e); - } - } - - /** - * Used to enable/disable periodic advertising. This method returns immediately, the operation - * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}. - * - * @param enable whether the periodic advertising should be enabled (true), or disabled - * (false). - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void setPeriodicAdvertisingEnabled(boolean enable) { - try { - mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "remote exception - ", e); - } - } - - /** - * Returns address associated with this advertising set. - * This method is exposed only for Bluetooth PTS tests, no app or system service - * should ever use it. - * - * @hide - */ - @RequiresBluetoothAdvertisePermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_ADVERTISE, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public void getOwnAddress() { - try { - mGatt.getOwnAddress(mAdvertiserId, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "remote exception - ", e); - } - } - - /** - * Returns advertiserId associated with this advertising set. - * - * @hide - */ - @RequiresNoPermission - public int getAdvertiserId() { - return mAdvertiserId; - } -} diff --git a/core/java/android/bluetooth/le/AdvertisingSetCallback.java b/core/java/android/bluetooth/le/AdvertisingSetCallback.java deleted file mode 100644 index 51324fdb01ff..000000000000 --- a/core/java/android/bluetooth/le/AdvertisingSetCallback.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -/** - * Bluetooth LE advertising set callbacks, used to deliver advertising operation - * status. - */ -public abstract class AdvertisingSetCallback { - - /** - * The requested operation was successful. - */ - public static final int ADVERTISE_SUCCESS = 0; - - /** - * Failed to start advertising as the advertise data to be broadcasted is too - * large. - */ - public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1; - - /** - * Failed to start advertising because no advertising instance is available. - */ - public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; - - /** - * Failed to start advertising as the advertising is already started. - */ - public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; - - /** - * Operation failed due to an internal error. - */ - public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4; - - /** - * This feature is not supported on this platform. - */ - public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; - - /** - * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet} - * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertisingSet - * contains the started set and it is advertising. If error occurred, advertisingSet is - * null, and status will be set to proper error code. - * - * @param advertisingSet The advertising set that was started or null if error. - * @param txPower tx power that will be used for this set. - * @param status Status of the operation. - */ - public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, int status) { - } - - /** - * Callback triggered in response to {@link BluetoothLeAdvertiser#stopAdvertisingSet} - * indicating advertising set is stopped. - * - * @param advertisingSet The advertising set. - */ - public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) { - } - - /** - * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet} - * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertising set is - * advertising. - * - * @param advertisingSet The advertising set. - * @param status Status of the operation. - */ - public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) { - } - - /** - * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating - * result of the operation. If status is ADVERTISE_SUCCESS, then data was changed. - * - * @param advertisingSet The advertising set. - * @param status Status of the operation. - */ - public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) { - } - - /** - * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating - * result of the operation. - * - * @param advertisingSet The advertising set. - * @param status Status of the operation. - */ - public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) { - } - - /** - * Callback triggered in response to {@link AdvertisingSet#setAdvertisingParameters} - * indicating result of the operation. - * - * @param advertisingSet The advertising set. - * @param txPower tx power that will be used for this set. - * @param status Status of the operation. - */ - public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet, - int txPower, int status) { - } - - /** - * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingParameters} - * indicating result of the operation. - * - * @param advertisingSet The advertising set. - * @param status Status of the operation. - */ - public void onPeriodicAdvertisingParametersUpdated(AdvertisingSet advertisingSet, int status) { - } - - /** - * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingData} - * indicating result of the operation. - * - * @param advertisingSet The advertising set. - * @param status Status of the operation. - */ - public void onPeriodicAdvertisingDataSet(AdvertisingSet advertisingSet, - int status) { - } - - /** - * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingEnabled} - * indicating result of the operation. - * - * @param advertisingSet The advertising set. - * @param status Status of the operation. - */ - public void onPeriodicAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, - int status) { - } - - /** - * Callback triggered in response to {@link AdvertisingSet#getOwnAddress()} - * indicating result of the operation. - * - * @param advertisingSet The advertising set. - * @param addressType type of address. - * @param address advertising set bluetooth address. - * @hide - */ - public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType, String address) { - } -} diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java deleted file mode 100644 index 5c8fae65193d..000000000000 --- a/core/java/android/bluetooth/le/AdvertisingSetParameters.java +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package 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 - * Bluetooth LE advertising set. Use {@link AdvertisingSetParameters.Builder} to - * create an - * instance of this class. - */ -public final class AdvertisingSetParameters implements Parcelable { - - /** - * Advertise on low frequency, around every 1000ms. This is the default and - * preferred advertising mode as it consumes the least power. - */ - public static final int INTERVAL_HIGH = 1600; - - /** - * Advertise on medium frequency, around every 250ms. This is balanced - * between advertising frequency and power consumption. - */ - public static final int INTERVAL_MEDIUM = 400; - - /** - * Perform high frequency, low latency advertising, around every 100ms. This - * has the highest power consumption and should not be used for continuous - * background advertising. - */ - public static final int INTERVAL_LOW = 160; - - /** - * Minimum value for advertising interval. - */ - public static final int INTERVAL_MIN = 160; - - /** - * Maximum value for advertising interval. - */ - public static final int INTERVAL_MAX = 16777215; - - /** - * Advertise using the lowest transmission (TX) power level. Low transmission - * power can be used to restrict the visibility range of advertising packets. - */ - public static final int TX_POWER_ULTRA_LOW = -21; - - /** - * Advertise using low TX power level. - */ - public static final int TX_POWER_LOW = -15; - - /** - * Advertise using medium TX power level. - */ - public static final int TX_POWER_MEDIUM = -7; - - /** - * Advertise using high TX power level. This corresponds to largest visibility - * range of the advertising packet. - */ - public static final int TX_POWER_HIGH = 1; - - /** - * Minimum value for TX power. - */ - public static final int TX_POWER_MIN = -127; - - /** - * Maximum value for TX power. - */ - public static final int TX_POWER_MAX = 1; - - /** - * The maximum limited advertisement duration as specified by the Bluetooth - * SIG - */ - 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; - private final int mPrimaryPhy; - private final int mSecondaryPhy; - private final boolean mConnectable; - 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, @AddressTypeStatus int ownAddressType) { - mConnectable = connectable; - mScannable = scannable; - mIsLegacy = isLegacy; - mIsAnonymous = isAnonymous; - mIncludeTxPower = includeTxPower; - mPrimaryPhy = primaryPhy; - mSecondaryPhy = secondaryPhy; - mInterval = interval; - mTxPowerLevel = txPowerLevel; - mOwnAddressType = ownAddressType; - } - - private AdvertisingSetParameters(Parcel in) { - mConnectable = in.readInt() != 0; - mScannable = in.readInt() != 0; - mIsLegacy = in.readInt() != 0; - mIsAnonymous = in.readInt() != 0; - mIncludeTxPower = in.readInt() != 0; - mPrimaryPhy = in.readInt(); - mSecondaryPhy = in.readInt(); - mInterval = in.readInt(); - mTxPowerLevel = in.readInt(); - mOwnAddressType = in.readInt(); - } - - /** - * Returns whether the advertisement will be connectable. - */ - public boolean isConnectable() { - return mConnectable; - } - - /** - * Returns whether the advertisement will be scannable. - */ - public boolean isScannable() { - return mScannable; - } - - /** - * Returns whether the legacy advertisement will be used. - */ - public boolean isLegacy() { - return mIsLegacy; - } - - /** - * Returns whether the advertisement will be anonymous. - */ - public boolean isAnonymous() { - return mIsAnonymous; - } - - /** - * Returns whether the TX Power will be included. - */ - public boolean includeTxPower() { - return mIncludeTxPower; - } - - /** - * Returns the primary advertising phy. - */ - public int getPrimaryPhy() { - return mPrimaryPhy; - } - - /** - * Returns the secondary advertising phy. - */ - public int getSecondaryPhy() { - return mSecondaryPhy; - } - - /** - * Returns the advertising interval. - */ - public int getInterval() { - return mInterval; - } - - /** - * Returns the TX power level for advertising. - */ - public int getTxPowerLevel() { - 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 - + ", isLegacy=" + mIsLegacy - + ", isAnonymous=" + mIsAnonymous - + ", includeTxPower=" + mIncludeTxPower - + ", primaryPhy=" + mPrimaryPhy - + ", secondaryPhy=" + mSecondaryPhy - + ", interval=" + mInterval - + ", txPowerLevel=" + mTxPowerLevel - + ", ownAddressType=" + mOwnAddressType + "]"; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mConnectable ? 1 : 0); - dest.writeInt(mScannable ? 1 : 0); - dest.writeInt(mIsLegacy ? 1 : 0); - dest.writeInt(mIsAnonymous ? 1 : 0); - dest.writeInt(mIncludeTxPower ? 1 : 0); - dest.writeInt(mPrimaryPhy); - dest.writeInt(mSecondaryPhy); - dest.writeInt(mInterval); - dest.writeInt(mTxPowerLevel); - dest.writeInt(mOwnAddressType); - } - - public static final @android.annotation.NonNull Parcelable.Creator<AdvertisingSetParameters> CREATOR = - new Creator<AdvertisingSetParameters>() { - @Override - public AdvertisingSetParameters[] newArray(int size) { - return new AdvertisingSetParameters[size]; - } - - @Override - public AdvertisingSetParameters createFromParcel(Parcel in) { - return new AdvertisingSetParameters(in); - } - }; - - /** - * Builder class for {@link AdvertisingSetParameters}. - */ - public static final class Builder { - private boolean mConnectable = false; - private boolean mScannable = false; - private boolean mIsLegacy = false; - private boolean mIsAnonymous = false; - private boolean mIncludeTxPower = false; - private int mPrimaryPhy = BluetoothDevice.PHY_LE_1M; - 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 - * non-connectable. - * Legacy advertisements can be both connectable and scannable. Non-legacy - * advertisements can be only scannable or only connectable. - * - * @param connectable Controls whether the advertisement type will be connectable (true) or - * non-connectable (false). - */ - public Builder setConnectable(boolean connectable) { - mConnectable = connectable; - return this; - } - - /** - * Set whether the advertisement type should be scannable. - * Legacy advertisements can be both connectable and scannable. Non-legacy - * advertisements can be only scannable or only connectable. - * - * @param scannable Controls whether the advertisement type will be scannable (true) or - * non-scannable (false). - */ - public Builder setScannable(boolean scannable) { - mScannable = scannable; - return this; - } - - /** - * When set to true, advertising set will advertise 4.x Spec compliant - * advertisements. - * - * @param isLegacy whether legacy advertising mode should be used. - */ - public Builder setLegacyMode(boolean isLegacy) { - mIsLegacy = isLegacy; - return this; - } - - /** - * Set whether advertiser address should be ommited from all packets. If this - * mode is used, periodic advertising can't be enabled for this set. - * - * This is used only if legacy mode is not used. - * - * @param isAnonymous whether anonymous advertising should be used. - */ - public Builder setAnonymous(boolean isAnonymous) { - mIsAnonymous = isAnonymous; - return this; - } - - /** - * Set whether TX power should be included in the extended header. - * - * This is used only if legacy mode is not used. - * - * @param includeTxPower whether TX power should be included in extended header - */ - public Builder setIncludeTxPower(boolean includeTxPower) { - mIncludeTxPower = includeTxPower; - return this; - } - - /** - * Set the primary physical channel used for this advertising set. - * - * This is used only if legacy mode is not used. - * - * Use {@link BluetoothAdapter#isLeCodedPhySupported} to determine if LE Coded PHY is - * supported on this device. - * - * @param primaryPhy Primary advertising physical channel, can only be {@link - * BluetoothDevice#PHY_LE_1M} or {@link BluetoothDevice#PHY_LE_CODED}. - * @throws IllegalArgumentException If the primaryPhy is invalid. - */ - public Builder setPrimaryPhy(int primaryPhy) { - if (primaryPhy != BluetoothDevice.PHY_LE_1M - && primaryPhy != BluetoothDevice.PHY_LE_CODED) { - throw new IllegalArgumentException("bad primaryPhy " + primaryPhy); - } - mPrimaryPhy = primaryPhy; - return this; - } - - /** - * Set the secondary physical channel used for this advertising set. - * - * This is used only if legacy mode is not used. - * - * Use {@link BluetoothAdapter#isLeCodedPhySupported} and - * {@link BluetoothAdapter#isLe2MPhySupported} to determine if LE Coded PHY or 2M PHY is - * supported on this device. - * - * @param secondaryPhy Secondary advertising physical channel, can only be one of {@link - * BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M} or {@link - * BluetoothDevice#PHY_LE_CODED}. - * @throws IllegalArgumentException If the secondaryPhy is invalid. - */ - public Builder setSecondaryPhy(int secondaryPhy) { - if (secondaryPhy != BluetoothDevice.PHY_LE_1M - && secondaryPhy != BluetoothDevice.PHY_LE_2M - && secondaryPhy != BluetoothDevice.PHY_LE_CODED) { - throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy); - } - mSecondaryPhy = secondaryPhy; - return this; - } - - /** - * Set advertising interval. - * - * @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid range is from - * 160 (100ms) to 16777215 (10,485.759375 s). Recommended values are: {@link - * AdvertisingSetParameters#INTERVAL_LOW}, {@link AdvertisingSetParameters#INTERVAL_MEDIUM}, - * or {@link AdvertisingSetParameters#INTERVAL_HIGH}. - * @throws IllegalArgumentException If the interval is invalid. - */ - public Builder setInterval(int interval) { - if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { - throw new IllegalArgumentException("unknown interval " + interval); - } - mInterval = interval; - return this; - } - - /** - * Set the transmission power level for the advertising. - * - * @param txPowerLevel Transmission power of Bluetooth LE Advertising, in dBm. The valid - * range is [-127, 1] Recommended values are: - * {@link AdvertisingSetParameters#TX_POWER_ULTRA_LOW}, - * {@link AdvertisingSetParameters#TX_POWER_LOW}, - * {@link AdvertisingSetParameters#TX_POWER_MEDIUM}, - * or {@link AdvertisingSetParameters#TX_POWER_HIGH}. - * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid. - */ - public Builder setTxPowerLevel(int txPowerLevel) { - if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) { - throw new IllegalArgumentException("unknown txPowerLevel " + txPowerLevel); - } - mTxPowerLevel = txPowerLevel; - return this; - } - - /** - * 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. - */ - public AdvertisingSetParameters build() { - if (mIsLegacy) { - if (mIsAnonymous) { - throw new IllegalArgumentException("Legacy advertising can't be anonymous"); - } - - if (mConnectable && !mScannable) { - throw new IllegalStateException( - "Legacy advertisement can't be connectable and non-scannable"); - } - - if (mIncludeTxPower) { - throw new IllegalStateException( - "Legacy advertising can't include TX power level in header"); - } - } else { - if (mConnectable && mScannable) { - throw new IllegalStateException( - "Advertising can't be both connectable and scannable"); - } - - if (mIsAnonymous && mConnectable) { - throw new IllegalStateException( - "Advertising can't be both connectable and anonymous"); - } - } - - return new AdvertisingSetParameters(mConnectable, mScannable, mIsLegacy, mIsAnonymous, - mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel, - mOwnAddressType); - } - } -} diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java deleted file mode 100644 index 879dceedaaec..000000000000 --- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ /dev/null @@ -1,756 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.RequiresNoPermission; -import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothUuid; -import android.bluetooth.IBluetoothGatt; -import android.bluetooth.IBluetoothManager; -import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.content.AttributionSource; -import android.os.Handler; -import android.os.Looper; -import android.os.ParcelUuid; -import android.os.RemoteException; -import android.util.Log; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * This class provides a way to perform Bluetooth LE advertise operations, such as starting and - * stopping advertising. An advertiser can broadcast up to 31 bytes of advertisement data - * represented by {@link AdvertiseData}. - * <p> - * To get an instance of {@link BluetoothLeAdvertiser}, call the - * {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method. - * - * @see AdvertiseData - */ -public final class BluetoothLeAdvertiser { - - private static final String TAG = "BluetoothLeAdvertiser"; - - private static final int MAX_ADVERTISING_DATA_BYTES = 1650; - private static final int MAX_LEGACY_ADVERTISING_DATA_BYTES = 31; - // Each fields need one byte for field length and another byte for field type. - private static final int OVERHEAD_BYTES_PER_FIELD = 2; - // Flags field will be set by system. - private static final int FLAGS_FIELD_BYTES = 3; - private static final int MANUFACTURER_SPECIFIC_DATA_LENGTH = 2; - - private final BluetoothAdapter mBluetoothAdapter; - private final IBluetoothManager mBluetoothManager; - private final AttributionSource mAttributionSource; - - private final Handler mHandler; - private final Map<AdvertiseCallback, AdvertisingSetCallback> - mLegacyAdvertisers = new HashMap<>(); - private final Map<AdvertisingSetCallback, IAdvertisingSetCallback> - mCallbackWrappers = Collections.synchronizedMap(new HashMap<>()); - private final Map<Integer, AdvertisingSet> - mAdvertisingSets = Collections.synchronizedMap(new HashMap<>()); - - /** - * Use BluetoothAdapter.getLeAdvertiser() instead. - * - * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management - * @hide - */ - public BluetoothLeAdvertiser(BluetoothAdapter bluetoothAdapter) { - mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter); - mBluetoothManager = mBluetoothAdapter.getBluetoothManager(); - mAttributionSource = mBluetoothAdapter.getAttributionSource(); - mHandler = new Handler(Looper.getMainLooper()); - } - - /** - * Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be broadcasted. - * Returns immediately, the operation status is delivered through {@code callback}. - * - * @param settings Settings for Bluetooth LE advertising. - * @param advertiseData Advertisement data to be broadcasted. - * @param callback Callback for advertising status. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void startAdvertising(AdvertiseSettings settings, - AdvertiseData advertiseData, final AdvertiseCallback callback) { - startAdvertising(settings, advertiseData, null, callback); - } - - /** - * Start Bluetooth LE Advertising. The {@code advertiseData} will be broadcasted if the - * operation succeeds. The {@code scanResponse} is returned when a scanning device sends an - * active scan request. This method returns immediately, the operation status is delivered - * through {@code callback}. - * - * @param settings Settings for Bluetooth LE advertising. - * @param advertiseData Advertisement data to be advertised in advertisement packet. - * @param scanResponse Scan response associated with the advertisement data. - * @param callback Callback for advertising status. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void startAdvertising(AdvertiseSettings settings, - AdvertiseData advertiseData, AdvertiseData scanResponse, - final AdvertiseCallback callback) { - synchronized (mLegacyAdvertisers) { - BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - boolean isConnectable = settings.isConnectable(); - if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES - || totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) { - postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE); - return; - } - if (mLegacyAdvertisers.containsKey(callback)) { - postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED); - return; - } - - AdvertisingSetParameters.Builder parameters = new AdvertisingSetParameters.Builder(); - 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) { - parameters.setInterval(400); // 250ms - } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) { - parameters.setInterval(160); // 100ms - } - - if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW) { - parameters.setTxPowerLevel(-21); - } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_LOW) { - parameters.setTxPowerLevel(-15); - } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) { - parameters.setTxPowerLevel(-7); - } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) { - parameters.setTxPowerLevel(1); - } - - int duration = 0; - int timeoutMillis = settings.getTimeout(); - if (timeoutMillis > 0) { - duration = (timeoutMillis < 10) ? 1 : timeoutMillis / 10; - } - - AdvertisingSetCallback wrapped = wrapOldCallback(callback, settings); - mLegacyAdvertisers.put(callback, wrapped); - startAdvertisingSet(parameters.build(), advertiseData, scanResponse, null, null, - duration, 0, wrapped); - } - } - - @SuppressLint({ - "AndroidFrameworkBluetoothPermission", - "AndroidFrameworkRequiresPermission", - }) - AdvertisingSetCallback wrapOldCallback(AdvertiseCallback callback, AdvertiseSettings settings) { - return new AdvertisingSetCallback() { - @Override - public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower, - int status) { - if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { - postStartFailure(callback, status); - return; - } - - postStartSuccess(callback, settings); - } - - /* Legacy advertiser is disabled on timeout */ - @Override - public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enabled, - int status) { - if (enabled) { - Log.e(TAG, "Legacy advertiser should be only disabled on timeout," - + " but was enabled!"); - return; - } - - stopAdvertising(callback); - } - - }; - } - - /** - * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in - * {@link BluetoothLeAdvertiser#startAdvertising}. - * - * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void stopAdvertising(final AdvertiseCallback callback) { - synchronized (mLegacyAdvertisers) { - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - AdvertisingSetCallback wrapper = mLegacyAdvertisers.get(callback); - if (wrapper == null) return; - - stopAdvertisingSet(wrapper); - - mLegacyAdvertisers.remove(callback); - } - } - - /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - * <p> - * - * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link - * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable, - * three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not - * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. - * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will - * not be started. - * @param periodicData Periodic advertising data. Size must not exceed {@link - * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. - * @param callback Callback for advertising set. - * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable - * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising - * feature is made when it's not supported by the controller. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void startAdvertisingSet(AdvertisingSetParameters parameters, - AdvertiseData advertiseData, AdvertiseData scanResponse, - PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, AdvertisingSetCallback callback) { - startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, 0, 0, callback, new Handler(Looper.getMainLooper())); - } - - /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - * <p> - * - * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link - * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable, - * three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not - * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. - * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will - * not be started. - * @param periodicData Periodic advertising data. Size must not exceed {@link - * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. - * @param callback Callback for advertising set. - * @param handler thread upon which the callbacks will be invoked. - * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable - * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising - * feature is made when it's not supported by the controller. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void startAdvertisingSet(AdvertisingSetParameters parameters, - AdvertiseData advertiseData, AdvertiseData scanResponse, - PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, AdvertisingSetCallback callback, - Handler handler) { - startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, 0, 0, callback, handler); - } - - /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - * <p> - * - * @param parameters advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link - * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable, - * three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not - * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. - * @param periodicParameters periodic advertisng parameters. If null, periodic advertising will - * not be started. - * @param periodicData Periodic advertising data. Size must not exceed {@link - * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. - * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535 - * (655,350 ms). 0 means advertising should continue until stopped. - * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the - * controller shall attempt to send prior to terminating the extended advertising, even if the - * duration has not expired. Valid range is from 1 to 255. 0 means no maximum. - * @param callback Callback for advertising set. - * @throws IllegalArgumentException when any of the data parameter exceed the maximum allowable - * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising - * feature is made when it's not supported by the controller. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void startAdvertisingSet(AdvertisingSetParameters parameters, - AdvertiseData advertiseData, AdvertiseData scanResponse, - PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, int duration, - int maxExtendedAdvertisingEvents, - AdvertisingSetCallback callback) { - startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, duration, maxExtendedAdvertisingEvents, callback, - new Handler(Looper.getMainLooper())); - } - - /** - * Creates a new advertising set. If operation succeed, device will start advertising. This - * method returns immediately, the operation status is delivered through - * {@code callback.onAdvertisingSetStarted()}. - * <p> - * - * @param parameters Advertising set parameters. - * @param advertiseData Advertisement data to be broadcasted. Size must not exceed {@link - * BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the advertisement is connectable, - * three bytes will be added for flags. - * @param scanResponse Scan response associated with the advertisement data. Size must not - * exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength} - * @param periodicParameters Periodic advertisng parameters. If null, periodic advertising will - * not be started. - * @param periodicData Periodic advertising data. Size must not exceed {@link - * BluetoothAdapter#getLeMaximumAdvertisingDataLength} - * @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535 - * (655,350 ms). 0 means advertising should continue until stopped. - * @param maxExtendedAdvertisingEvents maximum number of extended advertising events the - * controller shall attempt to send prior to terminating the extended advertising, even if the - * duration has not expired. Valid range is from 1 to 255. 0 means no maximum. - * @param callback Callback for advertising set. - * @param handler Thread upon which the callbacks will be invoked. - * @throws IllegalArgumentException When any of the data parameter exceed the maximum allowable - * size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising - * feature is made when it's not supported by the controller, or when - * maxExtendedAdvertisingEvents is used on a controller that doesn't support the LE Extended - * Advertising - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void startAdvertisingSet(AdvertisingSetParameters parameters, - AdvertiseData advertiseData, AdvertiseData scanResponse, - PeriodicAdvertisingParameters periodicParameters, - AdvertiseData periodicData, int duration, - int maxExtendedAdvertisingEvents, AdvertisingSetCallback callback, - Handler handler) { - BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - - boolean isConnectable = parameters.isConnectable(); - if (parameters.isLegacy()) { - if (totalBytes(advertiseData, isConnectable) > MAX_LEGACY_ADVERTISING_DATA_BYTES) { - throw new IllegalArgumentException("Legacy advertising data too big"); - } - - if (totalBytes(scanResponse, false) > MAX_LEGACY_ADVERTISING_DATA_BYTES) { - throw new IllegalArgumentException("Legacy scan response data too big"); - } - } else { - boolean supportCodedPhy = mBluetoothAdapter.isLeCodedPhySupported(); - boolean support2MPhy = mBluetoothAdapter.isLe2MPhySupported(); - int pphy = parameters.getPrimaryPhy(); - int sphy = parameters.getSecondaryPhy(); - if (pphy == BluetoothDevice.PHY_LE_CODED && !supportCodedPhy) { - throw new IllegalArgumentException("Unsupported primary PHY selected"); - } - - if ((sphy == BluetoothDevice.PHY_LE_CODED && !supportCodedPhy) - || (sphy == BluetoothDevice.PHY_LE_2M && !support2MPhy)) { - throw new IllegalArgumentException("Unsupported secondary PHY selected"); - } - - int maxData = mBluetoothAdapter.getLeMaximumAdvertisingDataLength(); - if (totalBytes(advertiseData, isConnectable) > maxData) { - throw new IllegalArgumentException("Advertising data too big"); - } - - if (totalBytes(scanResponse, false) > maxData) { - throw new IllegalArgumentException("Scan response data too big"); - } - - if (totalBytes(periodicData, false) > maxData) { - throw new IllegalArgumentException("Periodic advertising data too big"); - } - - boolean supportPeriodic = mBluetoothAdapter.isLePeriodicAdvertisingSupported(); - if (periodicParameters != null && !supportPeriodic) { - throw new IllegalArgumentException( - "Controller does not support LE Periodic Advertising"); - } - } - - if (maxExtendedAdvertisingEvents < 0 || maxExtendedAdvertisingEvents > 255) { - throw new IllegalArgumentException( - "maxExtendedAdvertisingEvents out of range: " + maxExtendedAdvertisingEvents); - } - - if (maxExtendedAdvertisingEvents != 0 - && !mBluetoothAdapter.isLePeriodicAdvertisingSupported()) { - throw new IllegalArgumentException( - "Can't use maxExtendedAdvertisingEvents with controller that don't support " - + "LE Extended Advertising"); - } - - if (duration < 0 || duration > 65535) { - throw new IllegalArgumentException("duration out of range: " + duration); - } - - IBluetoothGatt gatt; - try { - gatt = mBluetoothManager.getBluetoothGatt(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get Bluetooth GATT - ", e); - postStartSetFailure(handler, callback, - AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); - return; - } - - if (gatt == null) { - Log.e(TAG, "Bluetooth GATT is null"); - postStartSetFailure(handler, callback, - AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); - return; - } - - IAdvertisingSetCallback wrapped = wrap(callback, handler); - if (mCallbackWrappers.putIfAbsent(callback, wrapped) != null) { - throw new IllegalArgumentException( - "callback instance already associated with advertising"); - } - - try { - gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters, - periodicData, duration, maxExtendedAdvertisingEvents, wrapped, - mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Failed to start advertising set - ", e); - postStartSetFailure(handler, callback, - AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR); - return; - } - } - - /** - * Used to dispose of a {@link AdvertisingSet} object, obtained with {@link - * BluetoothLeAdvertiser#startAdvertisingSet}. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - public void stopAdvertisingSet(AdvertisingSetCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null"); - } - - IAdvertisingSetCallback wrapped = mCallbackWrappers.remove(callback); - if (wrapped == null) { - return; - } - - IBluetoothGatt gatt; - try { - gatt = mBluetoothManager.getBluetoothGatt(); - gatt.stopAdvertisingSet(wrapped, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Failed to stop advertising - ", e); - } - } - - /** - * Cleans up advertisers. Should be called when bluetooth is down. - * - * @hide - */ - @RequiresNoPermission - public void cleanup() { - mLegacyAdvertisers.clear(); - mCallbackWrappers.clear(); - mAdvertisingSets.clear(); - } - - // Compute the size of advertisement data or scan resp - @RequiresBluetoothAdvertisePermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) - private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) { - if (data == null) return 0; - // Flags field is omitted if the advertising is not connectable. - int size = (isFlagsIncluded) ? FLAGS_FIELD_BYTES : 0; - if (data.getServiceUuids() != null) { - int num16BitUuids = 0; - int num32BitUuids = 0; - int num128BitUuids = 0; - for (ParcelUuid uuid : data.getServiceUuids()) { - if (BluetoothUuid.is16BitUuid(uuid)) { - ++num16BitUuids; - } else if (BluetoothUuid.is32BitUuid(uuid)) { - ++num32BitUuids; - } else { - ++num128BitUuids; - } - } - // 16 bit service uuids are grouped into one field when doing advertising. - if (num16BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; - } - // 32 bit service uuids are grouped into one field when doing advertising. - if (num32BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; - } - // 128 bit service uuids are grouped into one field when doing advertising. - if (num128BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD - + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; - } - } - if (data.getServiceSolicitationUuids() != null) { - int num16BitUuids = 0; - int num32BitUuids = 0; - int num128BitUuids = 0; - for (ParcelUuid uuid : data.getServiceSolicitationUuids()) { - if (BluetoothUuid.is16BitUuid(uuid)) { - ++num16BitUuids; - } else if (BluetoothUuid.is32BitUuid(uuid)) { - ++num32BitUuids; - } else { - ++num128BitUuids; - } - } - // 16 bit service uuids are grouped into one field when doing advertising. - if (num16BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + num16BitUuids * BluetoothUuid.UUID_BYTES_16_BIT; - } - // 32 bit service uuids are grouped into one field when doing advertising. - if (num32BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD + num32BitUuids * BluetoothUuid.UUID_BYTES_32_BIT; - } - // 128 bit service uuids are grouped into one field when doing advertising. - if (num128BitUuids != 0) { - size += OVERHEAD_BYTES_PER_FIELD - + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; - } - } - for (TransportDiscoveryData transportDiscoveryData : data.getTransportDiscoveryData()) { - size += OVERHEAD_BYTES_PER_FIELD + transportDiscoveryData.totalBytes(); - } - for (ParcelUuid uuid : data.getServiceData().keySet()) { - int uuidLen = BluetoothUuid.uuidToBytes(uuid).length; - size += OVERHEAD_BYTES_PER_FIELD + uuidLen - + byteLength(data.getServiceData().get(uuid)); - } - for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) { - size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH - + byteLength(data.getManufacturerSpecificData().valueAt(i)); - } - if (data.getIncludeTxPowerLevel()) { - size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte. - } - if (data.getIncludeDeviceName()) { - final int length = mBluetoothAdapter.getNameLengthForAdvertise(); - if (length >= 0) { - size += OVERHEAD_BYTES_PER_FIELD + length; - } - } - return size; - } - - private int byteLength(byte[] array) { - return array == null ? 0 : array.length; - } - - @SuppressLint("AndroidFrameworkBluetoothPermission") - IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) { - return new IAdvertisingSetCallback.Stub() { - @Override - public void onAdvertisingSetStarted(int advertiserId, int txPower, int status) { - handler.post(new Runnable() { - @Override - public void run() { - if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) { - callback.onAdvertisingSetStarted(null, 0, status); - mCallbackWrappers.remove(callback); - return; - } - - AdvertisingSet advertisingSet = new AdvertisingSet( - advertiserId, mBluetoothManager, mAttributionSource); - mAdvertisingSets.put(advertiserId, advertisingSet); - callback.onAdvertisingSetStarted(advertisingSet, txPower, status); - } - }); - } - - @Override - public void onOwnAddressRead(int advertiserId, int addressType, String address) { - handler.post(new Runnable() { - @Override - public void run() { - AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onOwnAddressRead(advertisingSet, addressType, address); - } - }); - } - - @Override - public void onAdvertisingSetStopped(int advertiserId) { - handler.post(new Runnable() { - @Override - public void run() { - AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onAdvertisingSetStopped(advertisingSet); - mAdvertisingSets.remove(advertiserId); - mCallbackWrappers.remove(callback); - } - }); - } - - @Override - public void onAdvertisingEnabled(int advertiserId, boolean enabled, int status) { - handler.post(new Runnable() { - @Override - public void run() { - AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onAdvertisingEnabled(advertisingSet, enabled, status); - } - }); - } - - @Override - public void onAdvertisingDataSet(int advertiserId, int status) { - handler.post(new Runnable() { - @Override - public void run() { - AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onAdvertisingDataSet(advertisingSet, status); - } - }); - } - - @Override - public void onScanResponseDataSet(int advertiserId, int status) { - handler.post(new Runnable() { - @Override - public void run() { - AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onScanResponseDataSet(advertisingSet, status); - } - }); - } - - @Override - public void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) { - handler.post(new Runnable() { - @Override - public void run() { - AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onAdvertisingParametersUpdated(advertisingSet, txPower, status); - } - }); - } - - @Override - public void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) { - handler.post(new Runnable() { - @Override - public void run() { - AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onPeriodicAdvertisingParametersUpdated(advertisingSet, status); - } - }); - } - - @Override - public void onPeriodicAdvertisingDataSet(int advertiserId, int status) { - handler.post(new Runnable() { - @Override - public void run() { - AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onPeriodicAdvertisingDataSet(advertisingSet, status); - } - }); - } - - @Override - public void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) { - handler.post(new Runnable() { - @Override - public void run() { - AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId); - callback.onPeriodicAdvertisingEnabled(advertisingSet, enable, status); - } - }); - } - }; - } - - @SuppressLint("AndroidFrameworkBluetoothPermission") - private void postStartSetFailure(Handler handler, final AdvertisingSetCallback callback, - final int error) { - handler.post(new Runnable() { - @Override - public void run() { - callback.onAdvertisingSetStarted(null, 0, error); - } - }); - } - - @SuppressLint("AndroidFrameworkBluetoothPermission") - private void postStartFailure(final AdvertiseCallback callback, final int error) { - mHandler.post(new Runnable() { - @Override - public void run() { - callback.onStartFailure(error); - } - }); - } - - @SuppressLint("AndroidFrameworkBluetoothPermission") - private void postStartSuccess(final AdvertiseCallback callback, - final AdvertiseSettings settings) { - mHandler.post(new Runnable() { - - @Override - public void run() { - callback.onStartSuccess(settings); - } - }); - } -} diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java deleted file mode 100644 index 540e5a778c27..000000000000 --- a/core/java/android/bluetooth/le/BluetoothLeScanner.java +++ /dev/null @@ -1,658 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package android.bluetooth.le; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresNoPermission; -import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.app.PendingIntent; -import android.bluetooth.Attributable; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothGatt; -import android.bluetooth.IBluetoothGatt; -import android.bluetooth.IBluetoothManager; -import android.bluetooth.annotations.RequiresBluetoothLocationPermission; -import android.bluetooth.annotations.RequiresBluetoothScanPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.content.AttributionSource; -import android.os.Handler; -import android.os.Looper; -import android.os.RemoteException; -import android.os.WorkSource; -import android.util.Log; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * This class provides methods to perform scan related operations for Bluetooth LE devices. An - * application can scan for a particular type of Bluetooth LE devices using {@link ScanFilter}. It - * can also request different types of callbacks for delivering the result. - * <p> - * Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of - * {@link BluetoothLeScanner}. - * - * @see ScanFilter - */ -public final class BluetoothLeScanner { - - private static final String TAG = "BluetoothLeScanner"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - /** - * Extra containing a list of ScanResults. It can have one or more results if there was no - * error. In case of error, {@link #EXTRA_ERROR_CODE} will contain the error code and this - * extra will not be available. - */ - public static final String EXTRA_LIST_SCAN_RESULT = - "android.bluetooth.le.extra.LIST_SCAN_RESULT"; - - /** - * Optional extra indicating the error code, if any. The error code will be one of the - * SCAN_FAILED_* codes in {@link ScanCallback}. - */ - public static final String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE"; - - /** - * Optional extra indicating the callback type, which will be one of - * CALLBACK_TYPE_* constants in {@link ScanSettings}. - * - * @see ScanCallback#onScanResult(int, ScanResult) - */ - public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE"; - - private final BluetoothAdapter mBluetoothAdapter; - private final IBluetoothManager mBluetoothManager; - private final AttributionSource mAttributionSource; - - private final Handler mHandler; - private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients; - - /** - * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. - * - * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. - * @param opPackageName The opPackageName of the context this object was created from - * @param featureId The featureId of the context this object was created from - * @hide - */ - public BluetoothLeScanner(BluetoothAdapter bluetoothAdapter) { - mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter); - mBluetoothManager = mBluetoothAdapter.getBluetoothManager(); - mAttributionSource = mBluetoothAdapter.getAttributionSource(); - mHandler = new Handler(Looper.getMainLooper()); - mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>(); - } - - /** - * Start Bluetooth LE scan with default parameters and no filters. The scan results will be - * delivered through {@code callback}. For unfiltered scans, scanning is stopped on screen - * off to save power. Scanning is resumed when screen is turned on again. To avoid this, use - * {@link #startScan(List, ScanSettings, ScanCallback)} with desired {@link ScanFilter}. - * <p> - * An app must have - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission - * in order to get results. An App targeting Android Q or later must have - * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission - * in order to get results. - * - * @param callback Callback used to deliver scan results. - * @throws IllegalArgumentException If {@code callback} is null. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresBluetoothLocationPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void startScan(final ScanCallback callback) { - startScan(null, new ScanSettings.Builder().build(), callback); - } - - /** - * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}. - * For unfiltered scans, scanning is stopped on screen off to save power. Scanning is - * resumed when screen is turned on again. To avoid this, do filetered scanning by - * using proper {@link ScanFilter}. - * <p> - * An app must have - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission - * in order to get results. An App targeting Android Q or later must have - * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission - * in order to get results. - * - * @param filters {@link ScanFilter}s for finding exact BLE devices. - * @param settings Settings for the scan. - * @param callback Callback used to deliver scan results. - * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresBluetoothLocationPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void startScan(List<ScanFilter> filters, ScanSettings settings, - final ScanCallback callback) { - startScan(filters, settings, null, callback, /*callbackIntent=*/ null); - } - - /** - * Start Bluetooth LE scan using a {@link PendingIntent}. The scan results will be delivered via - * the PendingIntent. Use this method of scanning if your process is not always running and it - * should be started when scan results are available. - * <p> - * An app must have - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission - * in order to get results. An App targeting Android Q or later must have - * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission - * in order to get results. - * <p> - * When the PendingIntent is delivered, the Intent passed to the receiver or activity - * will contain one or more of the extras {@link #EXTRA_CALLBACK_TYPE}, - * {@link #EXTRA_ERROR_CODE} and {@link #EXTRA_LIST_SCAN_RESULT} to indicate the result of - * the scan. - * - * @param filters Optional list of ScanFilters for finding exact BLE devices. - * @param settings Optional settings for the scan. - * @param callbackIntent The PendingIntent to deliver the result to. - * @return Returns 0 for success or an error code from {@link ScanCallback} if the scan request - * could not be sent. - * @see #stopScan(PendingIntent) - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresBluetoothLocationPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public int startScan(@Nullable List<ScanFilter> filters, @Nullable ScanSettings settings, - @NonNull PendingIntent callbackIntent) { - return startScan(filters, - settings != null ? settings : new ScanSettings.Builder().build(), - null, null, callbackIntent); - } - - /** - * Start Bluetooth LE scan. Same as {@link #startScan(ScanCallback)} but allows the caller to - * specify on behalf of which application(s) the work is being done. - * - * @param workSource {@link WorkSource} identifying the application(s) for which to blame for - * the scan. - * @param callback Callback used to deliver scan results. - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresBluetoothLocationPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_SCAN, - android.Manifest.permission.UPDATE_DEVICE_STATS - }) - public void startScanFromSource(final WorkSource workSource, final ScanCallback callback) { - startScanFromSource(null, new ScanSettings.Builder().build(), workSource, callback); - } - - /** - * Start Bluetooth LE scan. Same as {@link #startScan(List, ScanSettings, ScanCallback)} but - * allows the caller to specify on behalf of which application(s) the work is being done. - * - * @param filters {@link ScanFilter}s for finding exact BLE devices. - * @param settings Settings for the scan. - * @param workSource {@link WorkSource} identifying the application(s) for which to blame for - * the scan. - * @param callback Callback used to deliver scan results. - * @hide - */ - @SystemApi - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresBluetoothLocationPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_SCAN, - android.Manifest.permission.UPDATE_DEVICE_STATS - }) - @SuppressLint("AndroidFrameworkRequiresPermission") - public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings, - final WorkSource workSource, final ScanCallback callback) { - startScan(filters, settings, workSource, callback, null); - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - private int startScan(List<ScanFilter> filters, ScanSettings settings, - final WorkSource workSource, final ScanCallback callback, - final PendingIntent callbackIntent) { - BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); - if (callback == null && callbackIntent == null) { - throw new IllegalArgumentException("callback is null"); - } - if (settings == null) { - throw new IllegalArgumentException("settings is null"); - } - synchronized (mLeScanClients) { - if (callback != null && mLeScanClients.containsKey(callback)) { - return postCallbackErrorOrReturn(callback, - ScanCallback.SCAN_FAILED_ALREADY_STARTED); - } - IBluetoothGatt gatt; - try { - gatt = mBluetoothManager.getBluetoothGatt(); - } catch (RemoteException e) { - gatt = null; - } - if (gatt == null) { - return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); - } - if (!isSettingsConfigAllowedForScan(settings)) { - return postCallbackErrorOrReturn(callback, - ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); - } - if (!isHardwareResourcesAvailableForScan(settings)) { - return postCallbackErrorOrReturn(callback, - ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES); - } - if (!isSettingsAndFilterComboAllowed(settings, filters)) { - return postCallbackErrorOrReturn(callback, - ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); - } - if (callback != null) { - BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, - settings, workSource, callback); - wrapper.startRegistration(); - } else { - try { - gatt.startScanForIntent(callbackIntent, settings, filters, - mAttributionSource); - } catch (RemoteException e) { - return ScanCallback.SCAN_FAILED_INTERNAL_ERROR; - } - } - } - return ScanCallback.NO_ERROR; - } - - /** - * Stops an ongoing Bluetooth LE scan. - * - * @param callback - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void stopScan(ScanCallback callback) { - BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); - synchronized (mLeScanClients) { - BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback); - if (wrapper == null) { - if (DBG) Log.d(TAG, "could not find callback wrapper"); - return; - } - wrapper.stopLeScan(); - } - } - - /** - * Stops an ongoing Bluetooth LE scan started using a PendingIntent. When creating the - * PendingIntent parameter, please do not use the FLAG_CANCEL_CURRENT flag. Otherwise, the stop - * scan may have no effect. - * - * @param callbackIntent The PendingIntent that was used to start the scan. - * @see #startScan(List, ScanSettings, PendingIntent) - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void stopScan(PendingIntent callbackIntent) { - BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); - IBluetoothGatt gatt; - try { - gatt = mBluetoothManager.getBluetoothGatt(); - gatt.stopScanForIntent(callbackIntent, mAttributionSource); - } catch (RemoteException e) { - } - } - - /** - * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth - * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data - * will be delivered through the {@code callback}. - * - * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one - * used to start scan. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void flushPendingScanResults(ScanCallback callback) { - BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); - if (callback == null) { - throw new IllegalArgumentException("callback cannot be null!"); - } - synchronized (mLeScanClients) { - BleScanCallbackWrapper wrapper = mLeScanClients.get(callback); - if (wrapper == null) { - return; - } - wrapper.flushPendingBatchResults(); - } - } - - /** - * Start truncated scan. - * - * @deprecated this is not used anywhere - * - * @hide - */ - @Deprecated - @SystemApi - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void startTruncatedScan(List<TruncatedFilter> truncatedFilters, ScanSettings settings, - final ScanCallback callback) { - int filterSize = truncatedFilters.size(); - List<ScanFilter> scanFilters = new ArrayList<ScanFilter>(filterSize); - for (TruncatedFilter filter : truncatedFilters) { - scanFilters.add(filter.getFilter()); - } - startScan(scanFilters, settings, null, callback, null); - } - - /** - * Cleans up scan clients. Should be called when bluetooth is down. - * - * @hide - */ - @RequiresNoPermission - public void cleanup() { - mLeScanClients.clear(); - } - - /** - * Bluetooth GATT interface callbacks - */ - @SuppressLint("AndroidFrameworkRequiresPermission") - private class BleScanCallbackWrapper extends IScannerCallback.Stub { - private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000; - - private final ScanCallback mScanCallback; - private final List<ScanFilter> mFilters; - private final WorkSource mWorkSource; - private ScanSettings mSettings; - private IBluetoothGatt mBluetoothGatt; - - // mLeHandle 0: not registered - // -2: registration failed because app is scanning to frequently - // -1: scan stopped or registration failed - // > 0: registered and scan started - private int mScannerId; - - public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, - List<ScanFilter> filters, ScanSettings settings, - WorkSource workSource, ScanCallback scanCallback) { - mBluetoothGatt = bluetoothGatt; - mFilters = filters; - mSettings = settings; - mWorkSource = workSource; - mScanCallback = scanCallback; - mScannerId = 0; - } - - public void startRegistration() { - synchronized (this) { - // Scan stopped. - if (mScannerId == -1 || mScannerId == -2) return; - try { - mBluetoothGatt.registerScanner(this, mWorkSource, mAttributionSource); - wait(REGISTRATION_CALLBACK_TIMEOUT_MILLIS); - } catch (InterruptedException | RemoteException e) { - Log.e(TAG, "application registeration exception", e); - postCallbackError(mScanCallback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); - } - if (mScannerId > 0) { - mLeScanClients.put(mScanCallback, this); - } else { - // Registration timed out or got exception, reset RscannerId to -1 so no - // subsequent operations can proceed. - if (mScannerId == 0) mScannerId = -1; - - // If scanning too frequently, don't report anything to the app. - if (mScannerId == -2) return; - - postCallbackError(mScanCallback, - ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED); - } - } - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void stopLeScan() { - synchronized (this) { - if (mScannerId <= 0) { - Log.e(TAG, "Error state, mLeHandle: " + mScannerId); - return; - } - try { - mBluetoothGatt.stopScan(mScannerId, mAttributionSource); - mBluetoothGatt.unregisterScanner(mScannerId, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Failed to stop scan and unregister", e); - } - mScannerId = -1; - } - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - void flushPendingBatchResults() { - synchronized (this) { - if (mScannerId <= 0) { - Log.e(TAG, "Error state, mLeHandle: " + mScannerId); - return; - } - try { - mBluetoothGatt.flushPendingBatchResults(mScannerId, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get pending scan results", e); - } - } - } - - /** - * Application interface registered - app is ready to go - */ - @Override - public void onScannerRegistered(int status, int scannerId) { - Log.d(TAG, "onScannerRegistered() - status=" + status - + " scannerId=" + scannerId + " mScannerId=" + mScannerId); - synchronized (this) { - if (status == BluetoothGatt.GATT_SUCCESS) { - try { - if (mScannerId == -1) { - // Registration succeeds after timeout, unregister scanner. - mBluetoothGatt.unregisterScanner(scannerId, mAttributionSource); - } else { - mScannerId = scannerId; - mBluetoothGatt.startScan(mScannerId, mSettings, mFilters, - mAttributionSource); - } - } catch (RemoteException e) { - Log.e(TAG, "fail to start le scan: " + e); - mScannerId = -1; - } - } else if (status == ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY) { - // applicaiton was scanning too frequently - mScannerId = -2; - } else { - // registration failed - mScannerId = -1; - } - notifyAll(); - } - } - - /** - * Callback reporting an LE scan result. - * - * @hide - */ - @Override - public void onScanResult(final ScanResult scanResult) { - Attributable.setAttributionSource(scanResult, mAttributionSource); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "onScanResult() - mScannerId=" + mScannerId); - } - if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); - - // Check null in case the scan has been stopped - synchronized (this) { - if (mScannerId <= 0) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Ignoring result as scan stopped."); - } - return; - }; - } - Handler handler = new Handler(Looper.getMainLooper()); - handler.post(new Runnable() { - @Override - public void run() { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "onScanResult() - handler run"); - } - mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); - } - }); - } - - @Override - public void onBatchScanResults(final List<ScanResult> results) { - Attributable.setAttributionSource(results, mAttributionSource); - Handler handler = new Handler(Looper.getMainLooper()); - handler.post(new Runnable() { - @Override - public void run() { - mScanCallback.onBatchScanResults(results); - } - }); - } - - @Override - public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) { - Attributable.setAttributionSource(scanResult, mAttributionSource); - if (VDBG) { - Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + " " + scanResult.toString()); - } - - // Check null in case the scan has been stopped - synchronized (this) { - if (mScannerId <= 0) { - return; - } - } - Handler handler = new Handler(Looper.getMainLooper()); - handler.post(new Runnable() { - @Override - public void run() { - if (onFound) { - mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, - scanResult); - } else { - mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, - scanResult); - } - } - }); - } - - @Override - public void onScanManagerErrorCallback(final int errorCode) { - if (VDBG) { - Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode); - } - synchronized (this) { - if (mScannerId <= 0) { - return; - } - } - postCallbackError(mScanCallback, errorCode); - } - } - - private int postCallbackErrorOrReturn(final ScanCallback callback, final int errorCode) { - if (callback == null) { - return errorCode; - } else { - postCallbackError(callback, errorCode); - return ScanCallback.NO_ERROR; - } - } - - @SuppressLint("AndroidFrameworkBluetoothPermission") - private void postCallbackError(final ScanCallback callback, final int errorCode) { - mHandler.post(new Runnable() { - @Override - public void run() { - callback.onScanFailed(errorCode); - } - }); - } - - private boolean isSettingsConfigAllowedForScan(ScanSettings settings) { - if (mBluetoothAdapter.isOffloadedFilteringSupported()) { - return true; - } - final int callbackType = settings.getCallbackType(); - // Only support regular scan if no offloaded filter support. - if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES - && settings.getReportDelayMillis() == 0) { - return true; - } - return false; - } - - private boolean isSettingsAndFilterComboAllowed(ScanSettings settings, - List<ScanFilter> filterList) { - final int callbackType = settings.getCallbackType(); - // If onlost/onfound is requested, a non-empty filter is expected - if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH - | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) { - if (filterList == null) { - return false; - } - for (ScanFilter filter : filterList) { - if (filter.isAllFieldsEmpty()) { - return false; - } - } - } - return true; - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) { - final int callbackType = settings.getCallbackType(); - if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0 - || (callbackType & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) { - // For onlost/onfound, we required hw support be available - return (mBluetoothAdapter.isOffloadedFilteringSupported() - && mBluetoothAdapter.isHardwareTrackingFiltersAvailable()); - } - return true; - } -} diff --git a/core/java/android/bluetooth/le/BluetoothLeUtils.java b/core/java/android/bluetooth/le/BluetoothLeUtils.java deleted file mode 100644 index ed50b09597bb..000000000000 --- a/core/java/android/bluetooth/le/BluetoothLeUtils.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.bluetooth.BluetoothAdapter; -import android.util.SparseArray; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; - -/** - * Helper class for Bluetooth LE utils. - * - * @hide - */ -public class BluetoothLeUtils { - - /** - * Returns a string composed from a {@link SparseArray}. - */ - static String toString(SparseArray<byte[]> array) { - if (array == null) { - return "null"; - } - if (array.size() == 0) { - return "{}"; - } - StringBuilder buffer = new StringBuilder(); - buffer.append('{'); - for (int i = 0; i < array.size(); ++i) { - buffer.append(array.keyAt(i)).append("=").append(Arrays.toString(array.valueAt(i))); - } - buffer.append('}'); - return buffer.toString(); - } - - /** - * Returns a string composed from a {@link Map}. - */ - static <T> String toString(Map<T, byte[]> map) { - if (map == null) { - return "null"; - } - if (map.isEmpty()) { - return "{}"; - } - StringBuilder buffer = new StringBuilder(); - buffer.append('{'); - Iterator<Map.Entry<T, byte[]>> it = map.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry<T, byte[]> entry = it.next(); - Object key = entry.getKey(); - buffer.append(key).append("=").append(Arrays.toString(map.get(key))); - if (it.hasNext()) { - buffer.append(", "); - } - } - buffer.append('}'); - return buffer.toString(); - } - - /** - * Check whether two {@link SparseArray} equal. - */ - static boolean equals(SparseArray<byte[]> array, SparseArray<byte[]> otherArray) { - if (array == otherArray) { - return true; - } - if (array == null || otherArray == null) { - return false; - } - if (array.size() != otherArray.size()) { - return false; - } - - // Keys are guaranteed in ascending order when indices are in ascending order. - for (int i = 0; i < array.size(); ++i) { - if (array.keyAt(i) != otherArray.keyAt(i) - || !Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) { - return false; - } - } - return true; - } - - /** - * Check whether two {@link Map} equal. - */ - static <T> boolean equals(Map<T, byte[]> map, Map<T, byte[]> otherMap) { - if (map == otherMap) { - return true; - } - if (map == null || otherMap == null) { - return false; - } - if (map.size() != otherMap.size()) { - return false; - } - Set<T> keys = map.keySet(); - if (!keys.equals(otherMap.keySet())) { - return false; - } - for (T key : keys) { - if (!Objects.deepEquals(map.get(key), otherMap.get(key))) { - return false; - } - } - return true; - } - - /** - * Ensure Bluetooth is turned on. - * - * @throws IllegalStateException If {@code adapter} is null or Bluetooth state is not {@link - * BluetoothAdapter#STATE_ON}. - */ - static void checkAdapterStateOn(BluetoothAdapter adapter) { - if (adapter == null || !adapter.isLeEnabled()) { - throw new IllegalStateException("BT Adapter is not turned ON"); - } - } - - /** - * Compares two UUIDs with a UUID mask. - * - * @param data first {@link #UUID} to compare. - * @param uuid second {@link #UUID} to compare. - * @param mask mask {@link #UUID}. - * @return true if both UUIDs are equals when masked, false otherwise. - */ - static boolean maskedEquals(UUID data, UUID uuid, UUID mask) { - if (mask == null) { - return Objects.equals(data, uuid); - } - return (data.getLeastSignificantBits() & mask.getLeastSignificantBits()) - == (uuid.getLeastSignificantBits() & mask.getLeastSignificantBits()) - && (data.getMostSignificantBits() & mask.getMostSignificantBits()) - == (uuid.getMostSignificantBits() & mask.getMostSignificantBits()); - } -} diff --git a/core/java/android/bluetooth/le/OWNERS b/core/java/android/bluetooth/le/OWNERS deleted file mode 100644 index 3523ee0640ab..000000000000 --- a/core/java/android/bluetooth/le/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -# Bug component: 27441 - -zachoverflow@google.com -siyuanh@google.com diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java b/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java deleted file mode 100644 index 14ac911fcb7f..000000000000 --- a/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.bluetooth.BluetoothDevice; - -/** - * Bluetooth LE periodic advertising callbacks, used to deliver periodic - * advertising operation status. - * - * @hide - * @see PeriodicAdvertisingManager#createSync - */ -public abstract class PeriodicAdvertisingCallback { - - /** - * The requested operation was successful. - * - * @hide - */ - public static final int SYNC_SUCCESS = 0; - - /** - * Sync failed to be established because remote device did not respond. - */ - public static final int SYNC_NO_RESPONSE = 1; - - /** - * Sync failed to be established because controller can't support more syncs. - */ - public static final int SYNC_NO_RESOURCES = 2; - - - /** - * Callback when synchronization was established. - * - * @param syncHandle handle used to identify this synchronization. - * @param device remote device. - * @param advertisingSid synchronized advertising set id. - * @param skip The number of periodic advertising packets that can be skipped after a successful - * receive in force. @see PeriodicAdvertisingManager#createSync - * @param timeout Synchronization timeout for the periodic advertising in force. One unit is - * 10ms. @see PeriodicAdvertisingManager#createSync - * @param timeout - * @param status operation status. - */ - public void onSyncEstablished(int syncHandle, BluetoothDevice device, - int advertisingSid, int skip, int timeout, - int status) { - } - - /** - * Callback when periodic advertising report is received. - * - * @param report periodic advertising report. - */ - public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { - } - - /** - * Callback when periodic advertising synchronization was lost. - * - * @param syncHandle handle used to identify this synchronization. - */ - public void onSyncLost(int syncHandle) { - } -} diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java deleted file mode 100644 index bbd31170bb41..000000000000 --- a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package android.bluetooth.le; - -import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; -import android.bluetooth.Attributable; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.IBluetoothGatt; -import android.bluetooth.IBluetoothManager; -import android.bluetooth.annotations.RequiresBluetoothLocationPermission; -import android.bluetooth.annotations.RequiresBluetoothScanPermission; -import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; -import android.content.AttributionSource; -import android.os.Handler; -import android.os.Looper; -import android.os.RemoteException; -import android.util.Log; - -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.Objects; - -/** - * This class provides methods to perform periodic advertising related - * operations. An application can register for periodic advertisements using - * {@link PeriodicAdvertisingManager#registerSync}. - * <p> - * Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an - * instance of {@link PeriodicAdvertisingManager}. - * - * @hide - */ -public final class PeriodicAdvertisingManager { - - private static final String TAG = "PeriodicAdvertisingManager"; - - private static final int SKIP_MIN = 0; - private static final int SKIP_MAX = 499; - private static final int TIMEOUT_MIN = 10; - private static final int TIMEOUT_MAX = 16384; - - private static final int SYNC_STARTING = -1; - - private final BluetoothAdapter mBluetoothAdapter; - private final IBluetoothManager mBluetoothManager; - private final AttributionSource mAttributionSource; - - /* maps callback, to callback wrapper and sync handle */ - Map<PeriodicAdvertisingCallback, - IPeriodicAdvertisingCallback /* callbackWrapper */> mCallbackWrappers; - - /** - * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead. - * - * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management. - * @hide - */ - public PeriodicAdvertisingManager(BluetoothAdapter bluetoothAdapter) { - mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter); - mBluetoothManager = mBluetoothAdapter.getBluetoothManager(); - mAttributionSource = mBluetoothAdapter.getAttributionSource(); - mCallbackWrappers = new IdentityHashMap<>(); - } - - /** - * Synchronize with periodic advertising pointed to by the {@code scanResult}. - * The {@code scanResult} used must contain a valid advertisingSid. First - * call to registerSync will use the {@code skip} and {@code timeout} provided. - * Subsequent calls from other apps, trying to sync with same set will reuse - * existing sync, thus {@code skip} and {@code timeout} values will not take - * effect. The values in effect will be returned in - * {@link PeriodicAdvertisingCallback#onSyncEstablished}. - * - * @param scanResult Scan result containing advertisingSid. - * @param skip The number of periodic advertising packets that can be skipped after a successful - * receive. Must be between 0 and 499. - * @param timeout Synchronization timeout for the periodic advertising. One unit is 10ms. Must - * be between 10 (100ms) and 16384 (163.84s). - * @param callback Callback used to deliver all operations status. - * @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or - * {@code timeout} is invalid or {@code callback} is null. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresBluetoothLocationPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void registerSync(ScanResult scanResult, int skip, int timeout, - PeriodicAdvertisingCallback callback) { - registerSync(scanResult, skip, timeout, callback, null); - } - - /** - * Synchronize with periodic advertising pointed to by the {@code scanResult}. - * The {@code scanResult} used must contain a valid advertisingSid. First - * call to registerSync will use the {@code skip} and {@code timeout} provided. - * Subsequent calls from other apps, trying to sync with same set will reuse - * existing sync, thus {@code skip} and {@code timeout} values will not take - * effect. The values in effect will be returned in - * {@link PeriodicAdvertisingCallback#onSyncEstablished}. - * - * @param scanResult Scan result containing advertisingSid. - * @param skip The number of periodic advertising packets that can be skipped after a successful - * receive. Must be between 0 and 499. - * @param timeout Synchronization timeout for the periodic advertising. One unit is 10ms. Must - * be between 10 (100ms) and 16384 (163.84s). - * @param callback Callback used to deliver all operations status. - * @param handler thread upon which the callbacks will be invoked. - * @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or - * {@code timeout} is invalid or {@code callback} is null. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresBluetoothLocationPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void registerSync(ScanResult scanResult, int skip, int timeout, - PeriodicAdvertisingCallback callback, Handler handler) { - if (callback == null) { - throw new IllegalArgumentException("callback can't be null"); - } - - if (scanResult == null) { - throw new IllegalArgumentException("scanResult can't be null"); - } - - if (scanResult.getAdvertisingSid() == ScanResult.SID_NOT_PRESENT) { - throw new IllegalArgumentException("scanResult must contain a valid sid"); - } - - if (skip < SKIP_MIN || skip > SKIP_MAX) { - throw new IllegalArgumentException( - "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); - } - - if (timeout < TIMEOUT_MIN || timeout > TIMEOUT_MAX) { - throw new IllegalArgumentException( - "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX); - } - - IBluetoothGatt gatt; - try { - gatt = mBluetoothManager.getBluetoothGatt(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get Bluetooth gatt - ", e); - callback.onSyncEstablished(0, scanResult.getDevice(), scanResult.getAdvertisingSid(), - skip, timeout, - PeriodicAdvertisingCallback.SYNC_NO_RESOURCES); - return; - } - - if (handler == null) { - handler = new Handler(Looper.getMainLooper()); - } - - IPeriodicAdvertisingCallback wrapped = wrap(callback, handler); - mCallbackWrappers.put(callback, wrapped); - - try { - gatt.registerSync( - scanResult, skip, timeout, wrapped, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Failed to register sync - ", e); - return; - } - } - - /** - * Cancel pending attempt to create sync, or terminate existing sync. - * - * @param callback Callback used to deliver all operations status. - * @throws IllegalArgumentException if {@code callback} is null, or not a properly registered - * callback. - */ - @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) - public void unregisterSync(PeriodicAdvertisingCallback callback) { - if (callback == null) { - throw new IllegalArgumentException("callback can't be null"); - } - - IBluetoothGatt gatt; - try { - gatt = mBluetoothManager.getBluetoothGatt(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get Bluetooth gatt - ", e); - return; - } - - IPeriodicAdvertisingCallback wrapper = mCallbackWrappers.remove(callback); - if (wrapper == null) { - throw new IllegalArgumentException("callback was not properly registered"); - } - - try { - gatt.unregisterSync(wrapper, mAttributionSource); - } catch (RemoteException e) { - Log.e(TAG, "Failed to cancel sync creation - ", e); - return; - } - } - - @SuppressLint("AndroidFrameworkBluetoothPermission") - private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback, - Handler handler) { - return new IPeriodicAdvertisingCallback.Stub() { - public void onSyncEstablished(int syncHandle, BluetoothDevice device, - int advertisingSid, int skip, int timeout, int status) { - Attributable.setAttributionSource(device, mAttributionSource); - handler.post(new Runnable() { - @Override - public void run() { - callback.onSyncEstablished(syncHandle, device, advertisingSid, skip, - timeout, - status); - - if (status != PeriodicAdvertisingCallback.SYNC_SUCCESS) { - // App can still unregister the sync until notified it failed. Remove - // callback - // after app was notifed. - mCallbackWrappers.remove(callback); - } - } - }); - } - - public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { - handler.post(new Runnable() { - @Override - public void run() { - callback.onPeriodicAdvertisingReport(report); - } - }); - } - - public void onSyncLost(int syncHandle) { - handler.post(new Runnable() { - @Override - public void run() { - callback.onSyncLost(syncHandle); - // App can still unregister the sync until notified it's lost. - // Remove callback after app was notifed. - mCallbackWrappers.remove(callback); - } - }); - } - }; - } -} diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java deleted file mode 100644 index 4e64dbed7017..000000000000 --- a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * The {@link PeriodicAdvertisingParameters} provide a way to adjust periodic - * advertising preferences for each Bluetooth LE advertising set. Use {@link - * PeriodicAdvertisingParameters.Builder} to create an instance of this class. - */ -public final class PeriodicAdvertisingParameters implements Parcelable { - - private static final int INTERVAL_MIN = 80; - private static final int INTERVAL_MAX = 65519; - - private final boolean mIncludeTxPower; - private final int mInterval; - - private PeriodicAdvertisingParameters(boolean includeTxPower, int interval) { - mIncludeTxPower = includeTxPower; - mInterval = interval; - } - - private PeriodicAdvertisingParameters(Parcel in) { - mIncludeTxPower = in.readInt() != 0; - mInterval = in.readInt(); - } - - /** - * Returns whether the TX Power will be included. - */ - public boolean getIncludeTxPower() { - return mIncludeTxPower; - } - - /** - * Returns the periodic advertising interval, in 1.25ms unit. - * Valid values are from 80 (100ms) to 65519 (81.89875s). - */ - public int getInterval() { - return mInterval; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mIncludeTxPower ? 1 : 0); - dest.writeInt(mInterval); - } - - public static final Parcelable - .Creator<PeriodicAdvertisingParameters> CREATOR = - new Creator<PeriodicAdvertisingParameters>() { - @Override - public PeriodicAdvertisingParameters[] newArray(int size) { - return new PeriodicAdvertisingParameters[size]; - } - - @Override - public PeriodicAdvertisingParameters createFromParcel(Parcel in) { - return new PeriodicAdvertisingParameters(in); - } - }; - - public static final class Builder { - private boolean mIncludeTxPower = false; - private int mInterval = INTERVAL_MAX; - - /** - * Whether the transmission power level should be included in the periodic - * packet. - */ - public Builder setIncludeTxPower(boolean includeTxPower) { - mIncludeTxPower = includeTxPower; - return this; - } - - /** - * Set advertising interval for periodic advertising, in 1.25ms unit. - * Valid values are from 80 (100ms) to 65519 (81.89875s). - * Value from range [interval, interval+20ms] will be picked as the actual value. - * - * @throws IllegalArgumentException If the interval is invalid. - */ - public Builder setInterval(int interval) { - if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { - throw new IllegalArgumentException("Invalid interval (must be " + INTERVAL_MIN - + "-" + INTERVAL_MAX + ")"); - } - mInterval = interval; - return this; - } - - /** - * Build the {@link AdvertisingSetParameters} object. - */ - public PeriodicAdvertisingParameters build() { - return new PeriodicAdvertisingParameters(mIncludeTxPower, mInterval); - } - } -} diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java deleted file mode 100644 index 54b953c25c27..000000000000 --- a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Objects; - -/** - * PeriodicAdvertisingReport for Bluetooth LE synchronized advertising. - * - * @hide - */ -public final class PeriodicAdvertisingReport implements Parcelable { - - /** - * The data returned is complete - */ - public static final int DATA_COMPLETE = 0; - - /** - * The data returned is incomplete. The controller was unsuccessfull to - * receive all chained packets, returning only partial data. - */ - public static final int DATA_INCOMPLETE_TRUNCATED = 2; - - private int mSyncHandle; - private int mTxPower; - private int mRssi; - private int mDataStatus; - - // periodic advertising data. - @Nullable - private ScanRecord mData; - - // Device timestamp when the result was last seen. - private long mTimestampNanos; - - /** - * Constructor of periodic advertising result. - */ - public PeriodicAdvertisingReport(int syncHandle, int txPower, int rssi, - int dataStatus, ScanRecord data) { - mSyncHandle = syncHandle; - mTxPower = txPower; - mRssi = rssi; - mDataStatus = dataStatus; - mData = data; - } - - private PeriodicAdvertisingReport(Parcel in) { - readFromParcel(in); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mSyncHandle); - dest.writeInt(mTxPower); - dest.writeInt(mRssi); - dest.writeInt(mDataStatus); - if (mData != null) { - dest.writeInt(1); - dest.writeByteArray(mData.getBytes()); - } else { - dest.writeInt(0); - } - } - - private void readFromParcel(Parcel in) { - mSyncHandle = in.readInt(); - mTxPower = in.readInt(); - mRssi = in.readInt(); - mDataStatus = in.readInt(); - if (in.readInt() == 1) { - mData = ScanRecord.parseFromBytes(in.createByteArray()); - } - } - - @Override - public int describeContents() { - return 0; - } - - /** - * Returns the synchronization handle. - */ - public int getSyncHandle() { - return mSyncHandle; - } - - /** - * Returns the transmit power in dBm. The valid range is [-127, 126]. Value - * of 127 means information was not available. - */ - public int getTxPower() { - return mTxPower; - } - - /** - * Returns the received signal strength in dBm. The valid range is [-127, 20]. - */ - public int getRssi() { - return mRssi; - } - - /** - * Returns the data status. Can be one of {@link PeriodicAdvertisingReport#DATA_COMPLETE} - * or {@link PeriodicAdvertisingReport#DATA_INCOMPLETE_TRUNCATED}. - */ - public int getDataStatus() { - return mDataStatus; - } - - /** - * Returns the data contained in this periodic advertising report. - */ - @Nullable - public ScanRecord getData() { - return mData; - } - - /** - * Returns timestamp since boot when the scan record was observed. - */ - public long getTimestampNanos() { - return mTimestampNanos; - } - - @Override - public int hashCode() { - return Objects.hash(mSyncHandle, mTxPower, mRssi, mDataStatus, mData, mTimestampNanos); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - PeriodicAdvertisingReport other = (PeriodicAdvertisingReport) obj; - return (mSyncHandle == other.mSyncHandle) - && (mTxPower == other.mTxPower) - && (mRssi == other.mRssi) - && (mDataStatus == other.mDataStatus) - && Objects.equals(mData, other.mData) - && (mTimestampNanos == other.mTimestampNanos); - } - - @Override - public String toString() { - return "PeriodicAdvertisingReport{syncHandle=" + mSyncHandle - + ", txPower=" + mTxPower + ", rssi=" + mRssi + ", dataStatus=" + mDataStatus - + ", data=" + Objects.toString(mData) + ", timestampNanos=" + mTimestampNanos + '}'; - } - - public static final @android.annotation.NonNull Parcelable.Creator<PeriodicAdvertisingReport> CREATOR = - new Creator<PeriodicAdvertisingReport>() { - @Override - public PeriodicAdvertisingReport createFromParcel(Parcel source) { - return new PeriodicAdvertisingReport(source); - } - - @Override - public PeriodicAdvertisingReport[] newArray(int size) { - return new PeriodicAdvertisingReport[size]; - } - }; -} diff --git a/core/java/android/bluetooth/le/ResultStorageDescriptor.java b/core/java/android/bluetooth/le/ResultStorageDescriptor.java deleted file mode 100644 index f65048975deb..000000000000 --- a/core/java/android/bluetooth/le/ResultStorageDescriptor.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Describes the way to store scan result. - * - * @deprecated this is not used anywhere - * - * @hide - */ -@Deprecated -@SystemApi -public final class ResultStorageDescriptor implements Parcelable { - private int mType; - private int mOffset; - private int mLength; - - public int getType() { - return mType; - } - - public int getOffset() { - return mOffset; - } - - public int getLength() { - return mLength; - } - - /** - * Constructor of {@link ResultStorageDescriptor} - * - * @param type Type of the data. - * @param offset Offset from start of the advertise packet payload. - * @param length Byte length of the data - */ - public ResultStorageDescriptor(int type, int offset, int length) { - mType = type; - mOffset = offset; - mLength = length; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mType); - dest.writeInt(mOffset); - dest.writeInt(mLength); - } - - private ResultStorageDescriptor(Parcel in) { - ReadFromParcel(in); - } - - private void ReadFromParcel(Parcel in) { - mType = in.readInt(); - mOffset = in.readInt(); - mLength = in.readInt(); - } - - public static final @android.annotation.NonNull Parcelable.Creator<ResultStorageDescriptor> CREATOR = - new Creator<ResultStorageDescriptor>() { - @Override - public ResultStorageDescriptor createFromParcel(Parcel source) { - return new ResultStorageDescriptor(source); - } - - @Override - public ResultStorageDescriptor[] newArray(int size) { - return new ResultStorageDescriptor[size]; - } - }; -} diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java deleted file mode 100644 index 53d9310a1236..000000000000 --- a/core/java/android/bluetooth/le/ScanCallback.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import java.util.List; - -/** - * Bluetooth LE scan callbacks. Scan results are reported using these callbacks. - * - * @see BluetoothLeScanner#startScan - */ -public abstract class ScanCallback { - /** - * Fails to start scan as BLE scan with the same settings is already started by the app. - */ - public static final int SCAN_FAILED_ALREADY_STARTED = 1; - - /** - * Fails to start scan as app cannot be registered. - */ - public static final int SCAN_FAILED_APPLICATION_REGISTRATION_FAILED = 2; - - /** - * Fails to start scan due an internal error - */ - public static final int SCAN_FAILED_INTERNAL_ERROR = 3; - - /** - * Fails to start power optimized scan as this feature is not supported. - */ - public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = 4; - - /** - * Fails to start scan as it is out of hardware resources. - * - * @hide - */ - public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5; - - /** - * Fails to start scan as application tries to scan too frequently. - * @hide - */ - public static final int SCAN_FAILED_SCANNING_TOO_FREQUENTLY = 6; - - static final int NO_ERROR = 0; - - /** - * Callback when a BLE advertisement has been found. - * - * @param callbackType Determines how this callback was triggered. Could be one of {@link - * ScanSettings#CALLBACK_TYPE_ALL_MATCHES}, {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or - * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST} - * @param result A Bluetooth LE scan result. - */ - public void onScanResult(int callbackType, ScanResult result) { - } - - /** - * Callback when batch results are delivered. - * - * @param results List of scan results that are previously scanned. - */ - public void onBatchScanResults(List<ScanResult> results) { - } - - /** - * Callback when scan could not be started. - * - * @param errorCode Error code (one of SCAN_FAILED_*) for scan failure. - */ - public void onScanFailed(int errorCode) { - } -} diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java deleted file mode 100644 index b059193ae03f..000000000000 --- a/core/java/android/bluetooth/le/ScanFilter.java +++ /dev/null @@ -1,910 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import static java.util.Objects.requireNonNull; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothDevice.AddressType; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.os.Parcelable; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.UUID; - -/** - * Criteria for filtering result from Bluetooth LE scans. A {@link ScanFilter} allows clients to - * restrict scan results to only those that are of interest to them. - * <p> - * Current filtering on the following fields are supported: - * <li>Service UUIDs which identify the bluetooth gatt services running on the device. - * <li>Name of remote Bluetooth LE device. - * <li>Mac address of the remote device. - * <li>Service data which is the data associated with a service. - * <li>Manufacturer specific data which is the data associated with a particular manufacturer. - * - * @see ScanResult - * @see BluetoothLeScanner - */ -public final class ScanFilter implements Parcelable { - - @Nullable - private final String mDeviceName; - - @Nullable - private final String mDeviceAddress; - - private final @AddressType int mAddressType; - - @Nullable - private final byte[] mIrk; - - @Nullable - private final ParcelUuid mServiceUuid; - @Nullable - private final ParcelUuid mServiceUuidMask; - - @Nullable - private final ParcelUuid mServiceSolicitationUuid; - @Nullable - private final ParcelUuid mServiceSolicitationUuidMask; - - @Nullable - private final ParcelUuid mServiceDataUuid; - @Nullable - private final byte[] mServiceData; - @Nullable - private final byte[] mServiceDataMask; - - private final int mManufacturerId; - @Nullable - private final byte[] mManufacturerData; - @Nullable - private final byte[] mManufacturerDataMask; - - /** @hide */ - public static final ScanFilter EMPTY = new ScanFilter.Builder().build(); - - private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, - ParcelUuid uuidMask, ParcelUuid solicitationUuid, - ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid, - byte[] serviceData, byte[] serviceDataMask, - int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask, - @AddressType int addressType, @Nullable byte[] irk) { - mDeviceName = name; - mServiceUuid = uuid; - mServiceUuidMask = uuidMask; - mServiceSolicitationUuid = solicitationUuid; - mServiceSolicitationUuidMask = solicitationUuidMask; - mDeviceAddress = deviceAddress; - mServiceDataUuid = serviceDataUuid; - mServiceData = serviceData; - mServiceDataMask = serviceDataMask; - mManufacturerId = manufacturerId; - mManufacturerData = manufacturerData; - mManufacturerDataMask = manufacturerDataMask; - mAddressType = addressType; - mIrk = irk; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mDeviceName == null ? 0 : 1); - if (mDeviceName != null) { - dest.writeString(mDeviceName); - } - dest.writeInt(mDeviceAddress == null ? 0 : 1); - if (mDeviceAddress != null) { - dest.writeString(mDeviceAddress); - } - dest.writeInt(mServiceUuid == null ? 0 : 1); - if (mServiceUuid != null) { - dest.writeParcelable(mServiceUuid, flags); - dest.writeInt(mServiceUuidMask == null ? 0 : 1); - if (mServiceUuidMask != null) { - dest.writeParcelable(mServiceUuidMask, flags); - } - } - dest.writeInt(mServiceSolicitationUuid == null ? 0 : 1); - if (mServiceSolicitationUuid != null) { - dest.writeParcelable(mServiceSolicitationUuid, flags); - dest.writeInt(mServiceSolicitationUuidMask == null ? 0 : 1); - if (mServiceSolicitationUuidMask != null) { - dest.writeParcelable(mServiceSolicitationUuidMask, flags); - } - } - dest.writeInt(mServiceDataUuid == null ? 0 : 1); - if (mServiceDataUuid != null) { - dest.writeParcelable(mServiceDataUuid, flags); - dest.writeInt(mServiceData == null ? 0 : 1); - if (mServiceData != null) { - dest.writeInt(mServiceData.length); - dest.writeByteArray(mServiceData); - - dest.writeInt(mServiceDataMask == null ? 0 : 1); - if (mServiceDataMask != null) { - dest.writeInt(mServiceDataMask.length); - dest.writeByteArray(mServiceDataMask); - } - } - } - dest.writeInt(mManufacturerId); - dest.writeInt(mManufacturerData == null ? 0 : 1); - if (mManufacturerData != null) { - dest.writeInt(mManufacturerData.length); - dest.writeByteArray(mManufacturerData); - - dest.writeInt(mManufacturerDataMask == null ? 0 : 1); - if (mManufacturerDataMask != null) { - dest.writeInt(mManufacturerDataMask.length); - dest.writeByteArray(mManufacturerDataMask); - } - } - - // IRK - if (mDeviceAddress != null) { - dest.writeInt(mAddressType); - dest.writeInt(mIrk == null ? 0 : 1); - if (mIrk != null) { - dest.writeByteArray(mIrk); - } - } - } - - /** - * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel. - */ - public static final @android.annotation.NonNull Creator<ScanFilter> CREATOR = - new Creator<ScanFilter>() { - - @Override - public ScanFilter[] newArray(int size) { - return new ScanFilter[size]; - } - - @Override - public ScanFilter createFromParcel(Parcel in) { - Builder builder = new Builder(); - if (in.readInt() == 1) { - builder.setDeviceName(in.readString()); - } - String address = null; - // If we have a non-null address - if (in.readInt() == 1) { - address = in.readString(); - } - if (in.readInt() == 1) { - ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); - builder.setServiceUuid(uuid); - if (in.readInt() == 1) { - ParcelUuid uuidMask = in.readParcelable( - ParcelUuid.class.getClassLoader()); - builder.setServiceUuid(uuid, uuidMask); - } - } - if (in.readInt() == 1) { - ParcelUuid solicitationUuid = in.readParcelable( - ParcelUuid.class.getClassLoader()); - builder.setServiceSolicitationUuid(solicitationUuid); - if (in.readInt() == 1) { - ParcelUuid solicitationUuidMask = in.readParcelable( - ParcelUuid.class.getClassLoader()); - builder.setServiceSolicitationUuid(solicitationUuid, - solicitationUuidMask); - } - } - if (in.readInt() == 1) { - ParcelUuid servcieDataUuid = - in.readParcelable(ParcelUuid.class.getClassLoader()); - if (in.readInt() == 1) { - int serviceDataLength = in.readInt(); - byte[] serviceData = new byte[serviceDataLength]; - in.readByteArray(serviceData); - if (in.readInt() == 0) { - builder.setServiceData(servcieDataUuid, serviceData); - } else { - int serviceDataMaskLength = in.readInt(); - byte[] serviceDataMask = new byte[serviceDataMaskLength]; - in.readByteArray(serviceDataMask); - builder.setServiceData( - servcieDataUuid, serviceData, serviceDataMask); - } - } - } - - int manufacturerId = in.readInt(); - if (in.readInt() == 1) { - int manufacturerDataLength = in.readInt(); - byte[] manufacturerData = new byte[manufacturerDataLength]; - in.readByteArray(manufacturerData); - if (in.readInt() == 0) { - builder.setManufacturerData(manufacturerId, manufacturerData); - } else { - int manufacturerDataMaskLength = in.readInt(); - byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength]; - in.readByteArray(manufacturerDataMask); - builder.setManufacturerData(manufacturerId, manufacturerData, - manufacturerDataMask); - } - } - - // IRK - if (address != null) { - final int addressType = in.readInt(); - if (in.readInt() == 1) { - final byte[] irk = new byte[16]; - in.readByteArray(irk); - builder.setDeviceAddress(address, addressType, irk); - } else { - builder.setDeviceAddress(address, addressType); - } - } - return builder.build(); - } - }; - - /** - * Returns the filter set the device name field of Bluetooth advertisement data. - */ - @Nullable - public String getDeviceName() { - return mDeviceName; - } - - /** - * Returns the filter set on the service uuid. - */ - @Nullable - public ParcelUuid getServiceUuid() { - return mServiceUuid; - } - - @Nullable - public ParcelUuid getServiceUuidMask() { - return mServiceUuidMask; - } - - /** - * Returns the filter set on the service Solicitation uuid. - */ - @Nullable - public ParcelUuid getServiceSolicitationUuid() { - return mServiceSolicitationUuid; - } - - /** - * Returns the filter set on the service Solicitation uuid mask. - */ - @Nullable - public ParcelUuid getServiceSolicitationUuidMask() { - return mServiceSolicitationUuidMask; - } - - @Nullable - public String getDeviceAddress() { - return mDeviceAddress; - } - - /** - * @hide - */ - @SystemApi - public @AddressType int getAddressType() { - return mAddressType; - } - - /** - * @hide - */ - @SystemApi - @Nullable - public byte[] getIrk() { - return mIrk; - } - - @Nullable - public byte[] getServiceData() { - return mServiceData; - } - - @Nullable - public byte[] getServiceDataMask() { - return mServiceDataMask; - } - - @Nullable - public ParcelUuid getServiceDataUuid() { - return mServiceDataUuid; - } - - /** - * Returns the manufacturer id. -1 if the manufacturer filter is not set. - */ - public int getManufacturerId() { - return mManufacturerId; - } - - @Nullable - public byte[] getManufacturerData() { - return mManufacturerData; - } - - @Nullable - public byte[] getManufacturerDataMask() { - return mManufacturerDataMask; - } - - /** - * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match - * if it matches all the field filters. - */ - public boolean matches(ScanResult scanResult) { - if (scanResult == null) { - return false; - } - BluetoothDevice device = scanResult.getDevice(); - // Device match. - if (mDeviceAddress != null - && (device == null || !mDeviceAddress.equals(device.getAddress()))) { - return false; - } - - ScanRecord scanRecord = scanResult.getScanRecord(); - - // Scan record is null but there exist filters on it. - if (scanRecord == null - && (mDeviceName != null || mServiceUuid != null || mManufacturerData != null - || mServiceData != null || mServiceSolicitationUuid != null)) { - return false; - } - - // Local name match. - if (mDeviceName != null && !mDeviceName.equals(scanRecord.getDeviceName())) { - return false; - } - - // UUID match. - if (mServiceUuid != null && !matchesServiceUuids(mServiceUuid, mServiceUuidMask, - scanRecord.getServiceUuids())) { - return false; - } - - // solicitation UUID match. - if (mServiceSolicitationUuid != null && !matchesServiceSolicitationUuids( - mServiceSolicitationUuid, mServiceSolicitationUuidMask, - scanRecord.getServiceSolicitationUuids())) { - return false; - } - - // Service data match - if (mServiceDataUuid != null) { - if (!matchesPartialData(mServiceData, mServiceDataMask, - scanRecord.getServiceData(mServiceDataUuid))) { - return false; - } - } - - // Manufacturer data match. - if (mManufacturerId >= 0) { - if (!matchesPartialData(mManufacturerData, mManufacturerDataMask, - scanRecord.getManufacturerSpecificData(mManufacturerId))) { - return false; - } - } - // All filters match. - return true; - } - - /** - * Check if the uuid pattern is contained in a list of parcel uuids. - * - * @hide - */ - public static boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask, - List<ParcelUuid> uuids) { - if (uuid == null) { - return true; - } - if (uuids == null) { - return false; - } - - for (ParcelUuid parcelUuid : uuids) { - UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid(); - if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) { - return true; - } - } - return false; - } - - // Check if the uuid pattern matches the particular service uuid. - private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { - return BluetoothLeUtils.maskedEquals(data, uuid, mask); - } - - /** - * Check if the solicitation uuid pattern is contained in a list of parcel uuids. - * - */ - private static boolean matchesServiceSolicitationUuids(ParcelUuid solicitationUuid, - ParcelUuid parcelSolicitationUuidMask, List<ParcelUuid> solicitationUuids) { - if (solicitationUuid == null) { - return true; - } - if (solicitationUuids == null) { - return false; - } - - for (ParcelUuid parcelSolicitationUuid : solicitationUuids) { - UUID solicitationUuidMask = parcelSolicitationUuidMask == null - ? null : parcelSolicitationUuidMask.getUuid(); - if (matchesServiceUuid(solicitationUuid.getUuid(), solicitationUuidMask, - parcelSolicitationUuid.getUuid())) { - return true; - } - } - return false; - } - - // Check if the solicitation uuid pattern matches the particular service solicitation uuid. - private static boolean matchesServiceSolicitationUuid(UUID solicitationUuid, - UUID solicitationUuidMask, UUID data) { - return BluetoothLeUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask); - } - - // Check whether the data pattern matches the parsed data. - private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) { - if (parsedData == null || parsedData.length < data.length) { - return false; - } - if (dataMask == null) { - for (int i = 0; i < data.length; ++i) { - if (parsedData[i] != data[i]) { - return false; - } - } - return true; - } - for (int i = 0; i < data.length; ++i) { - if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) { - return false; - } - } - return true; - } - - @Override - public String toString() { - return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress=" - + mDeviceAddress - + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask - + ", mServiceSolicitationUuid=" + mServiceSolicitationUuid - + ", mServiceSolicitationUuidMask=" + mServiceSolicitationUuidMask - + ", mServiceDataUuid=" + Objects.toString(mServiceDataUuid) + ", mServiceData=" - + Arrays.toString(mServiceData) + ", mServiceDataMask=" - + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId - + ", mManufacturerData=" + Arrays.toString(mManufacturerData) - + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + "]"; - } - - @Override - public int hashCode() { - return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId, - Arrays.hashCode(mManufacturerData), - Arrays.hashCode(mManufacturerDataMask), - mServiceDataUuid, - Arrays.hashCode(mServiceData), - Arrays.hashCode(mServiceDataMask), - mServiceUuid, mServiceUuidMask, - mServiceSolicitationUuid, mServiceSolicitationUuidMask); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - ScanFilter other = (ScanFilter) obj; - return Objects.equals(mDeviceName, other.mDeviceName) - && Objects.equals(mDeviceAddress, other.mDeviceAddress) - && mManufacturerId == other.mManufacturerId - && Objects.deepEquals(mManufacturerData, other.mManufacturerData) - && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) - && Objects.equals(mServiceDataUuid, other.mServiceDataUuid) - && Objects.deepEquals(mServiceData, other.mServiceData) - && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) - && Objects.equals(mServiceUuid, other.mServiceUuid) - && Objects.equals(mServiceUuidMask, other.mServiceUuidMask) - && Objects.equals(mServiceSolicitationUuid, other.mServiceSolicitationUuid) - && Objects.equals(mServiceSolicitationUuidMask, - other.mServiceSolicitationUuidMask); - } - - /** - * Checks if the scanfilter is empty - * - * @hide - */ - public boolean isAllFieldsEmpty() { - return EMPTY.equals(this); - } - - /** - * Builder class for {@link ScanFilter}. - */ - public static final class Builder { - - /** - * @hide - */ - @SystemApi - public static final int LEN_IRK_OCTETS = 16; - - private String mDeviceName; - private String mDeviceAddress; - private @AddressType int mAddressType = BluetoothDevice.ADDRESS_TYPE_PUBLIC; - private byte[] mIrk; - - private ParcelUuid mServiceUuid; - private ParcelUuid mUuidMask; - - private ParcelUuid mServiceSolicitationUuid; - private ParcelUuid mServiceSolicitationUuidMask; - - private ParcelUuid mServiceDataUuid; - private byte[] mServiceData; - private byte[] mServiceDataMask; - - private int mManufacturerId = -1; - private byte[] mManufacturerData; - private byte[] mManufacturerDataMask; - - /** - * Set filter on device name. - */ - public Builder setDeviceName(String deviceName) { - mDeviceName = deviceName; - return this; - } - - /** - * Set filter on device address. - * - * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the - * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link - * BluetoothAdapter#checkBluetoothAddress}. The @AddressType is defaulted to {@link - * BluetoothDevice#ADDRESS_TYPE_PUBLIC} - * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. - */ - public Builder setDeviceAddress(String deviceAddress) { - if (deviceAddress == null) { - mDeviceAddress = deviceAddress; - return this; - } - return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC); - } - - /** - * Set filter on Address with AddressType - * - * <p>This key is used to resolve a private address from a public address. - * - * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the - * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link - * BluetoothAdapter#checkBluetoothAddress}. May be any type of address. - * @param addressType indication of the type of address - * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} - * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM} - * - * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. - * @throws IllegalArgumentException If the {@code addressType} is invalid length - * @throws NullPointerException if {@code deviceAddress} is null. - * - * @hide - */ - @NonNull - @SystemApi - public Builder setDeviceAddress(@NonNull String deviceAddress, - @AddressType int addressType) { - return setDeviceAddressInternal(deviceAddress, addressType, null); - } - - /** - * Set filter on Address with AddressType and the Identity Resolving Key (IRK). - * - * <p>The IRK is used to resolve a {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} from - * a PRIVATE_ADDRESS type. - * - * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the - * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link - * BluetoothAdapter#checkBluetoothAddress}. This Address type must only be PUBLIC OR RANDOM - * STATIC. - * @param addressType indication of the type of address - * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} - * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM} - * @param irk non-null byte array representing the Identity Resolving Key - * - * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. - * @throws IllegalArgumentException if the {@code irk} is invalid length. - * @throws IllegalArgumentException If the {@code addressType} is invalid length or is not - * PUBLIC or RANDOM STATIC when an IRK is present. - * @throws NullPointerException if {@code deviceAddress} or {@code irk} is null. - * - * @hide - */ - @NonNull - @SystemApi - public Builder setDeviceAddress(@NonNull String deviceAddress, - @AddressType int addressType, - @NonNull byte[] irk) { - requireNonNull(irk); - if (irk.length != LEN_IRK_OCTETS) { - throw new IllegalArgumentException("'irk' is invalid length!"); - } - return setDeviceAddressInternal(deviceAddress, addressType, irk); - } - - /** - * Set filter on Address with AddressType and the Identity Resolving Key (IRK). - * - * <p>Internal setter for the device address - * - * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the - * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link - * BluetoothAdapter#checkBluetoothAddress}. - * @param addressType indication of the type of address - * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} - * @param irk non-null byte array representing the Identity Resolving Address; nullable - * internally. - * - * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. - * @throws IllegalArgumentException If the {@code addressType} is invalid length. - * @throws NullPointerException if {@code deviceAddress} is null. - * - * @hide - */ - @NonNull - private Builder setDeviceAddressInternal(@NonNull String deviceAddress, - @AddressType int addressType, - @Nullable byte[] irk) { - - // Make sure our deviceAddress is valid! - requireNonNull(deviceAddress); - if (!BluetoothAdapter.checkBluetoothAddress(deviceAddress)) { - throw new IllegalArgumentException("invalid device address " + deviceAddress); - } - - // Verify type range - if (addressType < BluetoothDevice.ADDRESS_TYPE_PUBLIC - || addressType > BluetoothDevice.ADDRESS_TYPE_RANDOM) { - throw new IllegalArgumentException("'addressType' is invalid!"); - } - - // IRK can only be used for a PUBLIC or RANDOM (STATIC) Address. - if (addressType == BluetoothDevice.ADDRESS_TYPE_RANDOM) { - // Don't want a bad combination of address and irk! - if (irk != null) { - // Since there are 3 possible RANDOM subtypes we must check to make sure - // the correct type of address is used. - if (!BluetoothAdapter.isAddressRandomStatic(deviceAddress)) { - throw new IllegalArgumentException( - "Invalid combination: IRK requires either a PUBLIC or " - + "RANDOM (STATIC) Address"); - } - } - } - - // PUBLIC doesn't require extra work - // Without an IRK any address may be accepted - - mDeviceAddress = deviceAddress; - mAddressType = addressType; - mIrk = irk; - return this; - } - - /** - * Set filter on service uuid. - */ - public Builder setServiceUuid(ParcelUuid serviceUuid) { - mServiceUuid = serviceUuid; - mUuidMask = null; // clear uuid mask - return this; - } - - /** - * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the - * {@code serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the - * bit in {@code serviceUuid}, and 0 to ignore that bit. - * - * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but {@code - * uuidMask} is not {@code null}. - */ - public Builder setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask) { - if (mUuidMask != null && mServiceUuid == null) { - throw new IllegalArgumentException("uuid is null while uuidMask is not null!"); - } - mServiceUuid = serviceUuid; - mUuidMask = uuidMask; - return this; - } - - - /** - * Set filter on service solicitation uuid. - */ - public @NonNull Builder setServiceSolicitationUuid( - @Nullable ParcelUuid serviceSolicitationUuid) { - mServiceSolicitationUuid = serviceSolicitationUuid; - if (serviceSolicitationUuid == null) { - mServiceSolicitationUuidMask = null; - } - return this; - } - - - /** - * Set filter on partial service Solicitation uuid. The {@code SolicitationUuidMask} is the - * bit mask for the {@code serviceSolicitationUuid}. Set any bit in the mask to 1 to - * indicate a match is needed for the bit in {@code serviceSolicitationUuid}, and 0 to - * ignore that bit. - * - * @param serviceSolicitationUuid can only be null if solicitationUuidMask is null. - * @param solicitationUuidMask can be null or a mask with no restriction. - * - * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but - * {@code serviceSolicitationUuidMask} is not {@code null}. - */ - public @NonNull Builder setServiceSolicitationUuid( - @Nullable ParcelUuid serviceSolicitationUuid, - @Nullable ParcelUuid solicitationUuidMask) { - if (solicitationUuidMask != null && serviceSolicitationUuid == null) { - throw new IllegalArgumentException( - "SolicitationUuid is null while SolicitationUuidMask is not null!"); - } - mServiceSolicitationUuid = serviceSolicitationUuid; - mServiceSolicitationUuidMask = solicitationUuidMask; - return this; - } - - /** - * Set filtering on service data. - * - * @throws IllegalArgumentException If {@code serviceDataUuid} is null. - */ - public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { - if (serviceDataUuid == null) { - throw new IllegalArgumentException("serviceDataUuid is null"); - } - mServiceDataUuid = serviceDataUuid; - mServiceData = serviceData; - mServiceDataMask = null; // clear service data mask - return this; - } - - /** - * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to - * match the one in service data, otherwise set it to 0 to ignore that bit. - * <p> - * The {@code serviceDataMask} must have the same length of the {@code serviceData}. - * - * @throws IllegalArgumentException If {@code serviceDataUuid} is null or {@code - * serviceDataMask} is {@code null} while {@code serviceData} is not or {@code - * serviceDataMask} and {@code serviceData} has different length. - */ - public Builder setServiceData(ParcelUuid serviceDataUuid, - byte[] serviceData, byte[] serviceDataMask) { - if (serviceDataUuid == null) { - throw new IllegalArgumentException("serviceDataUuid is null"); - } - if (mServiceDataMask != null) { - if (mServiceData == null) { - throw new IllegalArgumentException( - "serviceData is null while serviceDataMask is not null"); - } - // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two - // byte array need to be the same. - if (mServiceData.length != mServiceDataMask.length) { - throw new IllegalArgumentException( - "size mismatch for service data and service data mask"); - } - } - mServiceDataUuid = serviceDataUuid; - mServiceData = serviceData; - mServiceDataMask = serviceDataMask; - return this; - } - - /** - * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id. - * - * @throws IllegalArgumentException If the {@code manufacturerId} is invalid. - */ - public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData) { - if (manufacturerData != null && manufacturerId < 0) { - throw new IllegalArgumentException("invalid manufacture id"); - } - mManufacturerId = manufacturerId; - mManufacturerData = manufacturerData; - mManufacturerDataMask = null; // clear manufacturer data mask - return this; - } - - /** - * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it needs - * to match the one in manufacturer data, otherwise set it to 0. - * <p> - * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}. - * - * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or {@code - * manufacturerData} is null while {@code manufacturerDataMask} is not, or {@code - * manufacturerData} and {@code manufacturerDataMask} have different length. - */ - public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData, - byte[] manufacturerDataMask) { - if (manufacturerData != null && manufacturerId < 0) { - throw new IllegalArgumentException("invalid manufacture id"); - } - if (mManufacturerDataMask != null) { - if (mManufacturerData == null) { - throw new IllegalArgumentException( - "manufacturerData is null while manufacturerDataMask is not null"); - } - // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths - // of the two byte array need to be the same. - if (mManufacturerData.length != mManufacturerDataMask.length) { - throw new IllegalArgumentException( - "size mismatch for manufacturerData and manufacturerDataMask"); - } - } - mManufacturerId = manufacturerId; - mManufacturerData = manufacturerData; - mManufacturerDataMask = manufacturerDataMask; - return this; - } - - /** - * Build {@link ScanFilter}. - * - * @throws IllegalArgumentException If the filter cannot be built. - */ - public ScanFilter build() { - return new ScanFilter(mDeviceName, mDeviceAddress, - mServiceUuid, mUuidMask, mServiceSolicitationUuid, - mServiceSolicitationUuidMask, - mServiceDataUuid, mServiceData, mServiceDataMask, - mManufacturerId, mManufacturerData, mManufacturerDataMask, - mAddressType, mIrk); - } - } -} diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java deleted file mode 100644 index 9b8c2eaf4d19..000000000000 --- a/core/java/android/bluetooth/le/ScanRecord.java +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SuppressLint; -import android.bluetooth.BluetoothUuid; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.ParcelUuid; -import android.util.ArrayMap; -import android.util.Log; -import android.util.SparseArray; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.function.Predicate; - -/** - * Represents a scan record from Bluetooth LE scan. - */ -@SuppressLint("AndroidFrameworkBluetoothPermission") -public final class ScanRecord { - - private static final String TAG = "ScanRecord"; - - // The following data type values are assigned by Bluetooth SIG. - // For more details refer to Bluetooth 4.1 specification, Volume 3, Part C, Section 18. - private static final int DATA_TYPE_FLAGS = 0x01; - private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02; - private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03; - private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04; - private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05; - private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06; - private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07; - private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08; - private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09; - private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A; - private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16; - private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20; - private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21; - private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT = 0x14; - private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT = 0x1F; - private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT = 0x15; - private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; - - // Flags of the advertising data. - private final int mAdvertiseFlags; - - @Nullable - private final List<ParcelUuid> mServiceUuids; - @Nullable - private final List<ParcelUuid> mServiceSolicitationUuids; - - private final SparseArray<byte[]> mManufacturerSpecificData; - - private final Map<ParcelUuid, byte[]> mServiceData; - - // Transmission power level(in dB). - private final int mTxPowerLevel; - - // Local name of the Bluetooth LE device. - private final String mDeviceName; - - // Raw bytes of scan record. - private final byte[] mBytes; - - /** - * Returns the advertising flags indicating the discoverable mode and capability of the device. - * Returns -1 if the flag field is not set. - */ - public int getAdvertiseFlags() { - return mAdvertiseFlags; - } - - /** - * Returns a list of service UUIDs within the advertisement that are used to identify the - * bluetooth GATT services. - */ - public List<ParcelUuid> getServiceUuids() { - return mServiceUuids; - } - - /** - * Returns a list of service solicitation UUIDs within the advertisement that are used to - * identify the Bluetooth GATT services. - */ - @NonNull - public List<ParcelUuid> getServiceSolicitationUuids() { - return mServiceSolicitationUuids; - } - - /** - * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific - * data. - */ - public SparseArray<byte[]> getManufacturerSpecificData() { - return mManufacturerSpecificData; - } - - /** - * Returns the manufacturer specific data associated with the manufacturer id. Returns - * {@code null} if the {@code manufacturerId} is not found. - */ - @Nullable - public byte[] getManufacturerSpecificData(int manufacturerId) { - if (mManufacturerSpecificData == null) { - return null; - } - return mManufacturerSpecificData.get(manufacturerId); - } - - /** - * Returns a map of service UUID and its corresponding service data. - */ - public Map<ParcelUuid, byte[]> getServiceData() { - return mServiceData; - } - - /** - * Returns the service data byte array associated with the {@code serviceUuid}. Returns - * {@code null} if the {@code serviceDataUuid} is not found. - */ - @Nullable - public byte[] getServiceData(ParcelUuid serviceDataUuid) { - if (serviceDataUuid == null || mServiceData == null) { - return null; - } - return mServiceData.get(serviceDataUuid); - } - - /** - * Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE} - * if the field is not set. This value can be used to calculate the path loss of a received - * packet using the following equation: - * <p> - * <code>pathloss = txPowerLevel - rssi</code> - */ - public int getTxPowerLevel() { - return mTxPowerLevel; - } - - /** - * Returns the local name of the BLE device. This is a UTF-8 encoded string. - */ - @Nullable - public String getDeviceName() { - return mDeviceName; - } - - /** - * Returns raw bytes of scan record. - */ - public byte[] getBytes() { - return mBytes; - } - - /** - * Test if any fields contained inside this scan record are matched by the - * given matcher. - * - * @hide - */ - public boolean matchesAnyField(@NonNull Predicate<byte[]> matcher) { - int pos = 0; - while (pos < mBytes.length) { - final int length = mBytes[pos] & 0xFF; - if (length == 0) { - break; - } - if (matcher.test(Arrays.copyOfRange(mBytes, pos, pos + length + 1))) { - return true; - } - pos += length + 1; - } - return false; - } - - private ScanRecord(List<ParcelUuid> serviceUuids, - List<ParcelUuid> serviceSolicitationUuids, - SparseArray<byte[]> manufacturerData, - Map<ParcelUuid, byte[]> serviceData, - int advertiseFlags, int txPowerLevel, - String localName, byte[] bytes) { - mServiceSolicitationUuids = serviceSolicitationUuids; - mServiceUuids = serviceUuids; - mManufacturerSpecificData = manufacturerData; - mServiceData = serviceData; - mDeviceName = localName; - mAdvertiseFlags = advertiseFlags; - mTxPowerLevel = txPowerLevel; - mBytes = bytes; - } - - /** - * Parse scan record bytes to {@link ScanRecord}. - * <p> - * The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18. - * <p> - * All numerical multi-byte entities and values shall use little-endian <strong>byte</strong> - * order. - * - * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response. - * @hide - */ - @UnsupportedAppUsage - public static ScanRecord parseFromBytes(byte[] scanRecord) { - if (scanRecord == null) { - return null; - } - - int currentPos = 0; - int advertiseFlag = -1; - List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>(); - List<ParcelUuid> serviceSolicitationUuids = new ArrayList<ParcelUuid>(); - String localName = null; - int txPowerLevel = Integer.MIN_VALUE; - - SparseArray<byte[]> manufacturerData = new SparseArray<byte[]>(); - Map<ParcelUuid, byte[]> serviceData = new ArrayMap<ParcelUuid, byte[]>(); - - try { - while (currentPos < scanRecord.length) { - // length is unsigned int. - int length = scanRecord[currentPos++] & 0xFF; - if (length == 0) { - break; - } - // Note the length includes the length of the field type itself. - int dataLength = length - 1; - // fieldType is unsigned int. - int fieldType = scanRecord[currentPos++] & 0xFF; - switch (fieldType) { - case DATA_TYPE_FLAGS: - advertiseFlag = scanRecord[currentPos] & 0xFF; - break; - case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, - dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids); - break; - case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, dataLength, - BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids); - break; - case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL: - case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE: - parseServiceUuid(scanRecord, currentPos, dataLength, - BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids); - break; - case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT: - parseServiceSolicitationUuid(scanRecord, currentPos, dataLength, - BluetoothUuid.UUID_BYTES_16_BIT, serviceSolicitationUuids); - break; - case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT: - parseServiceSolicitationUuid(scanRecord, currentPos, dataLength, - BluetoothUuid.UUID_BYTES_32_BIT, serviceSolicitationUuids); - break; - case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT: - parseServiceSolicitationUuid(scanRecord, currentPos, dataLength, - BluetoothUuid.UUID_BYTES_128_BIT, serviceSolicitationUuids); - break; - case DATA_TYPE_LOCAL_NAME_SHORT: - case DATA_TYPE_LOCAL_NAME_COMPLETE: - localName = new String( - extractBytes(scanRecord, currentPos, dataLength)); - break; - case DATA_TYPE_TX_POWER_LEVEL: - txPowerLevel = scanRecord[currentPos]; - break; - case DATA_TYPE_SERVICE_DATA_16_BIT: - case DATA_TYPE_SERVICE_DATA_32_BIT: - case DATA_TYPE_SERVICE_DATA_128_BIT: - int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT; - if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) { - serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT; - } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) { - serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT; - } - - byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos, - serviceUuidLength); - ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom( - serviceDataUuidBytes); - byte[] serviceDataArray = extractBytes(scanRecord, - currentPos + serviceUuidLength, dataLength - serviceUuidLength); - serviceData.put(serviceDataUuid, serviceDataArray); - break; - case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: - // The first two bytes of the manufacturer specific data are - // manufacturer ids in little endian. - int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) - + (scanRecord[currentPos] & 0xFF); - byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2, - dataLength - 2); - manufacturerData.put(manufacturerId, manufacturerDataBytes); - break; - default: - // Just ignore, we don't handle such data type. - break; - } - currentPos += dataLength; - } - - if (serviceUuids.isEmpty()) { - serviceUuids = null; - } - return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData, - serviceData, advertiseFlag, txPowerLevel, localName, scanRecord); - } catch (Exception e) { - Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord)); - // As the record is invalid, ignore all the parsed results for this packet - // and return an empty record with raw scanRecord bytes in results - return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, scanRecord); - } - } - - @Override - public String toString() { - return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids - + ", mServiceSolicitationUuids=" + mServiceSolicitationUuids - + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString( - mManufacturerSpecificData) - + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) - + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]"; - } - - // Parse service UUIDs. - private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength, - int uuidLength, List<ParcelUuid> serviceUuids) { - while (dataLength > 0) { - byte[] uuidBytes = extractBytes(scanRecord, currentPos, - uuidLength); - serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes)); - dataLength -= uuidLength; - currentPos += uuidLength; - } - return currentPos; - } - - /** - * Parse service Solicitation UUIDs. - */ - private static int parseServiceSolicitationUuid(byte[] scanRecord, int currentPos, - int dataLength, int uuidLength, List<ParcelUuid> serviceSolicitationUuids) { - while (dataLength > 0) { - byte[] uuidBytes = extractBytes(scanRecord, currentPos, uuidLength); - serviceSolicitationUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes)); - dataLength -= uuidLength; - currentPos += uuidLength; - } - return currentPos; - } - - // Helper method to extract bytes from byte array. - private static byte[] extractBytes(byte[] scanRecord, int start, int length) { - byte[] bytes = new byte[length]; - System.arraycopy(scanRecord, start, bytes, 0, length); - return bytes; - } -} diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java deleted file mode 100644 index f437d867ea37..000000000000 --- a/core/java/android/bluetooth/le/ScanResult.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.bluetooth.Attributable; -import android.bluetooth.BluetoothDevice; -import android.content.AttributionSource; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Objects; - -/** - * ScanResult for Bluetooth LE scan. - */ -public final class ScanResult implements Parcelable, Attributable { - - /** - * For chained advertisements, inidcates tha the data contained in this - * scan result is complete. - */ - public static final int DATA_COMPLETE = 0x00; - - /** - * For chained advertisements, indicates that the controller was - * unable to receive all chained packets and the scan result contains - * incomplete truncated data. - */ - public static final int DATA_TRUNCATED = 0x02; - - /** - * Indicates that the secondary physical layer was not used. - */ - public static final int PHY_UNUSED = 0x00; - - /** - * Advertising Set ID is not present in the packet. - */ - public static final int SID_NOT_PRESENT = 0xFF; - - /** - * TX power is not present in the packet. - */ - public static final int TX_POWER_NOT_PRESENT = 0x7F; - - /** - * Periodic advertising interval is not present in the packet. - */ - public static final int PERIODIC_INTERVAL_NOT_PRESENT = 0x00; - - /** - * Mask for checking whether event type represents legacy advertisement. - */ - private static final int ET_LEGACY_MASK = 0x10; - - /** - * Mask for checking whether event type represents connectable advertisement. - */ - private static final int ET_CONNECTABLE_MASK = 0x01; - - // Remote Bluetooth device. - private BluetoothDevice mDevice; - - // Scan record, including advertising data and scan response data. - @Nullable - private ScanRecord mScanRecord; - - // Received signal strength. - private int mRssi; - - // Device timestamp when the result was last seen. - private long mTimestampNanos; - - private int mEventType; - private int mPrimaryPhy; - private int mSecondaryPhy; - private int mAdvertisingSid; - private int mTxPower; - private int mPeriodicAdvertisingInterval; - - /** - * Constructs a new ScanResult. - * - * @param device Remote Bluetooth device found. - * @param scanRecord Scan record including both advertising data and scan response data. - * @param rssi Received signal strength. - * @param timestampNanos Timestamp at which the scan result was observed. - * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int, - * ScanRecord, long)} - */ - @Deprecated - public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi, - long timestampNanos) { - mDevice = device; - mScanRecord = scanRecord; - mRssi = rssi; - mTimestampNanos = timestampNanos; - mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK; - mPrimaryPhy = BluetoothDevice.PHY_LE_1M; - mSecondaryPhy = PHY_UNUSED; - mAdvertisingSid = SID_NOT_PRESENT; - mTxPower = 127; - mPeriodicAdvertisingInterval = 0; - } - - /** - * Constructs a new ScanResult. - * - * @param device Remote Bluetooth device found. - * @param eventType Event type. - * @param primaryPhy Primary advertising phy. - * @param secondaryPhy Secondary advertising phy. - * @param advertisingSid Advertising set ID. - * @param txPower Transmit power. - * @param rssi Received signal strength. - * @param periodicAdvertisingInterval Periodic advertising interval. - * @param scanRecord Scan record including both advertising data and scan response data. - * @param timestampNanos Timestamp at which the scan result was observed. - */ - public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy, - int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval, - ScanRecord scanRecord, long timestampNanos) { - mDevice = device; - mEventType = eventType; - mPrimaryPhy = primaryPhy; - mSecondaryPhy = secondaryPhy; - mAdvertisingSid = advertisingSid; - mTxPower = txPower; - mRssi = rssi; - mPeriodicAdvertisingInterval = periodicAdvertisingInterval; - mScanRecord = scanRecord; - mTimestampNanos = timestampNanos; - } - - private ScanResult(Parcel in) { - readFromParcel(in); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - if (mDevice != null) { - dest.writeInt(1); - mDevice.writeToParcel(dest, flags); - } else { - dest.writeInt(0); - } - if (mScanRecord != null) { - dest.writeInt(1); - dest.writeByteArray(mScanRecord.getBytes()); - } else { - dest.writeInt(0); - } - dest.writeInt(mRssi); - dest.writeLong(mTimestampNanos); - dest.writeInt(mEventType); - dest.writeInt(mPrimaryPhy); - dest.writeInt(mSecondaryPhy); - dest.writeInt(mAdvertisingSid); - dest.writeInt(mTxPower); - dest.writeInt(mPeriodicAdvertisingInterval); - } - - private void readFromParcel(Parcel in) { - if (in.readInt() == 1) { - mDevice = BluetoothDevice.CREATOR.createFromParcel(in); - } - if (in.readInt() == 1) { - mScanRecord = ScanRecord.parseFromBytes(in.createByteArray()); - } - mRssi = in.readInt(); - mTimestampNanos = in.readLong(); - mEventType = in.readInt(); - mPrimaryPhy = in.readInt(); - mSecondaryPhy = in.readInt(); - mAdvertisingSid = in.readInt(); - mTxPower = in.readInt(); - mPeriodicAdvertisingInterval = in.readInt(); - } - - @Override - public int describeContents() { - return 0; - } - - /** {@hide} */ - public void setAttributionSource(@NonNull AttributionSource attributionSource) { - Attributable.setAttributionSource(mDevice, attributionSource); - } - - /** - * Returns the remote Bluetooth device identified by the Bluetooth device address. - */ - public BluetoothDevice getDevice() { - return mDevice; - } - - /** - * Returns the scan record, which is a combination of advertisement and scan response. - */ - @Nullable - public ScanRecord getScanRecord() { - return mScanRecord; - } - - /** - * Returns the received signal strength in dBm. The valid range is [-127, 126]. - */ - public int getRssi() { - return mRssi; - } - - /** - * Returns timestamp since boot when the scan record was observed. - */ - public long getTimestampNanos() { - return mTimestampNanos; - } - - /** - * Returns true if this object represents legacy scan result. - * Legacy scan results do not contain advanced advertising information - * as specified in the Bluetooth Core Specification v5. - */ - public boolean isLegacy() { - return (mEventType & ET_LEGACY_MASK) != 0; - } - - /** - * Returns true if this object represents connectable scan result. - */ - public boolean isConnectable() { - return (mEventType & ET_CONNECTABLE_MASK) != 0; - } - - /** - * Returns the data status. - * Can be one of {@link ScanResult#DATA_COMPLETE} or - * {@link ScanResult#DATA_TRUNCATED}. - */ - public int getDataStatus() { - // return bit 5 and 6 - return (mEventType >> 5) & 0x03; - } - - /** - * Returns the primary Physical Layer - * on which this advertisment was received. - * Can be one of {@link BluetoothDevice#PHY_LE_1M} or - * {@link BluetoothDevice#PHY_LE_CODED}. - */ - public int getPrimaryPhy() { - return mPrimaryPhy; - } - - /** - * Returns the secondary Physical Layer - * on which this advertisment was received. - * Can be one of {@link BluetoothDevice#PHY_LE_1M}, - * {@link BluetoothDevice#PHY_LE_2M}, {@link BluetoothDevice#PHY_LE_CODED} - * or {@link ScanResult#PHY_UNUSED} - if the advertisement - * was not received on a secondary physical channel. - */ - public int getSecondaryPhy() { - return mSecondaryPhy; - } - - /** - * Returns the advertising set id. - * May return {@link ScanResult#SID_NOT_PRESENT} if - * no set id was is present. - */ - public int getAdvertisingSid() { - return mAdvertisingSid; - } - - /** - * Returns the transmit power in dBm. - * Valid range is [-127, 126]. A value of {@link ScanResult#TX_POWER_NOT_PRESENT} - * indicates that the TX power is not present. - */ - public int getTxPower() { - return mTxPower; - } - - /** - * Returns the periodic advertising interval in units of 1.25ms. - * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of - * {@link ScanResult#PERIODIC_INTERVAL_NOT_PRESENT} means periodic - * advertising interval is not present. - */ - public int getPeriodicAdvertisingInterval() { - return mPeriodicAdvertisingInterval; - } - - @Override - public int hashCode() { - return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos, - mEventType, mPrimaryPhy, mSecondaryPhy, - mAdvertisingSid, mTxPower, - mPeriodicAdvertisingInterval); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - ScanResult other = (ScanResult) obj; - return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) - && Objects.equals(mScanRecord, other.mScanRecord) - && (mTimestampNanos == other.mTimestampNanos) - && mEventType == other.mEventType - && mPrimaryPhy == other.mPrimaryPhy - && mSecondaryPhy == other.mSecondaryPhy - && mAdvertisingSid == other.mAdvertisingSid - && mTxPower == other.mTxPower - && mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval; - } - - @Override - public String toString() { - return "ScanResult{" + "device=" + mDevice + ", scanRecord=" - + Objects.toString(mScanRecord) + ", rssi=" + mRssi - + ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType - + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy - + ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower - + ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}'; - } - - public static final @android.annotation.NonNull Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() { - @Override - public ScanResult createFromParcel(Parcel source) { - return new ScanResult(source); - } - - @Override - public ScanResult[] newArray(int size) { - return new ScanResult[size]; - } - }; - -} diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java deleted file mode 100644 index 1aa7cb5111ce..000000000000 --- a/core/java/android/bluetooth/le/ScanSettings.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.SystemApi; -import android.bluetooth.BluetoothDevice; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Bluetooth LE scan settings are passed to {@link BluetoothLeScanner#startScan} to define the - * parameters for the scan. - */ -public final class ScanSettings implements Parcelable { - - /** - * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for - * other scan results without starting BLE scans themselves. - */ - public static final int SCAN_MODE_OPPORTUNISTIC = -1; - - /** - * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the - * least power. This mode is enforced if the scanning application is not in foreground. - */ - public static final int SCAN_MODE_LOW_POWER = 0; - - /** - * Perform Bluetooth LE scan in balanced power mode. Scan results are returned at a rate that - * provides a good trade-off between scan frequency and power consumption. - */ - public static final int SCAN_MODE_BALANCED = 1; - - /** - * Scan using highest duty cycle. It's recommended to only use this mode when the application is - * running in the foreground. - */ - public static final int SCAN_MODE_LOW_LATENCY = 2; - - /** - * Perform Bluetooth LE scan in ambient discovery mode. This mode has lower duty cycle and more - * aggressive scan interval than balanced mode that provides a good trade-off between scan - * latency and power consumption. - * - * @hide - */ - @SystemApi - public static final int SCAN_MODE_AMBIENT_DISCOVERY = 3; - - /** - * Trigger a callback for every Bluetooth advertisement found that matches the filter criteria. - * If no filter is active, all advertisement packets are reported. - */ - public static final int CALLBACK_TYPE_ALL_MATCHES = 1; - - /** - * A result callback is only triggered for the first advertisement packet received that matches - * the filter criteria. - */ - public static final int CALLBACK_TYPE_FIRST_MATCH = 2; - - /** - * Receive a callback when advertisements are no longer received from a device that has been - * previously reported by a first match callback. - */ - public static final int CALLBACK_TYPE_MATCH_LOST = 4; - - - /** - * Determines how many advertisements to match per filter, as this is scarce hw resource - */ - /** - * Match one advertisement per filter - */ - public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1; - - /** - * Match few advertisement per filter, depends on current capability and availibility of - * the resources in hw - */ - public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2; - - /** - * Match as many advertisement per filter as hw could allow, depends on current - * capability and availibility of the resources in hw - */ - public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3; - - - /** - * In Aggressive mode, hw will determine a match sooner even with feeble signal strength - * and few number of sightings/match in a duration. - */ - public static final int MATCH_MODE_AGGRESSIVE = 1; - - /** - * For sticky mode, higher threshold of signal strength and sightings is required - * before reporting by hw - */ - public static final int MATCH_MODE_STICKY = 2; - - /** - * Request full scan results which contain the device, rssi, advertising data, scan response - * as well as the scan timestamp. - * - * @hide - */ - @SystemApi - public static final int SCAN_RESULT_TYPE_FULL = 0; - - /** - * Request abbreviated scan results which contain the device, rssi and scan timestamp. - * <p> - * <b>Note:</b> It is possible for an application to get more scan results than it asked for, if - * there are multiple apps using this type. - * - * @hide - */ - @SystemApi - public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1; - - /** - * Use all supported PHYs for scanning. - * This will check the controller capabilities, and start - * the scan on 1Mbit and LE Coded PHYs if supported, or on - * the 1Mbit PHY only. - */ - public static final int PHY_LE_ALL_SUPPORTED = 255; - - // Bluetooth LE scan mode. - private int mScanMode; - - // Bluetooth LE scan callback type - private int mCallbackType; - - // Bluetooth LE scan result type - private int mScanResultType; - - // Time of delay for reporting the scan result - private long mReportDelayMillis; - - private int mMatchMode; - - private int mNumOfMatchesPerFilter; - - // Include only legacy advertising results - private boolean mLegacy; - - private int mPhy; - - public int getScanMode() { - return mScanMode; - } - - public int getCallbackType() { - return mCallbackType; - } - - public int getScanResultType() { - return mScanResultType; - } - - /** - * @hide - */ - public int getMatchMode() { - return mMatchMode; - } - - /** - * @hide - */ - public int getNumOfMatches() { - return mNumOfMatchesPerFilter; - } - - /** - * Returns whether only legacy advertisements will be returned. - * Legacy advertisements include advertisements as specified - * by the Bluetooth core specification 4.2 and below. - */ - public boolean getLegacy() { - return mLegacy; - } - - /** - * Returns the physical layer used during a scan. - */ - public int getPhy() { - return mPhy; - } - - /** - * Returns report delay timestamp based on the device clock. - */ - public long getReportDelayMillis() { - return mReportDelayMillis; - } - - private ScanSettings(int scanMode, int callbackType, int scanResultType, - long reportDelayMillis, int matchMode, - int numOfMatchesPerFilter, boolean legacy, int phy) { - mScanMode = scanMode; - mCallbackType = callbackType; - mScanResultType = scanResultType; - mReportDelayMillis = reportDelayMillis; - mNumOfMatchesPerFilter = numOfMatchesPerFilter; - mMatchMode = matchMode; - mLegacy = legacy; - mPhy = phy; - } - - private ScanSettings(Parcel in) { - mScanMode = in.readInt(); - mCallbackType = in.readInt(); - mScanResultType = in.readInt(); - mReportDelayMillis = in.readLong(); - mMatchMode = in.readInt(); - mNumOfMatchesPerFilter = in.readInt(); - mLegacy = in.readInt() != 0; - mPhy = in.readInt(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mScanMode); - dest.writeInt(mCallbackType); - dest.writeInt(mScanResultType); - dest.writeLong(mReportDelayMillis); - dest.writeInt(mMatchMode); - dest.writeInt(mNumOfMatchesPerFilter); - dest.writeInt(mLegacy ? 1 : 0); - dest.writeInt(mPhy); - } - - @Override - public int describeContents() { - return 0; - } - - public static final @android.annotation.NonNull Parcelable.Creator<ScanSettings> CREATOR = - new Creator<ScanSettings>() { - @Override - public ScanSettings[] newArray(int size) { - return new ScanSettings[size]; - } - - @Override - public ScanSettings createFromParcel(Parcel in) { - return new ScanSettings(in); - } - }; - - /** - * Builder for {@link ScanSettings}. - */ - public static final class Builder { - private int mScanMode = SCAN_MODE_LOW_POWER; - private int mCallbackType = CALLBACK_TYPE_ALL_MATCHES; - private int mScanResultType = SCAN_RESULT_TYPE_FULL; - private long mReportDelayMillis = 0; - private int mMatchMode = MATCH_MODE_AGGRESSIVE; - private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT; - private boolean mLegacy = true; - private int mPhy = PHY_LE_ALL_SUPPORTED; - - /** - * Set scan mode for Bluetooth LE scan. - * - * @param scanMode The scan mode can be one of {@link ScanSettings#SCAN_MODE_LOW_POWER}, - * {@link ScanSettings#SCAN_MODE_BALANCED} or {@link ScanSettings#SCAN_MODE_LOW_LATENCY}. - * @throws IllegalArgumentException If the {@code scanMode} is invalid. - */ - public Builder setScanMode(int scanMode) { - switch (scanMode) { - case SCAN_MODE_OPPORTUNISTIC: - case SCAN_MODE_LOW_POWER: - case SCAN_MODE_BALANCED: - case SCAN_MODE_LOW_LATENCY: - case SCAN_MODE_AMBIENT_DISCOVERY: - mScanMode = scanMode; - break; - default: - throw new IllegalArgumentException("invalid scan mode " + scanMode); - } - return this; - } - - /** - * Set callback type for Bluetooth LE scan. - * - * @param callbackType The callback type flags for the scan. - * @throws IllegalArgumentException If the {@code callbackType} is invalid. - */ - public Builder setCallbackType(int callbackType) { - - if (!isValidCallbackType(callbackType)) { - throw new IllegalArgumentException("invalid callback type - " + callbackType); - } - mCallbackType = callbackType; - return this; - } - - // Returns true if the callbackType is valid. - private boolean isValidCallbackType(int callbackType) { - if (callbackType == CALLBACK_TYPE_ALL_MATCHES - || callbackType == CALLBACK_TYPE_FIRST_MATCH - || callbackType == CALLBACK_TYPE_MATCH_LOST) { - return true; - } - return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST); - } - - /** - * Set scan result type for Bluetooth LE scan. - * - * @param scanResultType Type for scan result, could be either {@link - * ScanSettings#SCAN_RESULT_TYPE_FULL} or {@link ScanSettings#SCAN_RESULT_TYPE_ABBREVIATED}. - * @throws IllegalArgumentException If the {@code scanResultType} is invalid. - * @hide - */ - @SystemApi - public Builder setScanResultType(int scanResultType) { - if (scanResultType < SCAN_RESULT_TYPE_FULL - || scanResultType > SCAN_RESULT_TYPE_ABBREVIATED) { - throw new IllegalArgumentException( - "invalid scanResultType - " + scanResultType); - } - mScanResultType = scanResultType; - return this; - } - - /** - * Set report delay timestamp for Bluetooth LE scan. If set to 0, you will be notified of - * scan results immediately. If > 0, scan results are queued up and delivered after the - * requested delay or 5000 milliseconds (whichever is higher). Note scan results may be - * delivered sooner if the internal buffers fill up. - * - * @param reportDelayMillis how frequently scan results should be delivered in - * milliseconds - * @throws IllegalArgumentException if {@code reportDelayMillis} < 0 - */ - public Builder setReportDelay(long reportDelayMillis) { - if (reportDelayMillis < 0) { - throw new IllegalArgumentException("reportDelay must be > 0"); - } - mReportDelayMillis = reportDelayMillis; - return this; - } - - /** - * Set the number of matches for Bluetooth LE scan filters hardware match - * - * @param numOfMatches The num of matches can be one of - * {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT} - * or {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or {@link - * ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT} - * @throws IllegalArgumentException If the {@code matchMode} is invalid. - */ - public Builder setNumOfMatches(int numOfMatches) { - if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT - || numOfMatches > MATCH_NUM_MAX_ADVERTISEMENT) { - throw new IllegalArgumentException("invalid numOfMatches " + numOfMatches); - } - mNumOfMatchesPerFilter = numOfMatches; - return this; - } - - /** - * Set match mode for Bluetooth LE scan filters hardware match - * - * @param matchMode The match mode can be one of {@link ScanSettings#MATCH_MODE_AGGRESSIVE} - * or {@link ScanSettings#MATCH_MODE_STICKY} - * @throws IllegalArgumentException If the {@code matchMode} is invalid. - */ - public Builder setMatchMode(int matchMode) { - if (matchMode < MATCH_MODE_AGGRESSIVE - || matchMode > MATCH_MODE_STICKY) { - throw new IllegalArgumentException("invalid matchMode " + matchMode); - } - mMatchMode = matchMode; - return this; - } - - /** - * Set whether only legacy advertisments should be returned in scan results. - * Legacy advertisements include advertisements as specified by the - * Bluetooth core specification 4.2 and below. This is true by default - * for compatibility with older apps. - * - * @param legacy true if only legacy advertisements will be returned - */ - public Builder setLegacy(boolean legacy) { - mLegacy = legacy; - return this; - } - - /** - * Set the Physical Layer to use during this scan. - * This is used only if {@link ScanSettings.Builder#setLegacy} - * is set to false. - * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported} - * may be used to check whether LE Coded phy is supported by calling - * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}. - * Selecting an unsupported phy will result in failure to start scan. - * - * @param phy Can be one of {@link BluetoothDevice#PHY_LE_1M}, {@link - * BluetoothDevice#PHY_LE_CODED} or {@link ScanSettings#PHY_LE_ALL_SUPPORTED} - */ - public Builder setPhy(int phy) { - mPhy = phy; - return this; - } - - /** - * Build {@link ScanSettings}. - */ - public ScanSettings build() { - return new ScanSettings(mScanMode, mCallbackType, mScanResultType, - mReportDelayMillis, mMatchMode, - mNumOfMatchesPerFilter, mLegacy, mPhy); - } - } -} diff --git a/core/java/android/bluetooth/le/TransportBlock.java b/core/java/android/bluetooth/le/TransportBlock.java deleted file mode 100644 index 18bad9c3c259..000000000000 --- a/core/java/android/bluetooth/le/TransportBlock.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; -import java.util.Arrays; - -/** - * Wrapper for Transport Discovery Data Transport Blocks. - * This class represents a Transport Block from a Transport Discovery Data. - * - * @see TransportDiscoveryData - * @see AdvertiseData - */ -public final class TransportBlock implements Parcelable { - private static final String TAG = "TransportBlock"; - private final int mOrgId; - private final int mTdsFlags; - private final int mTransportDataLength; - private final byte[] mTransportData; - - /** - * Creates an instance of TransportBlock from raw data. - * - * @param orgId the Organization ID - * @param tdsFlags the TDS flags - * @param transportDataLength the total length of the Transport Data - * @param transportData the Transport Data - */ - public TransportBlock(int orgId, int tdsFlags, int transportDataLength, - @Nullable byte[] transportData) { - mOrgId = orgId; - mTdsFlags = tdsFlags; - mTransportDataLength = transportDataLength; - mTransportData = transportData; - } - - private TransportBlock(Parcel in) { - mOrgId = in.readInt(); - mTdsFlags = in.readInt(); - mTransportDataLength = in.readInt(); - if (mTransportDataLength > 0) { - mTransportData = new byte[mTransportDataLength]; - in.readByteArray(mTransportData); - } else { - mTransportData = null; - } - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mOrgId); - dest.writeInt(mTdsFlags); - dest.writeInt(mTransportDataLength); - if (mTransportData != null) { - dest.writeByteArray(mTransportData); - } - } - - /** - * @hide - */ - @Override - public int describeContents() { - return 0; - } - - /** - * @hide - */ - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - TransportBlock other = (TransportBlock) obj; - return Arrays.equals(toByteArray(), other.toByteArray()); - } - - public static final @NonNull Creator<TransportBlock> CREATOR = new Creator<TransportBlock>() { - @Override - public TransportBlock createFromParcel(Parcel in) { - return new TransportBlock(in); - } - - @Override - public TransportBlock[] newArray(int size) { - return new TransportBlock[size]; - } - }; - - /** - * Gets the Organization ID of the Transport Block which corresponds to one of the - * the Bluetooth SIG Assigned Numbers. - */ - public int getOrgId() { - return mOrgId; - } - - /** - * Gets the TDS flags of the Transport Block which represents the role of the device and - * information about its state and supported features. - */ - public int getTdsFlags() { - return mTdsFlags; - } - - /** - * Gets the total number of octets in the Transport Data field in this Transport Block. - */ - public int getTransportDataLength() { - return mTransportDataLength; - } - - /** - * Gets the Transport Data of the Transport Block which contains organization-specific data. - */ - @Nullable - public byte[] getTransportData() { - return mTransportData; - } - - /** - * Converts this TransportBlock to byte array - * - * @return byte array representation of this Transport Block or null if the conversion failed - */ - @Nullable - public byte[] toByteArray() { - try { - ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); - buffer.put((byte) mOrgId); - buffer.put((byte) mTdsFlags); - buffer.put((byte) mTransportDataLength); - if (mTransportData != null) { - buffer.put(mTransportData); - } - return buffer.array(); - } catch (BufferOverflowException e) { - Log.e(TAG, "Error converting to byte array: " + e.toString()); - return null; - } - } - - /** - * @return total byte count of this TransportBlock - */ - public int totalBytes() { - // 3 uint8 + byte[] length - int size = 3 + mTransportDataLength; - return size; - } -} diff --git a/core/java/android/bluetooth/le/TransportDiscoveryData.java b/core/java/android/bluetooth/le/TransportDiscoveryData.java deleted file mode 100644 index 2b52f19798ad..000000000000 --- a/core/java/android/bluetooth/le/TransportDiscoveryData.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * Wrapper for Transport Discovery Data AD Type. - * This class contains the Transport Discovery Data AD Type Code as well as - * a list of potential Transport Blocks. - * - * @see AdvertiseData - */ -public final class TransportDiscoveryData implements Parcelable { - private static final String TAG = "TransportDiscoveryData"; - private final int mTransportDataType; - private final List<TransportBlock> mTransportBlocks; - - /** - * Creates a TransportDiscoveryData instance. - * - * @param transportDataType the Transport Discovery Data AD Type - * @param transportBlocks the list of Transport Blocks - */ - public TransportDiscoveryData(int transportDataType, - @NonNull List<TransportBlock> transportBlocks) { - mTransportDataType = transportDataType; - mTransportBlocks = transportBlocks; - } - - /** - * Creates a TransportDiscoveryData instance from byte arrays. - * - * Uses the transport discovery data bytes and parses them into an usable class. - * - * @param transportDiscoveryData the raw discovery data - */ - public TransportDiscoveryData(@NonNull byte[] transportDiscoveryData) { - ByteBuffer byteBuffer = ByteBuffer.wrap(transportDiscoveryData); - mTransportBlocks = new ArrayList(); - if (byteBuffer.remaining() > 0) { - mTransportDataType = byteBuffer.get(); - } else { - mTransportDataType = -1; - } - try { - while (byteBuffer.remaining() > 0) { - int orgId = byteBuffer.get(); - int tdsFlags = byteBuffer.get(); - int transportDataLength = byteBuffer.get(); - byte[] transportData = new byte[transportDataLength]; - byteBuffer.get(transportData, 0, transportDataLength); - mTransportBlocks.add(new TransportBlock(orgId, tdsFlags, - transportDataLength, transportData)); - } - } catch (BufferUnderflowException e) { - Log.e(TAG, "Error while parsing data: " + e.toString()); - } - } - - private TransportDiscoveryData(Parcel in) { - mTransportDataType = in.readInt(); - mTransportBlocks = in.createTypedArrayList(TransportBlock.CREATOR); - } - - /** - * @hide - */ - @Override - public int describeContents() { - return 0; - } - - /** - * @hide - */ - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - TransportDiscoveryData other = (TransportDiscoveryData) obj; - return Arrays.equals(toByteArray(), other.toByteArray()); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mTransportDataType); - dest.writeTypedList(mTransportBlocks); - } - - public static final @NonNull Creator<TransportDiscoveryData> CREATOR = - new Creator<TransportDiscoveryData>() { - @Override - public TransportDiscoveryData createFromParcel(Parcel in) { - return new TransportDiscoveryData(in); - } - - @Override - public TransportDiscoveryData[] newArray(int size) { - return new TransportDiscoveryData[size]; - } - }; - - /** - * Gets the transport data type. - */ - public int getTransportDataType() { - return mTransportDataType; - } - - /** - * @return the list of {@link TransportBlock} in this TransportDiscoveryData - * or an empty list if there are no Transport Blocks - */ - @NonNull - public List<TransportBlock> getTransportBlocks() { - if (mTransportBlocks == null) { - return Collections.emptyList(); - } - return mTransportBlocks; - } - - /** - * Converts this TransportDiscoveryData to byte array - * - * @return byte array representation of this Transport Discovery Data or null if the - * conversion failed - */ - @Nullable - public byte[] toByteArray() { - try { - ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); - buffer.put((byte) mTransportDataType); - for (TransportBlock transportBlock : getTransportBlocks()) { - buffer.put(transportBlock.toByteArray()); - } - return buffer.array(); - } catch (BufferOverflowException e) { - Log.e(TAG, "Error converting to byte array: " + e.toString()); - return null; - } - } - - /** - * @return total byte count of this TransportDataDiscovery - */ - public int totalBytes() { - int size = 1; // Counting Transport Data Type here. - for (TransportBlock transportBlock : getTransportBlocks()) { - size += transportBlock.totalBytes(); - } - return size; - } -} diff --git a/core/java/android/bluetooth/le/TruncatedFilter.java b/core/java/android/bluetooth/le/TruncatedFilter.java deleted file mode 100644 index 25925888a0d2..000000000000 --- a/core/java/android/bluetooth/le/TruncatedFilter.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.annotation.SuppressLint; -import android.annotation.SystemApi; - -import java.util.List; - -/** - * A special scan filter that lets the client decide how the scan record should be stored. - * - * @deprecated this is not used anywhere - * - * @hide - */ -@Deprecated -@SystemApi -@SuppressLint("AndroidFrameworkBluetoothPermission") -public final class TruncatedFilter { - private final ScanFilter mFilter; - private final List<ResultStorageDescriptor> mStorageDescriptors; - - /** - * Constructor for {@link TruncatedFilter}. - * - * @param filter Scan filter of the truncated filter. - * @param storageDescriptors Describes how the scan should be stored. - */ - public TruncatedFilter(ScanFilter filter, List<ResultStorageDescriptor> storageDescriptors) { - mFilter = filter; - mStorageDescriptors = storageDescriptors; - } - - /** - * Returns the scan filter. - */ - public ScanFilter getFilter() { - return mFilter; - } - - /** - * Returns a list of descriptor for scan result storage. - */ - public List<ResultStorageDescriptor> getStorageDescriptors() { - return mStorageDescriptors; - } - - -} diff --git a/core/java/android/bluetooth/package.html b/core/java/android/bluetooth/package.html deleted file mode 100644 index d9ca4f13101f..000000000000 --- a/core/java/android/bluetooth/package.html +++ /dev/null @@ -1,38 +0,0 @@ -<HTML> -<BODY> -<p>Provides classes that manage Bluetooth functionality, such as scanning for -devices, connecting with devices, and managing data transfer between devices. -The Bluetooth API supports both "Classic Bluetooth" and Bluetooth Low Energy.</p> - -<p>For more information about Classic Bluetooth, see the -<a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> guide. -For more information about Bluetooth Low Energy, see the -<a href="{@docRoot}guide/topics/connectivity/bluetooth-le.html"> -Bluetooth Low Energy</a> (BLE) guide.</p> -{@more} - -<p>The Bluetooth APIs let applications:</p> -<ul> - <li>Scan for other Bluetooth devices (including BLE devices).</li> - <li>Query the local Bluetooth adapter for paired Bluetooth devices.</li> - <li>Establish RFCOMM channels/sockets.</li> - <li>Connect to specified sockets on other devices.</li> - <li>Transfer data to and from other devices.</li> - <li>Communicate with BLE devices, such as proximity sensors, heart rate - monitors, fitness devices, and so on.</li> - <li>Act as a GATT client or a GATT server (BLE).</li> -</ul> - -<p> -To perform Bluetooth communication using these APIs, an application must -declare the {@link android.Manifest.permission#BLUETOOTH} permission. Some -additional functionality, such as requesting device discovery, -also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} -permission. -</p> - -<p class="note"><strong>Note:</strong> -Not all Android-powered devices provide Bluetooth functionality.</p> - -</BODY> -</HTML> 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/ISerialManager.aidl b/core/java/android/hardware/ISerialManager.aidl index 74d30f7afefe..65a0fa4f893e 100644 --- a/core/java/android/hardware/ISerialManager.aidl +++ b/core/java/android/hardware/ISerialManager.aidl @@ -22,8 +22,10 @@ import android.os.ParcelFileDescriptor; interface ISerialManager { /* Returns a list of all available serial ports */ + @EnforcePermission("SERIAL_PORT") String[] getSerialPorts(); /* Returns a file descriptor for the serial port. */ + @EnforcePermission("SERIAL_PORT") ParcelFileDescriptor openSerialPort(String name); } 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..6284f56c8258 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 notifyStatsProviderWarningOrLimitReached(); 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/LocalServerSocket.java b/core/java/android/net/LocalServerSocket.java index d1f49d2082f5..506cbcb0623d 100644 --- a/core/java/android/net/LocalServerSocket.java +++ b/core/java/android/net/LocalServerSocket.java @@ -55,7 +55,9 @@ public class LocalServerSocket implements Closeable { * Create a LocalServerSocket from a file descriptor that's already * been created and bound. listen() will be called immediately on it. * Used for cases where file descriptors are passed in via environment - * variables + * variables. The passed-in FileDescriptor is not managed by this class + * and must be closed by the caller. Calling {@link #close()} on a socket + * created by this method has no effect. * * @param fd bound file descriptor * @throws IOException diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java index 5b38f78782a8..b69410cf7d73 100644 --- a/core/java/android/net/LocalSocket.java +++ b/core/java/android/net/LocalSocket.java @@ -16,7 +16,14 @@ package android.net; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; +import android.system.ErrnoException; +import android.system.Os; import java.io.Closeable; import java.io.FileDescriptor; @@ -74,32 +81,39 @@ public class LocalSocket implements Closeable { this.isBound = false; } + private void checkConnected() { + try { + Os.getpeername(impl.getFileDescriptor()); + } catch (ErrnoException e) { + throw new IllegalArgumentException("Not a connected socket", e); + } + isConnected = true; + isBound = true; + implCreated = true; + } + /** - * Creates a LocalSocket instances using the FileDescriptor for an already-connected - * AF_LOCAL/UNIX domain stream socket. Note: the FileDescriptor must be closed by the caller: - * closing the LocalSocket will not close it. + * Creates a LocalSocket instance using the {@link FileDescriptor} for an already-connected + * AF_LOCAL/UNIX domain stream socket. The passed-in FileDescriptor is not managed by this class + * and must be closed by the caller. Calling {@link #close()} on a socket created by this + * method has no effect. + * + * @param fd the filedescriptor to adopt * - * @hide - used by BluetoothSocket. + * @hide */ - public static LocalSocket createConnectedLocalSocket(FileDescriptor fd) { - return createConnectedLocalSocket(new LocalSocketImpl(fd), SOCKET_UNKNOWN); + @SystemApi(client = MODULE_LIBRARIES) + public LocalSocket(@NonNull @SuppressLint("UseParcelFileDescriptor") FileDescriptor fd) { + this(new LocalSocketImpl(fd), SOCKET_UNKNOWN); + checkConnected(); } /** * for use with LocalServerSocket.accept() */ static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl) { - return createConnectedLocalSocket(impl, SOCKET_UNKNOWN); - } - - /** - * Creates a LocalSocket from an existing LocalSocketImpl that is already connected. - */ - private static LocalSocket createConnectedLocalSocket(LocalSocketImpl impl, int sockType) { - LocalSocket socket = new LocalSocket(impl, sockType); - socket.isConnected = true; - socket.isBound = true; - socket.implCreated = true; + LocalSocket socket = new LocalSocket(impl, SOCKET_UNKNOWN); + socket.checkConnected(); return socket; } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 7ebb646ba3eb..c936bfa05118 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 notifyStatsProviderWarningOrLimitReached() { + try { + mService.notifyStatsProviderWarningOrLimitReached(); + } 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..2339656979b5 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, a carrier-owned underlying network + * will still be selected (if available, 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/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 0f94cbef3886..86e6db1dc625 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -127,6 +127,7 @@ public final class BatteryUsageStats implements Parcelable { private final AggregateBatteryConsumer[] mAggregateBatteryConsumers; private final Parcel mHistoryBuffer; private final List<BatteryStats.HistoryTag> mHistoryTagPool; + private final BatteryStatsHistory mBatteryStatsHistory; private BatteryUsageStats(@NonNull Builder builder) { mStatsStartTimestampMs = builder.mStatsStartTimestampMs; @@ -138,6 +139,7 @@ public final class BatteryUsageStats implements Parcelable { mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah; mHistoryBuffer = builder.mHistoryBuffer; mHistoryTagPool = builder.mHistoryTagPool; + mBatteryStatsHistory = builder.mBatteryStatsHistory; mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs; mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs; mCustomPowerComponentNames = builder.mCustomPowerComponentNames; @@ -289,8 +291,8 @@ public final class BatteryUsageStats implements Parcelable { throw new IllegalStateException( "Battery history was not requested in the BatteryUsageStatsQuery"); } - return new BatteryStatsHistoryIterator(new BatteryStatsHistory(mHistoryBuffer), - mHistoryTagPool); + + return new BatteryStatsHistoryIterator(mBatteryStatsHistory, mHistoryTagPool); } @Override @@ -356,7 +358,10 @@ public final class BatteryUsageStats implements Parcelable { tag.poolIdx = source.readInt(); mHistoryTagPool.add(tag); } + mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer); + mBatteryStatsHistory.readFromBatteryUsageStatsParcel(source); } else { + mBatteryStatsHistory = null; mHistoryBuffer = null; mHistoryTagPool = null; } @@ -404,6 +409,7 @@ public final class BatteryUsageStats implements Parcelable { dest.writeInt(tag.uid); dest.writeInt(tag.poolIdx); } + mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest); } else { dest.writeBoolean(false); } @@ -757,6 +763,7 @@ public final class BatteryUsageStats implements Parcelable { new SparseArray<>(); private Parcel mHistoryBuffer; private List<BatteryStats.HistoryTag> mHistoryTagPool; + private BatteryStatsHistory mBatteryStatsHistory; public Builder(@NonNull String[] customPowerComponentNames) { this(customPowerComponentNames, false); @@ -865,10 +872,12 @@ public final class BatteryUsageStats implements Parcelable { * Sets the parceled recent history. */ @NonNull - public Builder setBatteryHistory(Parcel historyBuffer, - List<BatteryStats.HistoryTag> historyTagPool) { + public Builder setBatteryHistory(@NonNull Parcel historyBuffer, + @NonNull List<BatteryStats.HistoryTag> historyTagPool, + @NonNull BatteryStatsHistory batteryStatsHistory) { mHistoryBuffer = historyBuffer; mHistoryTagPool = historyTagPool; + mBatteryStatsHistory = batteryStatsHistory; return this; } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 09eac79c991e..ffaff9533c8c 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -3704,10 +3704,10 @@ public final class Parcel { final int m = list.size(); int i = 0; for (; i < m && i < n; i++) { - list.set(i, (T) readParcelableInternal(cl, clazz)); + list.set(i, readParcelableInternal(cl, clazz)); } for (; i < n; i++) { - list.add((T) readParcelableInternal(cl, clazz)); + list.add(readParcelableInternal(cl, clazz)); } for (; i < m; i++) { list.remove(n); @@ -4210,7 +4210,8 @@ public final class Parcel { * trying to instantiate an element. */ @Nullable - public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) { + public <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader, + @NonNull Class<? super T> clazz) { Objects.requireNonNull(clazz); return readParcelableInternal(loader, clazz); } @@ -4220,7 +4221,8 @@ public final class Parcel { */ @SuppressWarnings("unchecked") @Nullable - private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) { + private <T extends Parcelable> T readParcelableInternal(@Nullable ClassLoader loader, + @Nullable Class<? super T> clazz) { Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz); if (creator == null) { return null; @@ -4456,7 +4458,8 @@ public final class Parcel { * deserializing the object. */ @Nullable - public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) { + public <T extends Serializable> T readSerializable(@Nullable ClassLoader loader, + @NonNull Class<? super T> clazz) { Objects.requireNonNull(clazz); return readSerializableInternal( loader == null ? getClass().getClassLoader() : loader, clazz); @@ -4466,8 +4469,8 @@ public final class Parcel { * @param clazz The type of the serializable expected or {@code null} for performing no checks */ @Nullable - private <T> T readSerializableInternal(@Nullable final ClassLoader loader, - @Nullable Class<T> clazz) { + private <T extends Serializable> T readSerializableInternal(@Nullable final ClassLoader loader, + @Nullable Class<? super T> clazz) { String name = readString(); if (name == null) { // For some reason we were unable to read the name of the Serializable (either there diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 9f37c4877199..2030571dffe9 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -129,6 +129,7 @@ public class Process { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @TestApi + @SystemApi(client = MODULE_LIBRARIES) public static final int NFC_UID = 1027; /** diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index d974e0c0713a..6b869f13059d 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -16,7 +16,10 @@ package android.os; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; + import android.annotation.NonNull; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import dalvik.annotation.optimization.CriticalNative; @@ -90,6 +93,7 @@ public final class Trace { /** @hide */ public static final long TRACE_TAG_DATABASE = 1L << 20; /** @hide */ + @SystemApi(client = MODULE_LIBRARIES) public static final long TRACE_TAG_NETWORK = 1L << 21; /** @hide */ public static final long TRACE_TAG_ADB = 1L << 22; @@ -148,6 +152,7 @@ public final class Trace { * @hide */ @UnsupportedAppUsage + @SystemApi(client = MODULE_LIBRARIES) public static boolean isTagEnabled(long traceTag) { long tags = nativeGetEnabledTags(); return (tags & traceTag) != 0; @@ -163,7 +168,8 @@ public final class Trace { * @hide */ @UnsupportedAppUsage - public static void traceCounter(long traceTag, String counterName, int counterValue) { + @SystemApi(client = MODULE_LIBRARIES) + public static void traceCounter(long traceTag, @NonNull String counterName, int counterValue) { if (isTagEnabled(traceTag)) { nativeTraceCounter(traceTag, counterName, counterValue); } @@ -202,7 +208,8 @@ public final class Trace { * @hide */ @UnsupportedAppUsage - public static void traceBegin(long traceTag, String methodName) { + @SystemApi(client = MODULE_LIBRARIES) + public static void traceBegin(long traceTag, @NonNull String methodName) { if (isTagEnabled(traceTag)) { nativeTraceBegin(traceTag, methodName); } @@ -217,6 +224,7 @@ public final class Trace { * @hide */ @UnsupportedAppUsage + @SystemApi(client = MODULE_LIBRARIES) public static void traceEnd(long traceTag) { if (isTagEnabled(traceTag)) { nativeTraceEnd(traceTag); @@ -237,7 +245,8 @@ public final class Trace { * @hide */ @UnsupportedAppUsage - public static void asyncTraceBegin(long traceTag, String methodName, int cookie) { + @SystemApi(client = MODULE_LIBRARIES) + public static void asyncTraceBegin(long traceTag, @NonNull String methodName, int cookie) { if (isTagEnabled(traceTag)) { nativeAsyncTraceBegin(traceTag, methodName, cookie); } @@ -255,7 +264,8 @@ public final class Trace { * @hide */ @UnsupportedAppUsage - public static void asyncTraceEnd(long traceTag, String methodName, int cookie) { + @SystemApi(client = MODULE_LIBRARIES) + public static void asyncTraceEnd(long traceTag, @NonNull String methodName, int cookie) { if (isTagEnabled(traceTag)) { nativeAsyncTraceEnd(traceTag, methodName, cookie); } diff --git a/core/java/android/service/security/attestationverification/OWNERS b/core/java/android/service/security/attestationverification/OWNERS new file mode 100644 index 000000000000..12c997868f3c --- /dev/null +++ b/core/java/android/service/security/attestationverification/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/core/java/android/security/attestationverification/OWNERS diff --git a/core/java/android/service/wallpapereffectsgeneration/OWNERS b/core/java/android/service/wallpapereffectsgeneration/OWNERS new file mode 100644 index 000000000000..d2d3e2c0a7b6 --- /dev/null +++ b/core/java/android/service/wallpapereffectsgeneration/OWNERS @@ -0,0 +1,4 @@ +susharon@google.com +shanh@google.com +huiwu@google.com +srazdan@google.com 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/midi/OWNERS b/core/java/com/android/internal/midi/OWNERS new file mode 100644 index 000000000000..af273a6f50e0 --- /dev/null +++ b/core/java/com/android/internal/midi/OWNERS @@ -0,0 +1 @@ +include /services/midi/OWNERS 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/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index 879e0a8cfe10..8ebb11dd752b 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -160,6 +160,11 @@ public class BatteryStatsHistory { mHistoryDir = null; mHistoryBuffer = historyBuffer; } + + public File getHistoryDirectory() { + return mHistoryDir; + } + /** * Set the active file that mHistoryBuffer is backed up into. * @@ -375,12 +380,24 @@ public class BatteryStatsHistory { } /** + * Read all history files and serialize into a big Parcel. + * Checkin file calls this method. + * @param out the output parcel + */ + public void writeToParcel(Parcel out) { + writeToParcel(out, false /* useBlobs */); + } + + /** * Read all history files and serialize into a big Parcel. This is to send history files to * Settings app since Settings app can not access /data/system directory. - * Checkin file also call this method. * @param out the output parcel */ - public void writeToParcel(Parcel out) { + public void writeToBatteryUsageStatsParcel(Parcel out) { + writeToParcel(out, true /* useBlobs */); + } + + private void writeToParcel(Parcel out, boolean useBlobs) { final long start = SystemClock.uptimeMillis(); out.writeInt(mFileNumbers.size() - 1); for(int i = 0; i < mFileNumbers.size() - 1; i++) { @@ -391,7 +408,12 @@ public class BatteryStatsHistory { } catch(Exception e) { Slog.e(TAG, "Error reading file "+ file.getBaseFile().getPath(), e); } - out.writeByteArray(raw); + if (useBlobs) { + out.writeBlob(raw); + } else { + // Avoiding blobs in the check-in file for compatibility + out.writeByteArray(raw); + } } if (DEBUG) { Slog.d(TAG, "writeToParcel duration ms:" + (SystemClock.uptimeMillis() - start)); @@ -399,18 +421,29 @@ public class BatteryStatsHistory { } /** - * This is for Settings app, when Settings app receives big history parcel, it call - * this method to parse it into list of parcels. - * Checkin file also call this method. + * This is for the check-in file, which has all history files embedded. * @param in the input parcel. */ public void readFromParcel(Parcel in) { + readFromParcel(in, false /* useBlobs */); + } + + /** + * This is for Settings app, when Settings app receives big history parcel, it calls + * this method to parse it into list of parcels. + * @param in the input parcel. + */ + public void readFromBatteryUsageStatsParcel(Parcel in) { + readFromParcel(in, true /* useBlobs */); + } + + private void readFromParcel(Parcel in, boolean useBlobs) { final long start = SystemClock.uptimeMillis(); mHistoryParcels = new ArrayList<>(); final int count = in.readInt(); for(int i = 0; i < count; i++) { - byte[] temp = in.createByteArray(); - if (temp.length == 0) { + byte[] temp = useBlobs ? in.readBlob() : in.createByteArray(); + if (temp == null || temp.length == 0) { continue; } Parcel p = Parcel.obtain(); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 0f1c6f3a150a..58a0622ba53b 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; @@ -83,7 +83,6 @@ import android.util.Log; import android.util.LongSparseArray; import android.util.LongSparseLongArray; import android.util.MutableInt; -import android.util.Pools; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Slog; @@ -1201,12 +1200,21 @@ public class BatteryStatsImpl extends BatteryStats { } public BatteryStatsImpl(Clocks clocks) { + this(clocks, (File) null); + } + + public BatteryStatsImpl(Clocks clocks, File historyDirectory) { init(clocks); mStartClockTimeMs = clocks.currentTimeMillis(); - mStatsFile = null; mCheckinFile = null; mDailyFile = null; - mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer); + if (historyDirectory == null) { + mStatsFile = null; + mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer); + } else { + mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin")); + mBatteryStatsHistory = new BatteryStatsHistory(this, historyDirectory, mHistoryBuffer); + } mHandler = null; mPlatformIdleStateCallback = null; mMeasuredEnergyRetriever = null; @@ -11520,8 +11528,6 @@ public class BatteryStatsImpl extends BatteryStats { } } - private final Pools.Pool<NetworkStats> mNetworkStatsPool = new Pools.SynchronizedPool<>(6); - private final Object mWifiNetworkLock = new Object(); @GuardedBy("mWifiNetworkLock") @@ -11539,21 +11545,15 @@ 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); - } - return null; + protected NetworkStats readMobileNetworkStatsLocked( + @NonNull NetworkStatsManager networkStatsManager) { + return networkStatsManager.getMobileUidStats(); + } + + @VisibleForTesting + protected NetworkStats readWifiNetworkStatsLocked( + @NonNull NetworkStatsManager networkStatsManager) { + return networkStatsManager.getWifiUidStats(); } /** @@ -11561,7 +11561,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,20 +11570,15 @@ 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 = readWifiNetworkStatsLocked(networkStatsManager); if (latestStats != null) { - delta = NetworkStats.subtract(latestStats, mLastWifiNetworkStats, null, null, - mNetworkStatsPool.acquire()); - mNetworkStatsPool.release(mLastWifiNetworkStats); + delta = latestStats.subtract(mLastWifiNetworkStats); mLastWifiNetworkStats = latestStats; } } synchronized (this) { if (!mOnBatteryInternal || mIgnoreNextExternalStats) { - if (delta != null) { - mNetworkStatsPool.release(delta); - } if (mIgnoreNextExternalStats) { // TODO: Strictly speaking, we should re-mark all 5 timers for each uid (and the // global one) here like we do for display. But I'm not sure it's worth the @@ -11603,61 +11599,60 @@ public class BatteryStatsImpl extends BatteryStats { long totalTxPackets = 0; long totalRxPackets = 0; if (delta != null) { - NetworkStats.Entry entry = new NetworkStats.Entry(); - final int size = delta.size(); - for (int i = 0; i < size; i++) { - entry = delta.getValues(i, entry); - + for (NetworkStats.Entry entry : delta) { if (DEBUG_ENERGY) { - Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes - + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets - + " txPackets=" + entry.txPackets); + Slog.d(TAG, "Wifi uid " + entry.getUid() + + ": delta rx=" + entry.getRxBytes() + + " tx=" + entry.getTxBytes() + + " rxPackets=" + entry.getRxPackets() + + " txPackets=" + entry.getTxPackets()); } - if (entry.rxBytes == 0 && entry.txBytes == 0) { + if (entry.getRxBytes() == 0 && entry.getTxBytes() == 0) { // Skip the lookup below since there is no work to do. continue; } - final Uid u = getUidStatsLocked(mapUid(entry.uid), elapsedRealtimeMs, uptimeMs); - if (entry.rxBytes != 0) { - u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes, - entry.rxPackets); - if (entry.set == NetworkStats.SET_DEFAULT) { // Background transfers - u.noteNetworkActivityLocked(NETWORK_WIFI_BG_RX_DATA, entry.rxBytes, - entry.rxPackets); + final Uid u = getUidStatsLocked(mapUid(entry.getUid()), + elapsedRealtimeMs, uptimeMs); + if (entry.getRxBytes() != 0) { + u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.getRxBytes(), + entry.getRxPackets()); + if (entry.getSet() == NetworkStats.SET_DEFAULT) { // Background transfers + u.noteNetworkActivityLocked(NETWORK_WIFI_BG_RX_DATA, entry.getRxBytes(), + entry.getRxPackets()); } mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( - entry.rxBytes); + entry.getRxBytes()); mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( - entry.rxPackets); + entry.getRxPackets()); // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum? - rxPackets.put(u.getUid(), entry.rxPackets); + rxPackets.put(u.getUid(), entry.getRxPackets()); // Sum the total number of packets so that the Rx Power can // be evenly distributed amongst the apps. - totalRxPackets += entry.rxPackets; + totalRxPackets += entry.getRxPackets(); } - if (entry.txBytes != 0) { - u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes, - entry.txPackets); - if (entry.set == NetworkStats.SET_DEFAULT) { // Background transfers - u.noteNetworkActivityLocked(NETWORK_WIFI_BG_TX_DATA, entry.txBytes, - entry.txPackets); + if (entry.getTxBytes() != 0) { + u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.getTxBytes(), + entry.getTxPackets()); + if (entry.getSet() == NetworkStats.SET_DEFAULT) { // Background transfers + u.noteNetworkActivityLocked(NETWORK_WIFI_BG_TX_DATA, entry.getTxBytes(), + entry.getTxPackets()); } mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( - entry.txBytes); + entry.getTxBytes()); mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( - entry.txPackets); + entry.getTxPackets()); // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum? - txPackets.put(u.getUid(), entry.txPackets); + txPackets.put(u.getUid(), entry.getTxPackets()); // Sum the total number of packets so that the Tx Power can // be evenly distributed amongst the apps. - totalTxPackets += entry.txPackets; + totalTxPackets += entry.getTxPackets(); } // Calculate consumed energy for this uid. Only do so if WifiReporting isn't @@ -11685,11 +11680,10 @@ public class BatteryStatsImpl extends BatteryStats { uidEstimatedConsumptionMah.add(u.getUid(), mWifiPowerCalculator.calcPowerWithoutControllerDataMah( - entry.rxPackets, entry.txPackets, + entry.getRxPackets(), entry.getTxPackets(), uidRunningMs, uidScanMs, uidBatchScanMs)); } } - mNetworkStatsPool.release(delta); delta = null; } @@ -11921,7 +11915,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,20 +11930,15 @@ 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 = readMobileNetworkStatsLocked(networkStatsManager); if (latestStats != null) { - delta = NetworkStats.subtract(latestStats, mLastModemNetworkStats, null, null, - mNetworkStatsPool.acquire()); - mNetworkStatsPool.release(mLastModemNetworkStats); + delta = latestStats.subtract(mLastModemNetworkStats); mLastModemNetworkStats = latestStats; } } synchronized (this) { if (!mOnBatteryInternal || mIgnoreNextExternalStats) { - if (delta != null) { - mNetworkStatsPool.release(delta); - } return; } @@ -12150,7 +12140,6 @@ public class BatteryStatsImpl extends BatteryStats { totalEstimatedConsumptionMah); } - mNetworkStatsPool.release(delta); delta = null; } } diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index 980aec196079..79832867d01f 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -21,6 +21,7 @@ import android.hardware.SensorManager; import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; +import android.os.Parcel; import android.os.SystemClock; import android.os.UidBatteryConsumer; import android.util.Log; @@ -28,6 +29,7 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -186,16 +188,28 @@ public class BatteryUsageStatsProvider { } BatteryStatsImpl batteryStatsImpl = (BatteryStatsImpl) mStats; + + // Make a copy of battery history to avoid concurrent modification. + Parcel historyBuffer = Parcel.obtain(); + historyBuffer.appendFrom(batteryStatsImpl.mHistoryBuffer, 0, + batteryStatsImpl.mHistoryBuffer.dataSize()); + ArrayList<BatteryStats.HistoryTag> tags = new ArrayList<>( batteryStatsImpl.mHistoryTagPool.size()); for (Map.Entry<BatteryStats.HistoryTag, Integer> entry : batteryStatsImpl.mHistoryTagPool.entrySet()) { - final BatteryStats.HistoryTag tag = entry.getKey(); + final BatteryStats.HistoryTag tag = new BatteryStats.HistoryTag(); + tag.setTo(entry.getKey()); tag.poolIdx = entry.getValue(); tags.add(tag); } - batteryUsageStatsBuilder.setBatteryHistory(batteryStatsImpl.mHistoryBuffer, tags); + final File systemDir = + batteryStatsImpl.mBatteryStatsHistory.getHistoryDirectory().getParentFile(); + final BatteryStatsHistory batteryStatsHistory = + new BatteryStatsHistory(batteryStatsImpl, systemDir, historyBuffer); + + batteryUsageStatsBuilder.setBatteryHistory(historyBuffer, tags, batteryStatsHistory); } return batteryUsageStatsBuilder.build(); diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java index 20cf102953e4..e9d55db3a5b4 100644 --- a/core/java/com/android/internal/os/BinderLatencyObserver.java +++ b/core/java/com/android/internal/os/BinderLatencyObserver.java @@ -19,7 +19,6 @@ package com.android.internal.os; import android.annotation.Nullable; import android.os.Binder; import android.os.Handler; -import android.os.Looper; import android.os.SystemClock; import android.util.ArrayMap; import android.util.Slog; @@ -181,7 +180,7 @@ public class BinderLatencyObserver { } public Handler getHandler() { - return new Handler(Looper.getMainLooper()); + return BackgroundThread.getHandler(); } } diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 8d1f16b4b259..44c7f5406713 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -22,6 +22,7 @@ import android.app.ApplicationErrorReport; import android.app.IActivityManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.type.DefaultMimeMapFactory; +import android.net.TrafficStats; import android.os.Build; import android.os.DeadObjectException; import android.os.IBinder; @@ -32,7 +33,6 @@ import android.util.Log; import android.util.Slog; import com.android.internal.logging.AndroidConfig; -import com.android.server.NetworkManagementSocketTagger; import dalvik.system.RuntimeHooks; import dalvik.system.VMRuntime; @@ -254,7 +254,7 @@ public class RuntimeInit { /* * Wire socket tagging to traffic stats. */ - NetworkManagementSocketTagger.install(); + TrafficStats.attachSocketTagger(); initialized = true; } diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java index 3ca33203f554..4b3af1536175 100644 --- a/core/java/com/android/internal/util/FileRotator.java +++ b/core/java/com/android/internal/util/FileRotator.java @@ -17,7 +17,7 @@ package com.android.internal.util; import android.os.FileUtils; -import android.util.Slog; +import android.util.Log; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -32,7 +32,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import libcore.io.IoUtils; -import libcore.io.Streams; /** * Utility that rotates files over time, similar to {@code logrotate}. There is @@ -47,6 +46,8 @@ import libcore.io.Streams; * <p> * Users must periodically call {@link #maybeRotate(long)} to perform actual * rotation. Not inherently thread safe. + * + * @hide */ public class FileRotator { private static final String TAG = "FileRotator"; @@ -110,7 +111,7 @@ public class FileRotator { if (!name.startsWith(mPrefix)) continue; if (name.endsWith(SUFFIX_BACKUP)) { - if (LOGD) Slog.d(TAG, "recovering " + name); + if (LOGD) Log.d(TAG, "recovering " + name); final File backupFile = new File(mBasePath, name); final File file = new File( @@ -120,7 +121,7 @@ public class FileRotator { backupFile.renameTo(file); } else if (name.endsWith(SUFFIX_NO_BACKUP)) { - if (LOGD) Slog.d(TAG, "recovering " + name); + if (LOGD) Log.d(TAG, "recovering " + name); final File noBackupFile = new File(mBasePath, name); final File file = new File( @@ -231,7 +232,7 @@ public class FileRotator { * if the write fails. */ private void rewriteSingle(Rewriter rewriter, String name) throws IOException { - if (LOGD) Slog.d(TAG, "rewriting " + name); + if (LOGD) Log.d(TAG, "rewriting " + name); final File file = new File(mBasePath, name); final File backupFile; @@ -291,7 +292,7 @@ public class FileRotator { // read file when it overlaps if (info.startMillis <= matchEndMillis && matchStartMillis <= info.endMillis) { - if (LOGD) Slog.d(TAG, "reading matching " + name); + if (LOGD) Log.d(TAG, "reading matching " + name); final File file = new File(mBasePath, name); readFile(file, reader); @@ -348,7 +349,7 @@ public class FileRotator { if (info.isActive()) { if (info.startMillis <= rotateBefore) { // found active file; rotate if old enough - if (LOGD) Slog.d(TAG, "rotating " + name); + if (LOGD) Log.d(TAG, "rotating " + name); info.endMillis = currentTimeMillis; @@ -358,7 +359,7 @@ public class FileRotator { } } else if (info.endMillis <= deleteBefore) { // found rotated file; delete if old enough - if (LOGD) Slog.d(TAG, "deleting " + name); + if (LOGD) Log.d(TAG, "deleting " + name); final File file = new File(mBasePath, name); file.delete(); @@ -383,7 +384,10 @@ public class FileRotator { writer.write(bos); bos.flush(); } finally { - FileUtils.sync(fos); + try { + fos.getFD().sync(); + } catch (IOException e) { + } IoUtils.closeQuietly(bos); } } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 2cfec4b17f63..adcbb425d1cf 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -242,9 +242,9 @@ cc_library_shared { "audioflinger-aidl-cpp", "av-types-aidl-cpp", "android.hardware.camera.device@3.2", + "libandroid_net", "libandroidicu", "libbpf_android", - "libnetdbpf", "libnetdutils", "libmemtrack", "libandroidfw", diff --git a/core/jni/OWNERS b/core/jni/OWNERS index 832c49801bdc..80e83ad9865c 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -72,6 +72,9 @@ per-file AndroidRuntime.cpp = file:/graphics/java/android/graphics/OWNERS per-file AndroidRuntime.cpp = calin@google.com, ngeoffray@google.com, oth@google.com # Although marked "view" this is mostly graphics stuff per-file android_view_* = file:/graphics/java/android/graphics/OWNERS +# File used for Android Studio layoutlib +per-file LayoutlibLoader.cpp = file:/graphics/java/android/graphics/OWNERS +per-file LayoutlibLoader.cpp = diegoperez@google.com, jgaillard@google.com # Verity per-file com_android_internal_security_Verity* = ebiggers@google.com, victorhsieh@google.com 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/proto/OWNERS b/core/proto/OWNERS index 931ef4478098..43d2439b7f56 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -17,7 +17,8 @@ per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/a per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS # Biometrics -kchyn@google.com +jaggies@google.com +jbolinger@google.com # Launcher hyunyoungs@google.com diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 998ec96f6f23..51e150e28437 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -249,7 +249,8 @@ message IncidentProto { optional android.service.NetworkStatsServiceDumpProto netstats = 3001 [ (section).type = SECTION_DUMPSYS, - (section).args = "netstats --proto" + (section).args = "netstats --proto", + (section).userdebug_and_eng_only = true ]; optional android.providers.settings.SettingsServiceDumpProto settings = 3002 [ diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto index c8cdfddc3985..ba2b6d6bd7e0 100644 --- a/core/proto/android/service/netstats.proto +++ b/core/proto/android/service/netstats.proto @@ -17,15 +17,11 @@ syntax = "proto2"; package android.service; -import "frameworks/base/core/proto/android/privacy.proto"; - option java_multiple_files = true; option java_outer_classname = "NetworkStatsServiceProto"; // Represents dumpsys from NetworkStatsService (netstats). message NetworkStatsServiceDumpProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - repeated NetworkInterfaceProto active_interfaces = 1; repeated NetworkInterfaceProto active_uid_interfaces = 2; @@ -45,8 +41,6 @@ message NetworkStatsServiceDumpProto { // Corresponds to NetworkStatsService.mActiveIfaces/mActiveUidIfaces. message NetworkInterfaceProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - // Name of the network interface (eg: wlan). optional string interface = 1; @@ -55,26 +49,14 @@ message NetworkInterfaceProto { // Corresponds to NetworkIdentitySet. message NetworkIdentitySetProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - repeated NetworkIdentityProto identities = 1; } // Corresponds to NetworkIdentity. message NetworkIdentityProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - // Constants from ConnectivityManager.TYPE_*. optional int32 type = 1; - // Full subscriber ID on eng builds. The IMSI is scrubbed on user & userdebug - // builds to only include the info about the GSM network operator (the info - // that uniquely identifies the subscriber is removed). - optional string subscriber_id = 2 [ (android.privacy).dest = DEST_EXPLICIT ]; - - // Name of the network (eg: MyWifi). - optional string network_id = 3 [ (android.privacy).dest = DEST_EXPLICIT ]; - optional bool roaming = 4; optional bool metered = 5; @@ -86,8 +68,6 @@ message NetworkIdentityProto { // Corresponds to NetworkStatsRecorder. message NetworkStatsRecorderProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional int64 pending_total_bytes = 1; optional NetworkStatsCollectionProto complete_history = 2; @@ -95,15 +75,11 @@ message NetworkStatsRecorderProto { // Corresponds to NetworkStatsCollection. message NetworkStatsCollectionProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - repeated NetworkStatsCollectionStatsProto stats = 1; } // Corresponds to NetworkStatsCollection.mStats. message NetworkStatsCollectionStatsProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional NetworkStatsCollectionKeyProto key = 1; optional NetworkStatsHistoryProto history = 2; @@ -111,8 +87,6 @@ message NetworkStatsCollectionStatsProto { // Corresponds to NetworkStatsCollection.Key. message NetworkStatsCollectionKeyProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional NetworkIdentitySetProto identity = 1; optional int32 uid = 2; @@ -124,8 +98,6 @@ message NetworkStatsCollectionKeyProto { // Corresponds to NetworkStatsHistory. message NetworkStatsHistoryProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - // Duration for this bucket in milliseconds. optional int64 bucket_duration_ms = 1; @@ -134,8 +106,6 @@ message NetworkStatsHistoryProto { // Corresponds to each bucket in NetworkStatsHistory. message NetworkStatsHistoryBucketProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - // Bucket start time in milliseconds since epoch. optional int64 bucket_start_ms = 1; 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/res/res/values/config.xml b/core/res/res/values/config.xml index 2f334989b53a..ba647d7daa28 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -134,9 +134,6 @@ be sent during a change to the audio output device. --> <bool name="config_sendAudioBecomingNoisy">true</bool> - <!-- Whether Hearing Aid profile is supported --> - <bool name="config_hearing_aid_profile_supported">false</bool> - <!-- Flag to disable all transition animations --> <bool name="config_disableTransitionAnimation">false</bool> @@ -404,10 +401,6 @@ <string-array translatable="false" name="config_tether_bluetooth_regexs"> </string-array> - <!-- Max number of Bluetooth tethering connections allowed. If this is - updated config_tether_dhcp_range has to be updated appropriately. --> - <integer translatable="false" name="config_max_pan_devices">5</integer> - <!-- This setting is deprecated, please use com.android.networkstack.tethering.R.array.config_dhcp_range instead. --> <string-array translatable="false" name="config_tether_dhcp_range"> @@ -1820,53 +1813,38 @@ <!-- Integer to set a max latency the accelerometer will batch sensor requests with. --> <integer name="config_flipToScreenOffMaxLatencyMicros">2000000</integer> - <!-- Boolean indicating if current platform supports bluetooth SCO for off call - use cases --> + <!-- Note: This config is deprecated + Boolean indicating if current platform supports bluetooth SCO for off call + use cases + --> <bool name="config_bluetooth_sco_off_call">true</bool> - <!-- Boolean indicating if current platform supports bluetooth wide band - speech --> - <bool name="config_bluetooth_wide_band_speech">true</bool> - - <!-- Boolean indicating if current platform need do one-time bluetooth address - re-validation --> + <!-- Note: This config is deprecated + Boolean indicating if current platform need do one-time bluetooth address + re-validation + --> <bool name="config_bluetooth_address_validation">false</bool> - <!-- Boolean indicating if current platform supports BLE peripheral mode --> - <bool name="config_bluetooth_le_peripheral_mode_supported">false</bool> - - <!-- Boolean indicating if current platform supports HFP inband ringing --> - <bool name="config_bluetooth_hfp_inband_ringing_support">false</bool> - - <!-- Max number of scan filters supported by blutooth controller. 0 if the - device does not support hardware scan filters--> - <integer translatable="false" name="config_bluetooth_max_scan_filters">0</integer> - - <!-- Max number of advertisers supported by bluetooth controller. 0 if the - device does not support multiple advertisement--> - <integer translatable="false" name="config_bluetooth_max_advertisers">0</integer> - - <!-- Idle current for bluetooth controller. 0 by default--> + <!-- Note: This config is deprecated, use BluetoothProperties instead. + Idle current for bluetooth controller. 0 by default + --> <integer translatable="false" name="config_bluetooth_idle_cur_ma">0</integer> - <!-- Rx current for bluetooth controller. 0 by default--> + <!-- Note: This config is deprecated, use BluetoothProperties instead. + Rx current for bluetooth controller. 0 by default + --> <integer translatable="false" name="config_bluetooth_rx_cur_ma">0</integer> - <!-- Tx current for bluetooth controller. 0 by default--> + <!-- Note: This config is deprecated, use BluetoothProperties instead. + Tx current for bluetooth controller. 0 by default + --> <integer translatable="false" name="config_bluetooth_tx_cur_ma">0</integer> - <!-- Operating volatage for bluetooth controller. 0 by default--> + <!-- Note: This config is deprecated, use BluetoothProperties instead. + Operating volatage for bluetooth controller. 0 by default + --> <integer translatable="false" name="config_bluetooth_operating_voltage_mv">0</integer> - <!-- Max number of connected audio devices supported by Bluetooth stack --> - <integer name="config_bluetooth_max_connected_audio_devices">5</integer> - - <!-- Whether supported profiles should be reloaded upon enabling bluetooth --> - <bool name="config_bluetooth_reload_supported_profiles_when_enabled">false</bool> - - <!-- Enabling autoconnect over pan --> - <bool name="config_bluetooth_pan_enable_autoconnect">false</bool> - <!-- The default data-use polling period. --> <integer name="config_datause_polling_period_sec">600</integer> @@ -2007,10 +1985,6 @@ <!-- The name of the package that will be allowed to change its components' label/icon. --> <string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string> - <!-- Enable/disable default bluetooth profiles: - HSP_AG, ObexObjectPush, Audio, NAP --> - <bool name="config_bluetooth_default_profiles">true</bool> - <!-- IP address of the dns server to use if nobody else suggests one --> <string name="config_default_dns_server" translatable="false">8.8.8.8</string> @@ -4141,8 +4115,6 @@ <!-- Component name that should be granted Notification Assistant access --> <string name="config_defaultAssistantAccessComponent" translatable="false">android.ext.services/android.ext.services.notification.Assistant</string> - <bool name="config_supportBluetoothPersistedState">true</bool> - <bool name="config_keepRestrictedProfilesInBackground">true</bool> <!-- Cellular network service package name to bind to by default. --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 2403a605972e..6ef5bd46dce7 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3200,11 +3200,87 @@ <!-- @hide For use by platform and tools only. Developers should not specify this value. --> <public type="string" name="config_defaultRingtoneVibrationSound" id="0x0104003b" /> + <!-- =============================================================== + Resources added in version T of the platform + + NOTE: add <public> elements within a <staging-public-group> like so: + + <staging-public-group type="attr" first-id="0x01ff0000"> + <public name="exampleAttr1" /> + <public name="exampleAttr2" /> + </staging-public-group> + + To add a new <staging-public-group> block, find the id value for the + last <staging-public-group> block defined for thie API level, and + subtract 0x00010000 from it to get to the id of the new block. + + For example, if the block closest to the end of this file has an id + of 0x01ee0000, the id of the new block should be 0x01ed0000 + (0x01ee0000 - 0x00010000 = 0x01ed0000). + =============================================================== --> + <eat-comment /> + + <staging-public-group type="attr" first-id="0x01df0000"> + </staging-public-group> + + <staging-public-group type="id" first-id="0x01de0000"> + </staging-public-group> + + <staging-public-group type="style" first-id="0x0dfd0000"> + </staging-public-group> + + <staging-public-group type="string" first-id="0x0dfc0000"> + </staging-public-group> + + <staging-public-group type="dimen" first-id="0x01db0000"> + </staging-public-group> + + <staging-public-group type="color" first-id="0x01da0000"> + </staging-public-group> + + <staging-public-group type="array" first-id="0x01d90000"> + <!-- @hide @SystemApi --> + <public name="config_optionalIpSecAlgorithms" /> + </staging-public-group> + + <staging-public-group type="drawable" first-id="0x01d80000"> + </staging-public-group> + + <staging-public-group type="layout" first-id="0x01d70000"> + </staging-public-group> + + <staging-public-group type="anim" first-id="0x01d60000"> + </staging-public-group> + + <staging-public-group type="animator" first-id="0x01d50000"> + </staging-public-group> + + <staging-public-group type="interpolator" first-id="0x01d40000"> + </staging-public-group> + + <staging-public-group type="mipmap" first-id="0x01d30000"> + </staging-public-group> + + <staging-public-group type="integer" first-id="0x01d20000"> + </staging-public-group> + + <staging-public-group type="transition" first-id="0x01d10000"> + </staging-public-group> + + <staging-public-group type="raw" first-id="0x01d00000"> + </staging-public-group> + + <staging-public-group type="bool" first-id="0x01cf0000"> + </staging-public-group> + + <staging-public-group type="fraction" first-id="0x01ce0000"> + </staging-public-group> + <!-- =============================================================== DO NOT ADD UN-GROUPED ITEMS HERE Any new items (attrs, styles, ids, etc.) *must* be added in a - public-group block, as the preceding comment explains. + staging-public-group block, as the preceding comment explains. Items added outside of a group may have their value recalculated every time something new is added to this file. =============================================================== --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 4db6499569b3..590fcf46f373 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -278,8 +278,6 @@ <java-symbol type="bool" name="config_flipToScreenOffEnabled" /> <java-symbol type="integer" name="config_flipToScreenOffMaxLatencyMicros" /> <java-symbol type="bool" name="config_bluetooth_sco_off_call" /> - <java-symbol type="bool" name="config_bluetooth_le_peripheral_mode_supported" /> - <java-symbol type="bool" name="config_bluetooth_hfp_inband_ringing_support" /> <java-symbol type="bool" name="config_cellBroadcastAppLinks" /> <java-symbol type="bool" name="config_duplicate_port_omadm_wappush" /> <java-symbol type="bool" name="config_disableTransitionAnimation" /> @@ -343,7 +341,6 @@ <java-symbol type="integer" name="config_timeZoneRulesCheckRetryCount" /> <java-symbol type="bool" name="config_sendAudioBecomingNoisy" /> <java-symbol type="bool" name="config_enableScreenshotChord" /> - <java-symbol type="bool" name="config_bluetooth_default_profiles" /> <java-symbol type="bool" name="config_enableWifiDisplay" /> <java-symbol type="bool" name="config_allowAnimationsInLowPowerMode" /> <java-symbol type="bool" name="config_useDevInputEventForAudioJack" /> @@ -417,9 +414,6 @@ <java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" /> <java-symbol type="integer" name="config_pictureInPictureMaxNumberOfActions" /> <java-symbol type="dimen" name="config_closeToSquareDisplayMaxAspectRatio" /> - <java-symbol type="integer" name="config_bluetooth_max_advertisers" /> - <java-symbol type="integer" name="config_bluetooth_max_scan_filters" /> - <java-symbol type="integer" name="config_bluetooth_max_connected_audio_devices" /> <java-symbol type="integer" name="config_burnInProtectionMinHorizontalOffset" /> <java-symbol type="integer" name="config_burnInProtectionMaxHorizontalOffset" /> <java-symbol type="integer" name="config_burnInProtectionMinVerticalOffset" /> @@ -429,9 +423,6 @@ <java-symbol type="integer" name="config_bluetooth_rx_cur_ma" /> <java-symbol type="integer" name="config_bluetooth_tx_cur_ma" /> <java-symbol type="integer" name="config_bluetooth_operating_voltage_mv" /> - <java-symbol type="bool" name="config_bluetooth_pan_enable_autoconnect" /> - <java-symbol type="bool" name="config_bluetooth_reload_supported_profiles_when_enabled" /> - <java-symbol type="bool" name="config_hearing_aid_profile_supported" /> <java-symbol type="integer" name="config_cursorWindowSize" /> <java-symbol type="integer" name="config_drawLockTimeoutMillis" /> <java-symbol type="integer" name="config_doublePressOnPowerBehavior" /> @@ -450,7 +441,6 @@ <java-symbol type="integer" name="config_wakeUpToLastStateTimeoutMillis" /> <java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAdjust" /> <java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAbsolute" /> - <java-symbol type="integer" name="config_max_pan_devices" /> <java-symbol type="integer" name="config_ntpPollingInterval" /> <java-symbol type="integer" name="config_ntpPollingIntervalShorter" /> <java-symbol type="integer" name="config_ntpRetry" /> @@ -3782,8 +3772,6 @@ <java-symbol type="string" name="config_defaultAssistantAccessComponent" /> - <java-symbol type="bool" name="config_supportBluetoothPersistedState" /> - <java-symbol type="string" name="slices_permission_request" /> <java-symbol type="string" name="screenshot_edit" /> diff --git a/core/tests/bluetoothtests/Android.bp b/core/tests/bluetoothtests/Android.bp deleted file mode 100644 index 68416dd65466..000000000000 --- a/core/tests/bluetoothtests/Android.bp +++ /dev/null @@ -1,24 +0,0 @@ -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 - default_applicable_licenses: ["frameworks_base_license"], -} - -android_test { - name: "BluetoothTests", - // Include all test java files. - srcs: ["src/**/*.java"], - libs: [ - "android.test.runner", - "android.test.base", - ], - static_libs: [ - "junit", - "modules-utils-bytesmatcher", - ], - platform_apis: true, - certificate: "platform", -} diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml deleted file mode 100644 index 75583d5298cb..000000000000 --- a/core/tests/bluetoothtests/AndroidManifest.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.bluetooth.tests" - android:sharedUserId="android.uid.bluetooth" > - - <uses-permission android:name="android.permission.BLUETOOTH" /> - <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> - <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/> - <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> - <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> - <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" /> - <uses-permission android:name="android.permission.BROADCAST_STICKY" /> - <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> - <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" /> - <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> - <uses-permission android:name="android.permission.RECEIVE_SMS" /> - <uses-permission android:name="android.permission.READ_SMS"/> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - <uses-permission android:name="android.permission.WRITE_SETTINGS" /> - <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> - - <application > - <uses-library android:name="android.test.runner" /> - </application> - <instrumentation android:name="android.bluetooth.BluetoothTestRunner" - android:targetPackage="com.android.bluetooth.tests" - android:label="Bluetooth Tests" /> - <instrumentation android:name="android.bluetooth.BluetoothInstrumentation" - android:targetPackage="com.android.bluetooth.tests" - android:label="Bluetooth Test Utils" /> - -</manifest> diff --git a/core/tests/bluetoothtests/AndroidTest.xml b/core/tests/bluetoothtests/AndroidTest.xml deleted file mode 100644 index f93c4ebf5bf6..000000000000 --- a/core/tests/bluetoothtests/AndroidTest.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Config for Bluetooth test cases"> - <option name="test-suite-tag" value="apct"/> - <option name="test-suite-tag" value="apct-instrumentation"/> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="test-file-name" value="BluetoothTests.apk" /> - </target_preparer> - - <option name="test-suite-tag" value="apct"/> - <option name="test-tag" value="BluetoothTests"/> - - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="com.android.bluetooth.tests" /> - <option name="hidden-api-checks" value="false"/> - <option name="runner" value="android.bluetooth.BluetoothTestRunner"/> - </test> -</configuration> diff --git a/core/tests/bluetoothtests/OWNERS b/core/tests/bluetoothtests/OWNERS deleted file mode 100644 index 98bb87716207..000000000000 --- a/core/tests/bluetoothtests/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /core/java/android/bluetooth/OWNERS diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java deleted file mode 100644 index bd55426601fc..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright 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 android.bluetooth; - -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -/** - * Unit test cases for {@link BluetoothCodecConfig}. - * <p> - * To run this test, use: - * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java - */ -public class BluetoothCodecConfigTest extends TestCase { - private static final int[] kCodecTypeArray = new int[] { - BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, - BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, - BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, - BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, - BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID, - }; - private static final int[] kCodecPriorityArray = new int[] { - BluetoothCodecConfig.CODEC_PRIORITY_DISABLED, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST, - }; - private static final int[] kSampleRateArray = new int[] { - BluetoothCodecConfig.SAMPLE_RATE_NONE, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.SAMPLE_RATE_88200, - BluetoothCodecConfig.SAMPLE_RATE_96000, - BluetoothCodecConfig.SAMPLE_RATE_176400, - BluetoothCodecConfig.SAMPLE_RATE_192000, - }; - private static final int[] kBitsPerSampleArray = new int[] { - BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.BITS_PER_SAMPLE_24, - BluetoothCodecConfig.BITS_PER_SAMPLE_32, - }; - private static final int[] kChannelModeArray = new int[] { - BluetoothCodecConfig.CHANNEL_MODE_NONE, - BluetoothCodecConfig.CHANNEL_MODE_MONO, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - }; - private static final long[] kCodecSpecific1Array = new long[] { 1000, 1001, 1002, 1003, }; - private static final long[] kCodecSpecific2Array = new long[] { 2000, 2001, 2002, 2003, }; - private static final long[] kCodecSpecific3Array = new long[] { 3000, 3001, 3002, 3003, }; - private static final long[] kCodecSpecific4Array = new long[] { 4000, 4001, 4002, 4003, }; - - private static final int kTotalConfigs = kCodecTypeArray.length * kCodecPriorityArray.length * - kSampleRateArray.length * kBitsPerSampleArray.length * kChannelModeArray.length * - kCodecSpecific1Array.length * kCodecSpecific2Array.length * kCodecSpecific3Array.length * - kCodecSpecific4Array.length; - - private int selectCodecType(int configId) { - int left = kCodecTypeArray.length; - int right = kTotalConfigs / left; - int index = configId / right; - index = index % kCodecTypeArray.length; - return kCodecTypeArray[index]; - } - - private int selectCodecPriority(int configId) { - int left = kCodecTypeArray.length * kCodecPriorityArray.length; - int right = kTotalConfigs / left; - int index = configId / right; - index = index % kCodecPriorityArray.length; - return kCodecPriorityArray[index]; - } - - private int selectSampleRate(int configId) { - int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length; - int right = kTotalConfigs / left; - int index = configId / right; - index = index % kSampleRateArray.length; - return kSampleRateArray[index]; - } - - private int selectBitsPerSample(int configId) { - int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * - kBitsPerSampleArray.length; - int right = kTotalConfigs / left; - int index = configId / right; - index = index % kBitsPerSampleArray.length; - return kBitsPerSampleArray[index]; - } - - private int selectChannelMode(int configId) { - int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * - kBitsPerSampleArray.length * kChannelModeArray.length; - int right = kTotalConfigs / left; - int index = configId / right; - index = index % kChannelModeArray.length; - return kChannelModeArray[index]; - } - - private long selectCodecSpecific1(int configId) { - int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * - kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length; - int right = kTotalConfigs / left; - int index = configId / right; - index = index % kCodecSpecific1Array.length; - return kCodecSpecific1Array[index]; - } - - private long selectCodecSpecific2(int configId) { - int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * - kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length * - kCodecSpecific2Array.length; - int right = kTotalConfigs / left; - int index = configId / right; - index = index % kCodecSpecific2Array.length; - return kCodecSpecific2Array[index]; - } - - private long selectCodecSpecific3(int configId) { - int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * - kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length * - kCodecSpecific2Array.length * kCodecSpecific3Array.length; - int right = kTotalConfigs / left; - int index = configId / right; - index = index % kCodecSpecific3Array.length; - return kCodecSpecific3Array[index]; - } - - private long selectCodecSpecific4(int configId) { - int left = kCodecTypeArray.length * kCodecPriorityArray.length * kSampleRateArray.length * - kBitsPerSampleArray.length * kChannelModeArray.length * kCodecSpecific1Array.length * - kCodecSpecific2Array.length * kCodecSpecific3Array.length * - kCodecSpecific4Array.length; - int right = kTotalConfigs / left; - int index = configId / right; - index = index % kCodecSpecific4Array.length; - return kCodecSpecific4Array[index]; - } - - @SmallTest - public void testBluetoothCodecConfig_valid_get_methods() { - - for (int config_id = 0; config_id < kTotalConfigs; config_id++) { - int codec_type = selectCodecType(config_id); - int codec_priority = selectCodecPriority(config_id); - int sample_rate = selectSampleRate(config_id); - int bits_per_sample = selectBitsPerSample(config_id); - int channel_mode = selectChannelMode(config_id); - long codec_specific1 = selectCodecSpecific1(config_id); - long codec_specific2 = selectCodecSpecific2(config_id); - long codec_specific3 = selectCodecSpecific3(config_id); - long codec_specific4 = selectCodecSpecific4(config_id); - - BluetoothCodecConfig bcc = buildBluetoothCodecConfig(codec_type, codec_priority, - sample_rate, bits_per_sample, - channel_mode, codec_specific1, - codec_specific2, codec_specific3, - codec_specific4); - - if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) { - assertTrue(bcc.isMandatoryCodec()); - } else { - assertFalse(bcc.isMandatoryCodec()); - } - - if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) { - assertEquals("SBC", bcc.getCodecName()); - } - if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC) { - assertEquals("AAC", bcc.getCodecName()); - } - if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX) { - assertEquals("aptX", bcc.getCodecName()); - } - if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD) { - assertEquals("aptX HD", bcc.getCodecName()); - } - if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC) { - assertEquals("LDAC", bcc.getCodecName()); - } - if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) { - assertEquals("INVALID CODEC", bcc.getCodecName()); - } - - assertEquals(codec_type, bcc.getCodecType()); - assertEquals(codec_priority, bcc.getCodecPriority()); - assertEquals(sample_rate, bcc.getSampleRate()); - assertEquals(bits_per_sample, bcc.getBitsPerSample()); - assertEquals(channel_mode, bcc.getChannelMode()); - assertEquals(codec_specific1, bcc.getCodecSpecific1()); - assertEquals(codec_specific2, bcc.getCodecSpecific2()); - assertEquals(codec_specific3, bcc.getCodecSpecific3()); - assertEquals(codec_specific4, bcc.getCodecSpecific4()); - } - } - - @SmallTest - public void testBluetoothCodecConfig_equals() { - BluetoothCodecConfig bcc1 = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - BluetoothCodecConfig bcc2_same = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - assertTrue(bcc1.equals(bcc2_same)); - - BluetoothCodecConfig bcc3_codec_type = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - assertFalse(bcc1.equals(bcc3_codec_type)); - - BluetoothCodecConfig bcc4_codec_priority = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - assertFalse(bcc1.equals(bcc4_codec_priority)); - - BluetoothCodecConfig bcc5_sample_rate = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - assertFalse(bcc1.equals(bcc5_sample_rate)); - - BluetoothCodecConfig bcc6_bits_per_sample = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_24, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - assertFalse(bcc1.equals(bcc6_bits_per_sample)); - - BluetoothCodecConfig bcc7_channel_mode = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - assertFalse(bcc1.equals(bcc7_channel_mode)); - - BluetoothCodecConfig bcc8_codec_specific1 = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1001, 2000, 3000, 4000); - assertFalse(bcc1.equals(bcc8_codec_specific1)); - - BluetoothCodecConfig bcc9_codec_specific2 = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2002, 3000, 4000); - assertFalse(bcc1.equals(bcc9_codec_specific2)); - - BluetoothCodecConfig bcc10_codec_specific3 = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3003, 4000); - assertFalse(bcc1.equals(bcc10_codec_specific3)); - - BluetoothCodecConfig bcc11_codec_specific4 = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4004); - assertFalse(bcc1.equals(bcc11_codec_specific4)); - } - - private BluetoothCodecConfig buildBluetoothCodecConfig(int sourceCodecType, - int codecPriority, int sampleRate, int bitsPerSample, int channelMode, - long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) { - return new BluetoothCodecConfig.Builder() - .setCodecType(sourceCodecType) - .setCodecPriority(codecPriority) - .setSampleRate(sampleRate) - .setBitsPerSample(bitsPerSample) - .setChannelMode(channelMode) - .setCodecSpecific1(codecSpecific1) - .setCodecSpecific2(codecSpecific2) - .setCodecSpecific3(codecSpecific3) - .setCodecSpecific4(codecSpecific4) - .build(); - - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java deleted file mode 100644 index 1cb2dcae865c..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Copyright 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 android.bluetooth; - -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Unit test cases for {@link BluetoothCodecStatus}. - * <p> - * To run this test, use: - * runtest --path core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java - */ -public class BluetoothCodecStatusTest extends TestCase { - - // Codec configs: A and B are same; C is different - private static final BluetoothCodecConfig config_A = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig config_B = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig config_C = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - // Local capabilities: A and B are same; C is different - private static final BluetoothCodecConfig local_capability1_A = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability1_B = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability1_C = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - - private static final BluetoothCodecConfig local_capability2_A = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability2_B = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability2_C = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability3_A = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability3_B = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability3_C = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability4_A = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_24, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability4_B = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_24, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability4_C = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000, - BluetoothCodecConfig.BITS_PER_SAMPLE_24, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability5_A = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000 | - BluetoothCodecConfig.SAMPLE_RATE_88200 | - BluetoothCodecConfig.SAMPLE_RATE_96000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16 | - BluetoothCodecConfig.BITS_PER_SAMPLE_24 | - BluetoothCodecConfig.BITS_PER_SAMPLE_32, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability5_B = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000 | - BluetoothCodecConfig.SAMPLE_RATE_88200 | - BluetoothCodecConfig.SAMPLE_RATE_96000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16 | - BluetoothCodecConfig.BITS_PER_SAMPLE_24 | - BluetoothCodecConfig.BITS_PER_SAMPLE_32, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig local_capability5_C = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000 | - BluetoothCodecConfig.SAMPLE_RATE_88200 | - BluetoothCodecConfig.SAMPLE_RATE_96000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16 | - BluetoothCodecConfig.BITS_PER_SAMPLE_24 | - BluetoothCodecConfig.BITS_PER_SAMPLE_32, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - - // Selectable capabilities: A and B are same; C is different - private static final BluetoothCodecConfig selectable_capability1_A = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability1_B = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability1_C = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability2_A = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability2_B = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability2_C = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability3_A = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability3_B = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability3_C = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_16, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability4_A = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_24, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability4_B = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_24, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability4_C = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100, - BluetoothCodecConfig.BITS_PER_SAMPLE_24, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability5_A = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000 | - BluetoothCodecConfig.SAMPLE_RATE_88200 | - BluetoothCodecConfig.SAMPLE_RATE_96000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16 | - BluetoothCodecConfig.BITS_PER_SAMPLE_24 | - BluetoothCodecConfig.BITS_PER_SAMPLE_32, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability5_B = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000 | - BluetoothCodecConfig.SAMPLE_RATE_88200 | - BluetoothCodecConfig.SAMPLE_RATE_96000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16 | - BluetoothCodecConfig.BITS_PER_SAMPLE_24 | - BluetoothCodecConfig.BITS_PER_SAMPLE_32, - BluetoothCodecConfig.CHANNEL_MODE_STEREO | - BluetoothCodecConfig.CHANNEL_MODE_MONO, - 1000, 2000, 3000, 4000); - - private static final BluetoothCodecConfig selectable_capability5_C = - buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, - BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, - BluetoothCodecConfig.SAMPLE_RATE_44100 | - BluetoothCodecConfig.SAMPLE_RATE_48000 | - BluetoothCodecConfig.SAMPLE_RATE_88200 | - BluetoothCodecConfig.SAMPLE_RATE_96000, - BluetoothCodecConfig.BITS_PER_SAMPLE_16 | - BluetoothCodecConfig.BITS_PER_SAMPLE_24 | - BluetoothCodecConfig.BITS_PER_SAMPLE_32, - BluetoothCodecConfig.CHANNEL_MODE_STEREO, - 1000, 2000, 3000, 4000); - - private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_A = - new ArrayList() {{ - add(local_capability1_A); - add(local_capability2_A); - add(local_capability3_A); - add(local_capability4_A); - add(local_capability5_A); - }}; - - private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_B = - new ArrayList() {{ - add(local_capability1_B); - add(local_capability2_B); - add(local_capability3_B); - add(local_capability4_B); - add(local_capability5_B); - }}; - - private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_B_REORDERED = - new ArrayList() {{ - add(local_capability5_B); - add(local_capability4_B); - add(local_capability2_B); - add(local_capability3_B); - add(local_capability1_B); - }}; - - private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_C = - new ArrayList() {{ - add(local_capability1_C); - add(local_capability2_C); - add(local_capability3_C); - add(local_capability4_C); - add(local_capability5_C); - }}; - - private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_A = - new ArrayList() {{ - add(selectable_capability1_A); - add(selectable_capability2_A); - add(selectable_capability3_A); - add(selectable_capability4_A); - add(selectable_capability5_A); - }}; - - private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_B = - new ArrayList() {{ - add(selectable_capability1_B); - add(selectable_capability2_B); - add(selectable_capability3_B); - add(selectable_capability4_B); - add(selectable_capability5_B); - }}; - - private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_B_REORDERED = - new ArrayList() {{ - add(selectable_capability5_B); - add(selectable_capability4_B); - add(selectable_capability2_B); - add(selectable_capability3_B); - add(selectable_capability1_B); - }}; - - private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_C = - new ArrayList() {{ - add(selectable_capability1_C); - add(selectable_capability2_C); - add(selectable_capability3_C); - add(selectable_capability4_C); - add(selectable_capability5_C); - }}; - - private static final BluetoothCodecStatus bcs_A = - new BluetoothCodecStatus(config_A, LOCAL_CAPABILITY_A, SELECTABLE_CAPABILITY_A); - private static final BluetoothCodecStatus bcs_B = - new BluetoothCodecStatus(config_B, LOCAL_CAPABILITY_B, SELECTABLE_CAPABILITY_B); - private static final BluetoothCodecStatus bcs_B_reordered = - new BluetoothCodecStatus(config_B, LOCAL_CAPABILITY_B_REORDERED, - SELECTABLE_CAPABILITY_B_REORDERED); - private static final BluetoothCodecStatus bcs_C = - new BluetoothCodecStatus(config_C, LOCAL_CAPABILITY_C, SELECTABLE_CAPABILITY_C); - - @SmallTest - public void testBluetoothCodecStatus_get_methods() { - - assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_A)); - assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_B)); - assertFalse(Objects.equals(bcs_A.getCodecConfig(), config_C)); - - assertTrue(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_A)); - assertTrue(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_B)); - assertFalse(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_C)); - - assertTrue(bcs_A.getCodecsSelectableCapabilities() - .equals(SELECTABLE_CAPABILITY_A)); - assertTrue(bcs_A.getCodecsSelectableCapabilities() - .equals(SELECTABLE_CAPABILITY_B)); - assertFalse(bcs_A.getCodecsSelectableCapabilities() - .equals(SELECTABLE_CAPABILITY_C)); - } - - @SmallTest - public void testBluetoothCodecStatus_equals() { - assertTrue(bcs_A.equals(bcs_B)); - assertTrue(bcs_B.equals(bcs_A)); - assertTrue(bcs_A.equals(bcs_B_reordered)); - assertTrue(bcs_B_reordered.equals(bcs_A)); - assertFalse(bcs_A.equals(bcs_C)); - assertFalse(bcs_C.equals(bcs_A)); - } - - private static BluetoothCodecConfig buildBluetoothCodecConfig(int sourceCodecType, - int codecPriority, int sampleRate, int bitsPerSample, int channelMode, - long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) { - return new BluetoothCodecConfig.Builder() - .setCodecType(sourceCodecType) - .setCodecPriority(codecPriority) - .setSampleRate(sampleRate) - .setBitsPerSample(bitsPerSample) - .setChannelMode(channelMode) - .setCodecSpecific1(codecSpecific1) - .setCodecSpecific2(codecSpecific2) - .setCodecSpecific3(codecSpecific3) - .setCodecSpecific4(codecSpecific4) - .build(); - - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java deleted file mode 100644 index 37b2a50ed670..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.bluetooth; - -import android.app.Activity; -import android.app.Instrumentation; -import android.content.Context; -import android.os.Bundle; - -import junit.framework.Assert; - -import java.util.Set; - -public class BluetoothInstrumentation extends Instrumentation { - - private BluetoothTestUtils mUtils = null; - private BluetoothAdapter mAdapter = null; - private Bundle mArgs = null; - private Bundle mSuccessResult = null; - - private BluetoothTestUtils getBluetoothTestUtils() { - if (mUtils == null) { - mUtils = new BluetoothTestUtils(getContext(), - BluetoothInstrumentation.class.getSimpleName()); - } - return mUtils; - } - - private BluetoothAdapter getBluetoothAdapter() { - if (mAdapter == null) { - mAdapter = ((BluetoothManager)getContext().getSystemService( - Context.BLUETOOTH_SERVICE)).getAdapter(); - } - return mAdapter; - } - - @Override - public void onCreate(Bundle arguments) { - super.onCreate(arguments); - mArgs = arguments; - // create the default result response, but only use it in success code path - mSuccessResult = new Bundle(); - mSuccessResult.putString("result", "SUCCESS"); - start(); - } - - @Override - public void onStart() { - String command = mArgs.getString("command"); - if ("enable".equals(command)) { - enable(); - } else if ("disable".equals(command)) { - disable(); - } else if ("unpairAll".equals(command)) { - unpairAll(); - } else if ("getName".equals(command)) { - getName(); - } else if ("getAddress".equals(command)) { - getAddress(); - } else if ("getBondedDevices".equals(command)) { - getBondedDevices(); - } else { - finish(null); - } - } - - public void enable() { - getBluetoothTestUtils().enable(getBluetoothAdapter()); - finish(mSuccessResult); - } - - public void disable() { - getBluetoothTestUtils().disable(getBluetoothAdapter()); - finish(mSuccessResult); - } - - public void unpairAll() { - getBluetoothTestUtils().unpairAll(getBluetoothAdapter()); - finish(mSuccessResult); - } - - public void getName() { - String name = getBluetoothAdapter().getName(); - mSuccessResult.putString("name", name); - finish(mSuccessResult); - } - - public void getAddress() { - String name = getBluetoothAdapter().getAddress(); - mSuccessResult.putString("address", name); - finish(mSuccessResult); - } - - public void getBondedDevices() { - Set<BluetoothDevice> devices = getBluetoothAdapter().getBondedDevices(); - int i = 0; - for (BluetoothDevice device : devices) { - mSuccessResult.putString(String.format("device-%02d", i), device.getAddress()); - i++; - } - finish(mSuccessResult); - } - - public void finish(Bundle result) { - if (result == null) { - result = new Bundle(); - } - finish(Activity.RESULT_OK, result); - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java deleted file mode 100644 index c3d707cd7596..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.bluetooth; - -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -/** - * Unit test cases for {@link BluetoothLeAudioCodecConfig}. - */ -public class BluetoothLeAudioCodecConfigTest extends TestCase { - private int[] mCodecTypeArray = new int[] { - BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3, - BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID, - }; - - @SmallTest - public void testBluetoothLeAudioCodecConfig_valid_get_methods() { - - for (int codecIdx = 0; codecIdx < mCodecTypeArray.length; codecIdx++) { - int codecType = mCodecTypeArray[codecIdx]; - - BluetoothLeAudioCodecConfig leAudioCodecConfig = - buildBluetoothLeAudioCodecConfig(codecType); - - if (codecType == BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3) { - assertEquals("LC3", leAudioCodecConfig.getCodecName()); - } - if (codecType == BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID) { - assertEquals("INVALID CODEC", leAudioCodecConfig.getCodecName()); - } - - assertEquals(codecType, leAudioCodecConfig.getCodecType()); - } - } - - private BluetoothLeAudioCodecConfig buildBluetoothLeAudioCodecConfig(int sourceCodecType) { - return new BluetoothLeAudioCodecConfig.Builder() - .setCodecType(sourceCodecType) - .build(); - - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java deleted file mode 100644 index 33e9dd7fabc6..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2010 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.bluetooth; - -import android.content.Context; -import android.test.InstrumentationTestCase; - -/** - * Instrumentation test case for stress test involving rebooting the device. - * <p> - * This test case tests that bluetooth is enabled after a device reboot. Because - * the device will reboot, the instrumentation must be driven by a script on the - * host side. - */ -public class BluetoothRebootStressTest extends InstrumentationTestCase { - private static final String TAG = "BluetoothRebootStressTest"; - private static final String OUTPUT_FILE = "BluetoothRebootStressTestOutput.txt"; - - private BluetoothTestUtils mTestUtils; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - Context context = getInstrumentation().getTargetContext(); - mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - - mTestUtils.close(); - } - - /** - * Test method used to start the test by turning bluetooth on. - */ - public void testStart() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - mTestUtils.enable(adapter); - } - - /** - * Test method used in the middle iterations of the test to check if - * bluetooth is on. Does not toggle bluetooth after the check. Assumes that - * bluetooth has been turned on by {@code #testStart()} - */ - public void testMiddleNoToggle() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - - assertTrue(adapter.isEnabled()); - } - - /** - * Test method used in the middle iterations of the test to check if - * bluetooth is on. Toggles bluetooth after the check. Assumes that - * bluetooth has been turned on by {@code #testStart()} - */ - public void testMiddleToggle() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - - assertTrue(adapter.isEnabled()); - - mTestUtils.disable(adapter); - mTestUtils.enable(adapter); - } - - /** - * Test method used in the stop the test by turning bluetooth off. Assumes - * that bluetooth has been turned on by {@code #testStart()} - */ - public void testStop() { - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - - assertTrue(adapter.isEnabled()); - - mTestUtils.disable(adapter); - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java deleted file mode 100644 index 89dbe3f75b56..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2010 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.bluetooth; - -import android.content.Context; -import android.test.InstrumentationTestCase; - -/** - * Stress test suite for Bluetooth related functions. - * - * Includes tests for enabling/disabling bluetooth, enabling/disabling discoverable mode, - * starting/stopping scans, connecting/disconnecting to HFP, A2DP, HID, PAN profiles, and verifying - * that remote connections/disconnections occur for the PAN profile. - * <p> - * This test suite uses {@link android.bluetooth.BluetoothTestRunner} to for parameters such as the - * number of iterations and the addresses of remote Bluetooth devices. - */ -public class BluetoothStressTest extends InstrumentationTestCase { - private static final String TAG = "BluetoothStressTest"; - private static final String OUTPUT_FILE = "BluetoothStressTestOutput.txt"; - /** The amount of time to sleep between issuing start/stop SCO in ms. */ - private static final long SCO_SLEEP_TIME = 2 * 1000; - - private BluetoothAdapter mAdapter; - private BluetoothTestUtils mTestUtils; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - Context context = getInstrumentation().getTargetContext(); - mAdapter = BluetoothAdapter.getDefaultAdapter(); - mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE); - - // Start all tests in a disabled state. - if (mAdapter.isEnabled()) { - mTestUtils.disable(mAdapter); - } - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - mTestUtils.close(); - } - - /** - * Stress test for enabling and disabling Bluetooth. - */ - public void testEnable() { - int iterations = BluetoothTestRunner.sEnableIterations; - if (iterations == 0) { - return; - } - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("enable iteration " + (i + 1) + " of " + iterations); - mTestUtils.enable(mAdapter); - mTestUtils.disable(mAdapter); - } - } - - /** - * Stress test for putting the device in and taking the device out of discoverable mode. - */ - public void testDiscoverable() { - int iterations = BluetoothTestRunner.sDiscoverableIterations; - if (iterations == 0) { - return; - } - - mTestUtils.enable(mAdapter); - mTestUtils.undiscoverable(mAdapter); - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("discoverable iteration " + (i + 1) + " of " + iterations); - mTestUtils.discoverable(mAdapter); - mTestUtils.undiscoverable(mAdapter); - } - } - - /** - * Stress test for starting and stopping Bluetooth scans. - */ - public void testScan() { - int iterations = BluetoothTestRunner.sScanIterations; - if (iterations == 0) { - return; - } - - mTestUtils.enable(mAdapter); - mTestUtils.stopScan(mAdapter); - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("scan iteration " + (i + 1) + " of " + iterations); - mTestUtils.startScan(mAdapter); - mTestUtils.stopScan(mAdapter); - } - } - - /** - * Stress test for enabling and disabling the PAN NAP profile. - */ - public void testEnablePan() { - int iterations = BluetoothTestRunner.sEnablePanIterations; - if (iterations == 0) { - return; - } - - mTestUtils.enable(mAdapter); - mTestUtils.disablePan(mAdapter); - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("testEnablePan iteration " + (i + 1) + " of " - + iterations); - mTestUtils.enablePan(mAdapter); - mTestUtils.disablePan(mAdapter); - } - } - - /** - * Stress test for pairing and unpairing with a remote device. - * <p> - * In this test, the local device initiates pairing with a remote device, and then unpairs with - * the device after the pairing has successfully completed. - */ - public void testPair() { - int iterations = BluetoothTestRunner.sPairIterations; - if (iterations == 0) { - return; - } - - BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.enable(mAdapter); - mTestUtils.unpair(mAdapter, device); - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("pair iteration " + (i + 1) + " of " + iterations); - mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, - BluetoothTestRunner.sDevicePairPin); - mTestUtils.unpair(mAdapter, device); - } - } - - /** - * Stress test for accepting a pairing request and unpairing with a remote device. - * <p> - * In this test, the local device waits for a pairing request from a remote device. It accepts - * the request and then unpairs after the paring has successfully completed. - */ - public void testAcceptPair() { - int iterations = BluetoothTestRunner.sPairIterations; - if (iterations == 0) { - return; - } - BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.enable(mAdapter); - mTestUtils.unpair(mAdapter, device); - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("acceptPair iteration " + (i + 1) + " of " + iterations); - mTestUtils.acceptPair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, - BluetoothTestRunner.sDevicePairPin); - mTestUtils.unpair(mAdapter, device); - } - } - - /** - * Stress test for connecting and disconnecting with an A2DP source. - * <p> - * In this test, the local device plays the role of an A2DP sink, and initiates connections and - * disconnections with an A2DP source. - */ - public void testConnectA2dp() { - int iterations = BluetoothTestRunner.sConnectA2dpIterations; - if (iterations == 0) { - return; - } - - BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.enable(mAdapter); - mTestUtils.unpair(mAdapter, device); - mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, - BluetoothTestRunner.sDevicePairPin); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.A2DP, null); - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("connectA2dp iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.A2DP, - String.format("connectA2dp(device=%s)", device)); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.A2DP, - String.format("disconnectA2dp(device=%s)", device)); - } - - mTestUtils.unpair(mAdapter, device); - } - - /** - * Stress test for connecting and disconnecting the HFP with a hands free device. - * <p> - * In this test, the local device plays the role of an HFP audio gateway, and initiates - * connections and disconnections with a hands free device. - */ - public void testConnectHeadset() { - int iterations = BluetoothTestRunner.sConnectHeadsetIterations; - if (iterations == 0) { - return; - } - - BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.enable(mAdapter); - mTestUtils.unpair(mAdapter, device); - mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, - BluetoothTestRunner.sDevicePairPin); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null); - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("connectHeadset iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HEADSET, - String.format("connectHeadset(device=%s)", device)); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, - String.format("disconnectHeadset(device=%s)", device)); - } - - mTestUtils.unpair(mAdapter, device); - } - - /** - * Stress test for connecting and disconnecting with a HID device. - * <p> - * In this test, the local device plays the role of a HID host, and initiates connections and - * disconnections with a HID device. - */ - public void testConnectInput() { - int iterations = BluetoothTestRunner.sConnectInputIterations; - if (iterations == 0) { - return; - } - - BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.enable(mAdapter); - mTestUtils.unpair(mAdapter, device); - mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, - BluetoothTestRunner.sDevicePairPin); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST, null); - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HID_HOST, - String.format("connectInput(device=%s)", device)); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HID_HOST, - String.format("disconnectInput(device=%s)", device)); - } - - mTestUtils.unpair(mAdapter, device); - } - - /** - * Stress test for connecting and disconnecting with a PAN NAP. - * <p> - * In this test, the local device plays the role of a PANU, and initiates connections and - * disconnections with a NAP. - */ - public void testConnectPan() { - int iterations = BluetoothTestRunner.sConnectPanIterations; - if (iterations == 0) { - return; - } - - BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.enable(mAdapter); - mTestUtils.unpair(mAdapter, device); - mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, - BluetoothTestRunner.sDevicePairPin); - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("connectPan iteration " + (i + 1) + " of " + iterations); - mTestUtils.connectPan(mAdapter, device); - mTestUtils.disconnectPan(mAdapter, device); - } - - mTestUtils.unpair(mAdapter, device); - } - - /** - * Stress test for verifying a PANU connecting and disconnecting with the device. - * <p> - * In this test, the local device plays the role of a NAP which a remote PANU connects and - * disconnects from. - */ - public void testIncomingPanConnection() { - int iterations = BluetoothTestRunner.sConnectPanIterations; - if (iterations == 0) { - return; - } - - BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.enable(mAdapter); - mTestUtils.disablePan(mAdapter); - mTestUtils.enablePan(mAdapter); - mTestUtils.unpair(mAdapter, device); - mTestUtils.acceptPair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, - BluetoothTestRunner.sDevicePairPin); - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("incomingPanConnection iteration " + (i + 1) + " of " - + iterations); - mTestUtils.incomingPanConnection(mAdapter, device); - mTestUtils.incomingPanDisconnection(mAdapter, device); - } - - mTestUtils.unpair(mAdapter, device); - mTestUtils.disablePan(mAdapter); - } - - /** - * Stress test for verifying that AudioManager can open and close SCO connections. - * <p> - * In this test, a HSP connection is opened with an external headset and the SCO connection is - * repeatibly opened and closed. - */ - public void testStartStopSco() { - int iterations = BluetoothTestRunner.sStartStopScoIterations; - if (iterations == 0) { - return; - } - - BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.enable(mAdapter); - mTestUtils.unpair(mAdapter, device); - mTestUtils.pair(mAdapter, device, BluetoothTestRunner.sDevicePairPasskey, - BluetoothTestRunner.sDevicePairPin); - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null); - mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.HEADSET, null); - mTestUtils.stopSco(mAdapter, device); - - for (int i = 0; i < iterations; i++) { - mTestUtils.writeOutput("startStopSco iteration " + (i + 1) + " of " + iterations); - mTestUtils.startSco(mAdapter, device); - sleep(SCO_SLEEP_TIME); - mTestUtils.stopSco(mAdapter, device); - sleep(SCO_SLEEP_TIME); - } - - mTestUtils.disconnectProfile(mAdapter, device, BluetoothProfile.HEADSET, null); - mTestUtils.unpair(mAdapter, device); - } - - /* Make sure there is at least 1 unread message in the last week on remote device */ - public void testMceSetMessageStatus() { - int iterations = BluetoothTestRunner.sMceSetMessageStatusIterations; - if (iterations == 0) { - return; - } - - BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress); - mTestUtils.enable(mAdapter); - mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.MAP_CLIENT, null); - mTestUtils.mceGetUnreadMessage(mAdapter, device); - - for (int i = 0; i < iterations; i++) { - mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.READ); - mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.UNREAD); - } - - /** - * It is hard to find device to support set undeleted status, so just - * set deleted in 1 iteration - **/ - mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.DELETED); - } - - private void sleep(long time) { - try { - Thread.sleep(time); - } catch (InterruptedException e) { - } - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java deleted file mode 100644 index d19c2c3e7e24..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2010 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.bluetooth; - -import junit.framework.TestSuite; - -import android.os.Bundle; -import android.test.InstrumentationTestRunner; -import android.test.InstrumentationTestSuite; -import android.util.Log; - -/** - * Instrumentation test runner for Bluetooth tests. - * <p> - * To run: - * <pre> - * {@code - * adb shell am instrument \ - * [-e enable_iterations <iterations>] \ - * [-e discoverable_iterations <iterations>] \ - * [-e scan_iterations <iterations>] \ - * [-e enable_pan_iterations <iterations>] \ - * [-e pair_iterations <iterations>] \ - * [-e connect_a2dp_iterations <iterations>] \ - * [-e connect_headset_iterations <iterations>] \ - * [-e connect_input_iterations <iterations>] \ - * [-e connect_pan_iterations <iterations>] \ - * [-e start_stop_sco_iterations <iterations>] \ - * [-e mce_set_message_status_iterations <iterations>] \ - * [-e pair_address <address>] \ - * [-e headset_address <address>] \ - * [-e a2dp_address <address>] \ - * [-e input_address <address>] \ - * [-e pan_address <address>] \ - * [-e pair_pin <pin>] \ - * [-e pair_passkey <passkey>] \ - * -w com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner - * } - * </pre> - */ -public class BluetoothTestRunner extends InstrumentationTestRunner { - private static final String TAG = "BluetoothTestRunner"; - - public static int sEnableIterations = 100; - public static int sDiscoverableIterations = 1000; - public static int sScanIterations = 1000; - public static int sEnablePanIterations = 1000; - public static int sPairIterations = 100; - public static int sConnectHeadsetIterations = 100; - public static int sConnectA2dpIterations = 100; - public static int sConnectInputIterations = 100; - public static int sConnectPanIterations = 100; - public static int sStartStopScoIterations = 100; - public static int sMceSetMessageStatusIterations = 100; - - public static String sDeviceAddress = ""; - public static byte[] sDevicePairPin = {'1', '2', '3', '4'}; - public static int sDevicePairPasskey = 123456; - - @Override - public TestSuite getAllTests() { - TestSuite suite = new InstrumentationTestSuite(this); - suite.addTestSuite(BluetoothStressTest.class); - return suite; - } - - @Override - public ClassLoader getLoader() { - return BluetoothTestRunner.class.getClassLoader(); - } - - @Override - public void onCreate(Bundle arguments) { - String val = arguments.getString("enable_iterations"); - if (val != null) { - try { - sEnableIterations = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - val = arguments.getString("discoverable_iterations"); - if (val != null) { - try { - sDiscoverableIterations = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - val = arguments.getString("scan_iterations"); - if (val != null) { - try { - sScanIterations = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - val = arguments.getString("enable_pan_iterations"); - if (val != null) { - try { - sEnablePanIterations = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - val = arguments.getString("pair_iterations"); - if (val != null) { - try { - sPairIterations = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - val = arguments.getString("connect_a2dp_iterations"); - if (val != null) { - try { - sConnectA2dpIterations = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - val = arguments.getString("connect_headset_iterations"); - if (val != null) { - try { - sConnectHeadsetIterations = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - val = arguments.getString("connect_input_iterations"); - if (val != null) { - try { - sConnectInputIterations = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - val = arguments.getString("connect_pan_iterations"); - if (val != null) { - try { - sConnectPanIterations = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - val = arguments.getString("start_stop_sco_iterations"); - if (val != null) { - try { - sStartStopScoIterations = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - val = arguments.getString("mce_set_message_status_iterations"); - if (val != null) { - try { - sMceSetMessageStatusIterations = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - val = arguments.getString("device_address"); - if (val != null) { - sDeviceAddress = val; - } - - val = arguments.getString("device_pair_pin"); - if (val != null) { - byte[] pin = BluetoothDevice.convertPinToBytes(val); - if (pin != null) { - sDevicePairPin = pin; - } - } - - val = arguments.getString("device_pair_passkey"); - if (val != null) { - try { - sDevicePairPasskey = Integer.parseInt(val); - } catch (NumberFormatException e) { - // Invalid argument, fall back to default value - } - } - - Log.i(TAG, String.format("enable_iterations=%d", sEnableIterations)); - Log.i(TAG, String.format("discoverable_iterations=%d", sDiscoverableIterations)); - Log.i(TAG, String.format("scan_iterations=%d", sScanIterations)); - Log.i(TAG, String.format("pair_iterations=%d", sPairIterations)); - Log.i(TAG, String.format("connect_a2dp_iterations=%d", sConnectA2dpIterations)); - Log.i(TAG, String.format("connect_headset_iterations=%d", sConnectHeadsetIterations)); - Log.i(TAG, String.format("connect_input_iterations=%d", sConnectInputIterations)); - Log.i(TAG, String.format("connect_pan_iterations=%d", sConnectPanIterations)); - Log.i(TAG, String.format("start_stop_sco_iterations=%d", sStartStopScoIterations)); - Log.i(TAG, String.format("device_address=%s", sDeviceAddress)); - Log.i(TAG, String.format("device_pair_pin=%s", new String(sDevicePairPin))); - Log.i(TAG, String.format("device_pair_passkey=%d", sDevicePairPasskey)); - - // Call onCreate last since we want to set the static variables first. - super.onCreate(arguments); - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java deleted file mode 100644 index 409025bc670d..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java +++ /dev/null @@ -1,1649 +0,0 @@ -/* - * Copyright (C) 2010 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.bluetooth; - -import android.bluetooth.BluetoothPan; -import android.bluetooth.BluetoothProfile; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.media.AudioManager; -import android.os.Environment; -import android.util.Log; - -import junit.framework.Assert; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - -public class BluetoothTestUtils extends Assert { - - /** Timeout for enable/disable in ms. */ - private static final int ENABLE_DISABLE_TIMEOUT = 20000; - /** Timeout for discoverable/undiscoverable in ms. */ - private static final int DISCOVERABLE_UNDISCOVERABLE_TIMEOUT = 5000; - /** Timeout for starting/stopping a scan in ms. */ - private static final int START_STOP_SCAN_TIMEOUT = 5000; - /** Timeout for pair/unpair in ms. */ - private static final int PAIR_UNPAIR_TIMEOUT = 20000; - /** Timeout for connecting/disconnecting a profile in ms. */ - private static final int CONNECT_DISCONNECT_PROFILE_TIMEOUT = 20000; - /** Timeout to start or stop a SCO channel in ms. */ - private static final int START_STOP_SCO_TIMEOUT = 10000; - /** Timeout to connect a profile proxy in ms. */ - private static final int CONNECT_PROXY_TIMEOUT = 5000; - /** Time between polls in ms. */ - private static final int POLL_TIME = 100; - /** Timeout to get map message in ms. */ - private static final int GET_UNREAD_MESSAGE_TIMEOUT = 10000; - /** Timeout to set map message status in ms. */ - private static final int SET_MESSAGE_STATUS_TIMEOUT = 2000; - - private abstract class FlagReceiver extends BroadcastReceiver { - private int mExpectedFlags = 0; - private int mFiredFlags = 0; - private long mCompletedTime = -1; - - public FlagReceiver(int expectedFlags) { - mExpectedFlags = expectedFlags; - } - - public int getFiredFlags() { - synchronized (this) { - return mFiredFlags; - } - } - - public long getCompletedTime() { - synchronized (this) { - return mCompletedTime; - } - } - - protected void setFiredFlag(int flag) { - synchronized (this) { - mFiredFlags |= flag; - if ((mFiredFlags & mExpectedFlags) == mExpectedFlags) { - mCompletedTime = System.currentTimeMillis(); - } - } - } - } - - private class BluetoothReceiver extends FlagReceiver { - private static final int DISCOVERY_STARTED_FLAG = 1; - private static final int DISCOVERY_FINISHED_FLAG = 1 << 1; - private static final int SCAN_MODE_NONE_FLAG = 1 << 2; - private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3; - private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4; - private static final int STATE_OFF_FLAG = 1 << 5; - private static final int STATE_TURNING_ON_FLAG = 1 << 6; - private static final int STATE_ON_FLAG = 1 << 7; - private static final int STATE_TURNING_OFF_FLAG = 1 << 8; - private static final int STATE_GET_MESSAGE_FINISHED_FLAG = 1 << 9; - private static final int STATE_SET_MESSAGE_STATUS_FINISHED_FLAG = 1 << 10; - - public BluetoothReceiver(int expectedFlags) { - super(expectedFlags); - } - - @Override - public void onReceive(Context context, Intent intent) { - if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) { - setFiredFlag(DISCOVERY_STARTED_FLAG); - } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) { - setFiredFlag(DISCOVERY_FINISHED_FLAG); - } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) { - int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, -1); - assertNotSame(-1, mode); - switch (mode) { - case BluetoothAdapter.SCAN_MODE_NONE: - setFiredFlag(SCAN_MODE_NONE_FLAG); - break; - case BluetoothAdapter.SCAN_MODE_CONNECTABLE: - setFiredFlag(SCAN_MODE_CONNECTABLE_FLAG); - break; - case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: - setFiredFlag(SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG); - break; - } - } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) { - int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); - assertNotSame(-1, state); - switch (state) { - case BluetoothAdapter.STATE_OFF: - setFiredFlag(STATE_OFF_FLAG); - break; - case BluetoothAdapter.STATE_TURNING_ON: - setFiredFlag(STATE_TURNING_ON_FLAG); - break; - case BluetoothAdapter.STATE_ON: - setFiredFlag(STATE_ON_FLAG); - break; - case BluetoothAdapter.STATE_TURNING_OFF: - setFiredFlag(STATE_TURNING_OFF_FLAG); - break; - } - } - } - } - - private class PairReceiver extends FlagReceiver { - private static final int STATE_BONDED_FLAG = 1; - private static final int STATE_BONDING_FLAG = 1 << 1; - private static final int STATE_NONE_FLAG = 1 << 2; - - private BluetoothDevice mDevice; - private int mPasskey; - private byte[] mPin; - - public PairReceiver(BluetoothDevice device, int passkey, byte[] pin, int expectedFlags) { - super(expectedFlags); - - mDevice = device; - mPasskey = passkey; - mPin = pin; - } - - @Override - public void onReceive(Context context, Intent intent) { - if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) { - return; - } - - if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())) { - int varient = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, -1); - assertNotSame(-1, varient); - switch (varient) { - case BluetoothDevice.PAIRING_VARIANT_PIN: - case BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS: - mDevice.setPin(mPin); - break; - case BluetoothDevice.PAIRING_VARIANT_PASSKEY: - break; - case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION: - case BluetoothDevice.PAIRING_VARIANT_CONSENT: - mDevice.setPairingConfirmation(true); - break; - case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT: - break; - } - } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) { - int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1); - assertNotSame(-1, state); - switch (state) { - case BluetoothDevice.BOND_NONE: - setFiredFlag(STATE_NONE_FLAG); - break; - case BluetoothDevice.BOND_BONDING: - setFiredFlag(STATE_BONDING_FLAG); - break; - case BluetoothDevice.BOND_BONDED: - setFiredFlag(STATE_BONDED_FLAG); - break; - } - } - } - } - - private class ConnectProfileReceiver extends FlagReceiver { - private static final int STATE_DISCONNECTED_FLAG = 1; - private static final int STATE_CONNECTING_FLAG = 1 << 1; - private static final int STATE_CONNECTED_FLAG = 1 << 2; - private static final int STATE_DISCONNECTING_FLAG = 1 << 3; - - private BluetoothDevice mDevice; - private int mProfile; - private String mConnectionAction; - - public ConnectProfileReceiver(BluetoothDevice device, int profile, int expectedFlags) { - super(expectedFlags); - - mDevice = device; - mProfile = profile; - - switch (mProfile) { - case BluetoothProfile.A2DP: - mConnectionAction = BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED; - break; - case BluetoothProfile.HEADSET: - mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED; - break; - case BluetoothProfile.HID_HOST: - mConnectionAction = BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED; - break; - case BluetoothProfile.PAN: - mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED; - break; - case BluetoothProfile.MAP_CLIENT: - mConnectionAction = BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED; - break; - default: - mConnectionAction = null; - } - } - - @Override - public void onReceive(Context context, Intent intent) { - if (mConnectionAction != null && mConnectionAction.equals(intent.getAction())) { - if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) { - return; - } - - int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); - assertNotSame(-1, state); - switch (state) { - case BluetoothProfile.STATE_DISCONNECTED: - setFiredFlag(STATE_DISCONNECTED_FLAG); - break; - case BluetoothProfile.STATE_CONNECTING: - setFiredFlag(STATE_CONNECTING_FLAG); - break; - case BluetoothProfile.STATE_CONNECTED: - setFiredFlag(STATE_CONNECTED_FLAG); - break; - case BluetoothProfile.STATE_DISCONNECTING: - setFiredFlag(STATE_DISCONNECTING_FLAG); - break; - } - } - } - } - - private class ConnectPanReceiver extends ConnectProfileReceiver { - private int mRole; - - public ConnectPanReceiver(BluetoothDevice device, int role, int expectedFlags) { - super(device, BluetoothProfile.PAN, expectedFlags); - - mRole = role; - } - - @Override - public void onReceive(Context context, Intent intent) { - if (mRole != intent.getIntExtra(BluetoothPan.EXTRA_LOCAL_ROLE, -1)) { - return; - } - - super.onReceive(context, intent); - } - } - - private class StartStopScoReceiver extends FlagReceiver { - private static final int STATE_CONNECTED_FLAG = 1; - private static final int STATE_DISCONNECTED_FLAG = 1 << 1; - - public StartStopScoReceiver(int expectedFlags) { - super(expectedFlags); - } - - @Override - public void onReceive(Context context, Intent intent) { - if (AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(intent.getAction())) { - int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, - AudioManager.SCO_AUDIO_STATE_ERROR); - assertNotSame(AudioManager.SCO_AUDIO_STATE_ERROR, state); - switch(state) { - case AudioManager.SCO_AUDIO_STATE_CONNECTED: - setFiredFlag(STATE_CONNECTED_FLAG); - break; - case AudioManager.SCO_AUDIO_STATE_DISCONNECTED: - setFiredFlag(STATE_DISCONNECTED_FLAG); - break; - } - } - } - } - - - private class MceSetMessageStatusReceiver extends FlagReceiver { - private static final int MESSAGE_RECEIVED_FLAG = 1; - private static final int STATUS_CHANGED_FLAG = 1 << 1; - - public MceSetMessageStatusReceiver(int expectedFlags) { - super(expectedFlags); - } - - @Override - public void onReceive(Context context, Intent intent) { - if (BluetoothMapClient.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) { - String handle = intent.getStringExtra(BluetoothMapClient.EXTRA_MESSAGE_HANDLE); - assertNotNull(handle); - setFiredFlag(MESSAGE_RECEIVED_FLAG); - mMsgHandle = handle; - } else if (BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED.equals(intent.getAction())) { - int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE); - assertEquals(result, BluetoothMapClient.RESULT_SUCCESS); - setFiredFlag(STATUS_CHANGED_FLAG); - } else if (BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED.equals(intent.getAction())) { - int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE); - assertEquals(result, BluetoothMapClient.RESULT_SUCCESS); - setFiredFlag(STATUS_CHANGED_FLAG); - } - } - } - - private BluetoothProfile.ServiceListener mServiceListener = - new BluetoothProfile.ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - synchronized (this) { - switch (profile) { - case BluetoothProfile.A2DP: - mA2dp = (BluetoothA2dp) proxy; - break; - case BluetoothProfile.HEADSET: - mHeadset = (BluetoothHeadset) proxy; - break; - case BluetoothProfile.HID_HOST: - mInput = (BluetoothHidHost) proxy; - break; - case BluetoothProfile.PAN: - mPan = (BluetoothPan) proxy; - break; - case BluetoothProfile.MAP_CLIENT: - mMce = (BluetoothMapClient) proxy; - break; - } - } - } - - @Override - public void onServiceDisconnected(int profile) { - synchronized (this) { - switch (profile) { - case BluetoothProfile.A2DP: - mA2dp = null; - break; - case BluetoothProfile.HEADSET: - mHeadset = null; - break; - case BluetoothProfile.HID_HOST: - mInput = null; - break; - case BluetoothProfile.PAN: - mPan = null; - break; - case BluetoothProfile.MAP_CLIENT: - mMce = null; - break; - } - } - } - }; - - private List<BroadcastReceiver> mReceivers = new ArrayList<BroadcastReceiver>(); - - private BufferedWriter mOutputWriter; - private String mTag; - private String mOutputFile; - - private Context mContext; - private BluetoothA2dp mA2dp = null; - private BluetoothHeadset mHeadset = null; - private BluetoothHidHost mInput = null; - private BluetoothPan mPan = null; - private BluetoothMapClient mMce = null; - private String mMsgHandle = null; - - /** - * Creates a utility instance for testing Bluetooth. - * - * @param context The context of the application using the utility. - * @param tag The log tag of the application using the utility. - */ - public BluetoothTestUtils(Context context, String tag) { - this(context, tag, null); - } - - /** - * Creates a utility instance for testing Bluetooth. - * - * @param context The context of the application using the utility. - * @param tag The log tag of the application using the utility. - * @param outputFile The path to an output file if the utility is to write results to a - * separate file. - */ - public BluetoothTestUtils(Context context, String tag, String outputFile) { - mContext = context; - mTag = tag; - mOutputFile = outputFile; - - if (mOutputFile == null) { - mOutputWriter = null; - } else { - try { - mOutputWriter = new BufferedWriter(new FileWriter(new File( - Environment.getExternalStorageDirectory(), mOutputFile), true)); - } catch (IOException e) { - Log.w(mTag, "Test output file could not be opened", e); - mOutputWriter = null; - } - } - } - - /** - * Closes the utility instance and unregisters any BroadcastReceivers. - */ - public void close() { - while (!mReceivers.isEmpty()) { - mContext.unregisterReceiver(mReceivers.remove(0)); - } - - if (mOutputWriter != null) { - try { - mOutputWriter.close(); - } catch (IOException e) { - Log.w(mTag, "Test output file could not be closed", e); - } - } - } - - /** - * Enables Bluetooth and checks to make sure that Bluetooth was turned on and that the correct - * actions were broadcast. - * - * @param adapter The BT adapter. - */ - public void enable(BluetoothAdapter adapter) { - writeOutput("Enabling Bluetooth adapter."); - assertFalse(adapter.isEnabled()); - int btState = adapter.getState(); - final Semaphore completionSemaphore = new Semaphore(0); - final BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { - return; - } - final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, - BluetoothAdapter.ERROR); - if (state == BluetoothAdapter.STATE_ON) { - completionSemaphore.release(); - } - } - }; - - final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); - mContext.registerReceiver(receiver, filter); - // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to - // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable. - // So no assertion applied here. - adapter.enable(); - boolean success = false; - try { - success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS); - writeOutput(String.format("enable() completed in 0 ms")); - } catch (final InterruptedException e) { - // This should never happen but just in case it does, the test will fail anyway. - } - mContext.unregisterReceiver(receiver); - if (!success) { - fail(String.format("enable() timeout: state=%d (expected %d)", btState, - BluetoothAdapter.STATE_ON)); - } - } - - /** - * Disables Bluetooth and checks to make sure that Bluetooth was turned off and that the correct - * actions were broadcast. - * - * @param adapter The BT adapter. - */ - public void disable(BluetoothAdapter adapter) { - writeOutput("Disabling Bluetooth adapter."); - assertTrue(adapter.isEnabled()); - int btState = adapter.getState(); - final Semaphore completionSemaphore = new Semaphore(0); - final BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { - return; - } - final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, - BluetoothAdapter.ERROR); - if (state == BluetoothAdapter.STATE_OFF) { - completionSemaphore.release(); - } - } - }; - - final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); - mContext.registerReceiver(receiver, filter); - // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to - // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable. - // So no assertion applied here. - adapter.disable(); - boolean success = false; - try { - success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS); - writeOutput(String.format("disable() completed in 0 ms")); - } catch (final InterruptedException e) { - // This should never happen but just in case it does, the test will fail anyway. - } - mContext.unregisterReceiver(receiver); - if (!success) { - fail(String.format("disable() timeout: state=%d (expected %d)", btState, - BluetoothAdapter.STATE_OFF)); - } - } - - /** - * Puts the local device into discoverable mode and checks to make sure that the local device - * is in discoverable mode and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - */ - public void discoverable(BluetoothAdapter adapter) { - if (!adapter.isEnabled()) { - fail("discoverable() bluetooth not enabled"); - } - - int scanMode = adapter.getScanMode(); - if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE) { - return; - } - - final Semaphore completionSemaphore = new Semaphore(0); - final BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) { - return; - } - final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, - BluetoothAdapter.SCAN_MODE_NONE); - if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { - completionSemaphore.release(); - } - } - }; - - final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); - mContext.registerReceiver(receiver, filter); - assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)); - boolean success = false; - try { - success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT, - TimeUnit.MILLISECONDS); - writeOutput(String.format("discoverable() completed in 0 ms")); - } catch (final InterruptedException e) { - // This should never happen but just in case it does, the test will fail anyway. - } - mContext.unregisterReceiver(receiver); - if (!success) { - fail(String.format("discoverable() timeout: scanMode=%d (expected %d)", scanMode, - BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)); - } - } - - /** - * Puts the local device into connectable only mode and checks to make sure that the local - * device is in in connectable mode and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - */ - public void undiscoverable(BluetoothAdapter adapter) { - if (!adapter.isEnabled()) { - fail("undiscoverable() bluetooth not enabled"); - } - - int scanMode = adapter.getScanMode(); - if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { - return; - } - - final Semaphore completionSemaphore = new Semaphore(0); - final BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) { - return; - } - final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, - BluetoothAdapter.SCAN_MODE_NONE); - if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) { - completionSemaphore.release(); - } - } - }; - - final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); - mContext.registerReceiver(receiver, filter); - assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE)); - boolean success = false; - try { - success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT, - TimeUnit.MILLISECONDS); - writeOutput(String.format("undiscoverable() completed in 0 ms")); - } catch (InterruptedException e) { - // This should never happen but just in case it does, the test will fail anyway. - } - mContext.unregisterReceiver(receiver); - if (!success) { - fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d)", scanMode, - BluetoothAdapter.SCAN_MODE_CONNECTABLE)); - } - } - - /** - * Starts a scan for remote devices and checks to make sure that the local device is scanning - * and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - */ - public void startScan(BluetoothAdapter adapter) { - int mask = BluetoothReceiver.DISCOVERY_STARTED_FLAG; - - if (!adapter.isEnabled()) { - fail("startScan() bluetooth not enabled"); - } - - if (adapter.isDiscovering()) { - return; - } - - BluetoothReceiver receiver = getBluetoothReceiver(mask); - - long start = System.currentTimeMillis(); - assertTrue(adapter.startDiscovery()); - - while (System.currentTimeMillis() - start < START_STOP_SCAN_TIMEOUT) { - if (adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) { - writeOutput(String.format("startScan() completed in %d ms", - (receiver.getCompletedTime() - start))); - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)", - adapter.isDiscovering(), firedFlags, mask)); - } - - /** - * Stops a scan for remote devices and checks to make sure that the local device is not scanning - * and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - */ - public void stopScan(BluetoothAdapter adapter) { - int mask = BluetoothReceiver.DISCOVERY_FINISHED_FLAG; - - if (!adapter.isEnabled()) { - fail("stopScan() bluetooth not enabled"); - } - - if (!adapter.isDiscovering()) { - return; - } - - BluetoothReceiver receiver = getBluetoothReceiver(mask); - - long start = System.currentTimeMillis(); - assertTrue(adapter.cancelDiscovery()); - - while (System.currentTimeMillis() - start < START_STOP_SCAN_TIMEOUT) { - if (!adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) { - writeOutput(String.format("stopScan() completed in %d ms", - (receiver.getCompletedTime() - start))); - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)", - adapter.isDiscovering(), firedFlags, mask)); - - } - - /** - * Enables PAN tethering on the local device and checks to make sure that tethering is enabled. - * - * @param adapter The BT adapter. - */ - public void enablePan(BluetoothAdapter adapter) { - if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); - assertNotNull(mPan); - - long start = System.currentTimeMillis(); - mPan.setBluetoothTethering(true); - long stop = System.currentTimeMillis(); - assertTrue(mPan.isTetheringOn()); - - writeOutput(String.format("enablePan() completed in %d ms", (stop - start))); - } - - /** - * Disables PAN tethering on the local device and checks to make sure that tethering is - * disabled. - * - * @param adapter The BT adapter. - */ - public void disablePan(BluetoothAdapter adapter) { - if (mPan == null) mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); - assertNotNull(mPan); - - long start = System.currentTimeMillis(); - mPan.setBluetoothTethering(false); - long stop = System.currentTimeMillis(); - assertFalse(mPan.isTetheringOn()); - - writeOutput(String.format("disablePan() completed in %d ms", (stop - start))); - } - - /** - * Initiates a pairing with a remote device and checks to make sure that the devices are paired - * and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - * @param passkey The pairing passkey if pairing requires a passkey. Any value if not. - * @param pin The pairing pin if pairing requires a pin. Any value if not. - */ - public void pair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, byte[] pin) { - pairOrAcceptPair(adapter, device, passkey, pin, true); - } - - /** - * Accepts a pairing with a remote device and checks to make sure that the devices are paired - * and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - * @param passkey The pairing passkey if pairing requires a passkey. Any value if not. - * @param pin The pairing pin if pairing requires a pin. Any value if not. - */ - public void acceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, - byte[] pin) { - pairOrAcceptPair(adapter, device, passkey, pin, false); - } - - /** - * Helper method used by {@link #pair(BluetoothAdapter, BluetoothDevice, int, byte[])} and - * {@link #acceptPair(BluetoothAdapter, BluetoothDevice, int, byte[])} to either pair or accept - * a pairing request. - * - * @param adapter The BT adapter. - * @param device The remote device. - * @param passkey The pairing passkey if pairing requires a passkey. Any value if not. - * @param pin The pairing pin if pairing requires a pin. Any value if not. - * @param shouldPair Whether to pair or accept the pair. - */ - private void pairOrAcceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, - byte[] pin, boolean shouldPair) { - int mask = PairReceiver.STATE_BONDING_FLAG | PairReceiver.STATE_BONDED_FLAG; - long start = -1; - String methodName; - if (shouldPair) { - methodName = String.format("pair(device=%s)", device); - } else { - methodName = String.format("acceptPair(device=%s)", device); - } - - if (!adapter.isEnabled()) { - fail(String.format("%s bluetooth not enabled", methodName)); - } - - PairReceiver receiver = getPairReceiver(device, passkey, pin, mask); - - int state = device.getBondState(); - switch (state) { - case BluetoothDevice.BOND_NONE: - assertFalse(adapter.getBondedDevices().contains(device)); - start = System.currentTimeMillis(); - if (shouldPair) { - assertTrue(device.createBond()); - } - break; - case BluetoothDevice.BOND_BONDING: - mask = 0; // Don't check for received intents since we might have missed them. - break; - case BluetoothDevice.BOND_BONDED: - assertTrue(adapter.getBondedDevices().contains(device)); - return; - default: - removeReceiver(receiver); - fail(String.format("%s invalid state: state=%d", methodName, state)); - } - - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < PAIR_UNPAIR_TIMEOUT) { - state = device.getBondState(); - if (state == BluetoothDevice.BOND_BONDED && (receiver.getFiredFlags() & mask) == mask) { - assertTrue(adapter.getBondedDevices().contains(device)); - long finish = receiver.getCompletedTime(); - if (start != -1 && finish != -1) { - writeOutput(String.format("%s completed in %d ms", methodName, - (finish - start))); - } else { - writeOutput(String.format("%s completed", methodName)); - } - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", - methodName, state, BluetoothDevice.BOND_BONDED, firedFlags, mask)); - } - - /** - * Deletes a pairing with a remote device and checks to make sure that the devices are unpaired - * and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - */ - public void unpair(BluetoothAdapter adapter, BluetoothDevice device) { - int mask = PairReceiver.STATE_NONE_FLAG; - long start = -1; - String methodName = String.format("unpair(device=%s)", device); - - if (!adapter.isEnabled()) { - fail(String.format("%s bluetooth not enabled", methodName)); - } - - PairReceiver receiver = getPairReceiver(device, 0, null, mask); - - int state = device.getBondState(); - switch (state) { - case BluetoothDevice.BOND_NONE: - assertFalse(adapter.getBondedDevices().contains(device)); - removeReceiver(receiver); - return; - case BluetoothDevice.BOND_BONDING: - start = System.currentTimeMillis(); - assertTrue(device.removeBond()); - break; - case BluetoothDevice.BOND_BONDED: - assertTrue(adapter.getBondedDevices().contains(device)); - start = System.currentTimeMillis(); - assertTrue(device.removeBond()); - break; - default: - removeReceiver(receiver); - fail(String.format("%s invalid state: state=%d", methodName, state)); - } - - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < PAIR_UNPAIR_TIMEOUT) { - if (device.getBondState() == BluetoothDevice.BOND_NONE - && (receiver.getFiredFlags() & mask) == mask) { - assertFalse(adapter.getBondedDevices().contains(device)); - long finish = receiver.getCompletedTime(); - if (start != -1 && finish != -1) { - writeOutput(String.format("%s completed in %d ms", methodName, - (finish - start))); - } else { - writeOutput(String.format("%s completed", methodName)); - } - removeReceiver(receiver); - return; - } - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", - methodName, state, BluetoothDevice.BOND_BONDED, firedFlags, mask)); - } - - /** - * Deletes all pairings of remote devices - * @param adapter the BT adapter - */ - public void unpairAll(BluetoothAdapter adapter) { - Set<BluetoothDevice> devices = adapter.getBondedDevices(); - for (BluetoothDevice device : devices) { - unpair(adapter, device); - } - } - - /** - * Connects a profile from the local device to a remote device and checks to make sure that the - * profile is connected and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - * @param profile The profile to connect. One of {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#HID_HOST} or {@link BluetoothProfile#MAP_CLIENT}.. - * @param methodName The method name to printed in the logs. If null, will be - * "connectProfile(profile=<profile>, device=<device>)" - */ - public void connectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile, - String methodName) { - if (methodName == null) { - methodName = String.format("connectProfile(profile=%d, device=%s)", profile, device); - } - int mask = (ConnectProfileReceiver.STATE_CONNECTING_FLAG - | ConnectProfileReceiver.STATE_CONNECTED_FLAG); - long start = -1; - - if (!adapter.isEnabled()) { - fail(String.format("%s bluetooth not enabled", methodName)); - } - - if (!adapter.getBondedDevices().contains(device)) { - fail(String.format("%s device not paired", methodName)); - } - - BluetoothProfile proxy = connectProxy(adapter, profile); - assertNotNull(proxy); - - ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask); - - int state = proxy.getConnectionState(device); - switch (state) { - case BluetoothProfile.STATE_CONNECTED: - removeReceiver(receiver); - return; - case BluetoothProfile.STATE_CONNECTING: - mask = 0; // Don't check for received intents since we might have missed them. - break; - case BluetoothProfile.STATE_DISCONNECTED: - case BluetoothProfile.STATE_DISCONNECTING: - start = System.currentTimeMillis(); - if (profile == BluetoothProfile.A2DP) { - assertTrue(((BluetoothA2dp)proxy).connect(device)); - } else if (profile == BluetoothProfile.HEADSET) { - assertTrue(((BluetoothHeadset)proxy).connect(device)); - } else if (profile == BluetoothProfile.HID_HOST) { - assertTrue(((BluetoothHidHost)proxy).connect(device)); - } else if (profile == BluetoothProfile.MAP_CLIENT) { - assertTrue(((BluetoothMapClient)proxy).connect(device)); - } - break; - default: - removeReceiver(receiver); - fail(String.format("%s invalid state: state=%d", methodName, state)); - } - - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { - state = proxy.getConnectionState(device); - if (state == BluetoothProfile.STATE_CONNECTED - && (receiver.getFiredFlags() & mask) == mask) { - long finish = receiver.getCompletedTime(); - if (start != -1 && finish != -1) { - writeOutput(String.format("%s completed in %d ms", methodName, - (finish - start))); - } else { - writeOutput(String.format("%s completed", methodName)); - } - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", - methodName, state, BluetoothProfile.STATE_CONNECTED, firedFlags, mask)); - } - - /** - * Disconnects a profile between the local device and a remote device and checks to make sure - * that the profile is disconnected and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - * @param profile The profile to disconnect. One of {@link BluetoothProfile#A2DP}, - * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}. - * @param methodName The method name to printed in the logs. If null, will be - * "connectProfile(profile=<profile>, device=<device>)" - */ - public void disconnectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile, - String methodName) { - if (methodName == null) { - methodName = String.format("disconnectProfile(profile=%d, device=%s)", profile, device); - } - int mask = (ConnectProfileReceiver.STATE_DISCONNECTING_FLAG - | ConnectProfileReceiver.STATE_DISCONNECTED_FLAG); - long start = -1; - - if (!adapter.isEnabled()) { - fail(String.format("%s bluetooth not enabled", methodName)); - } - - if (!adapter.getBondedDevices().contains(device)) { - fail(String.format("%s device not paired", methodName)); - } - - BluetoothProfile proxy = connectProxy(adapter, profile); - assertNotNull(proxy); - - ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask); - - int state = proxy.getConnectionState(device); - switch (state) { - case BluetoothProfile.STATE_CONNECTED: - case BluetoothProfile.STATE_CONNECTING: - start = System.currentTimeMillis(); - if (profile == BluetoothProfile.A2DP) { - assertTrue(((BluetoothA2dp)proxy).disconnect(device)); - } else if (profile == BluetoothProfile.HEADSET) { - assertTrue(((BluetoothHeadset)proxy).disconnect(device)); - } else if (profile == BluetoothProfile.HID_HOST) { - assertTrue(((BluetoothHidHost)proxy).disconnect(device)); - } else if (profile == BluetoothProfile.MAP_CLIENT) { - assertTrue(((BluetoothMapClient)proxy).disconnect(device)); - } - break; - case BluetoothProfile.STATE_DISCONNECTED: - removeReceiver(receiver); - return; - case BluetoothProfile.STATE_DISCONNECTING: - mask = 0; // Don't check for received intents since we might have missed them. - break; - default: - removeReceiver(receiver); - fail(String.format("%s invalid state: state=%d", methodName, state)); - } - - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { - state = proxy.getConnectionState(device); - if (state == BluetoothProfile.STATE_DISCONNECTED - && (receiver.getFiredFlags() & mask) == mask) { - long finish = receiver.getCompletedTime(); - if (start != -1 && finish != -1) { - writeOutput(String.format("%s completed in %d ms", methodName, - (finish - start))); - } else { - writeOutput(String.format("%s completed", methodName)); - } - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", - methodName, state, BluetoothProfile.STATE_DISCONNECTED, firedFlags, mask)); - } - - /** - * Connects the PANU to a remote NAP and checks to make sure that the PANU is connected and that - * the correct actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - */ - public void connectPan(BluetoothAdapter adapter, BluetoothDevice device) { - connectPanOrIncomingPanConnection(adapter, device, true); - } - - /** - * Checks that a remote PANU connects to the local NAP correctly and that the correct actions - * were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - */ - public void incomingPanConnection(BluetoothAdapter adapter, BluetoothDevice device) { - connectPanOrIncomingPanConnection(adapter, device, false); - } - - /** - * Helper method used by {@link #connectPan(BluetoothAdapter, BluetoothDevice)} and - * {@link #incomingPanConnection(BluetoothAdapter, BluetoothDevice)} to either connect to a - * remote NAP or verify that a remote device connected to the local NAP. - * - * @param adapter The BT adapter. - * @param device The remote device. - * @param connect If the method should initiate the connection (is PANU) - */ - private void connectPanOrIncomingPanConnection(BluetoothAdapter adapter, BluetoothDevice device, - boolean connect) { - long start = -1; - int mask, role; - String methodName; - - if (connect) { - methodName = String.format("connectPan(device=%s)", device); - mask = (ConnectProfileReceiver.STATE_CONNECTED_FLAG | - ConnectProfileReceiver.STATE_CONNECTING_FLAG); - role = BluetoothPan.LOCAL_PANU_ROLE; - } else { - methodName = String.format("incomingPanConnection(device=%s)", device); - mask = ConnectProfileReceiver.STATE_CONNECTED_FLAG; - role = BluetoothPan.LOCAL_NAP_ROLE; - } - - if (!adapter.isEnabled()) { - fail(String.format("%s bluetooth not enabled", methodName)); - } - - if (!adapter.getBondedDevices().contains(device)) { - fail(String.format("%s device not paired", methodName)); - } - - mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); - assertNotNull(mPan); - ConnectPanReceiver receiver = getConnectPanReceiver(device, role, mask); - - int state = mPan.getConnectionState(device); - switch (state) { - case BluetoothPan.STATE_CONNECTED: - removeReceiver(receiver); - return; - case BluetoothPan.STATE_CONNECTING: - mask = 0; // Don't check for received intents since we might have missed them. - break; - case BluetoothPan.STATE_DISCONNECTED: - case BluetoothPan.STATE_DISCONNECTING: - start = System.currentTimeMillis(); - if (role == BluetoothPan.LOCAL_PANU_ROLE) { - Log.i("BT", "connect to pan"); - assertTrue(mPan.connect(device)); - } - break; - default: - removeReceiver(receiver); - fail(String.format("%s invalid state: state=%d", methodName, state)); - } - - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { - state = mPan.getConnectionState(device); - if (state == BluetoothPan.STATE_CONNECTED - && (receiver.getFiredFlags() & mask) == mask) { - long finish = receiver.getCompletedTime(); - if (start != -1 && finish != -1) { - writeOutput(String.format("%s completed in %d ms", methodName, - (finish - start))); - } else { - writeOutput(String.format("%s completed", methodName)); - } - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", - methodName, state, BluetoothPan.STATE_CONNECTED, firedFlags, mask)); - } - - /** - * Disconnects the PANU from a remote NAP and checks to make sure that the PANU is disconnected - * and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - */ - public void disconnectPan(BluetoothAdapter adapter, BluetoothDevice device) { - disconnectFromRemoteOrVerifyConnectNap(adapter, device, true); - } - - /** - * Checks that a remote PANU disconnects from the local NAP correctly and that the correct - * actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - */ - public void incomingPanDisconnection(BluetoothAdapter adapter, BluetoothDevice device) { - disconnectFromRemoteOrVerifyConnectNap(adapter, device, false); - } - - /** - * Helper method used by {@link #disconnectPan(BluetoothAdapter, BluetoothDevice)} and - * {@link #incomingPanDisconnection(BluetoothAdapter, BluetoothDevice)} to either disconnect - * from a remote NAP or verify that a remote device disconnected from the local NAP. - * - * @param adapter The BT adapter. - * @param device The remote device. - * @param disconnect Whether the method should connect or verify. - */ - private void disconnectFromRemoteOrVerifyConnectNap(BluetoothAdapter adapter, - BluetoothDevice device, boolean disconnect) { - long start = -1; - int mask, role; - String methodName; - - if (disconnect) { - methodName = String.format("disconnectPan(device=%s)", device); - mask = (ConnectProfileReceiver.STATE_DISCONNECTED_FLAG | - ConnectProfileReceiver.STATE_DISCONNECTING_FLAG); - role = BluetoothPan.LOCAL_PANU_ROLE; - } else { - methodName = String.format("incomingPanDisconnection(device=%s)", device); - mask = ConnectProfileReceiver.STATE_DISCONNECTED_FLAG; - role = BluetoothPan.LOCAL_NAP_ROLE; - } - - if (!adapter.isEnabled()) { - fail(String.format("%s bluetooth not enabled", methodName)); - } - - if (!adapter.getBondedDevices().contains(device)) { - fail(String.format("%s device not paired", methodName)); - } - - mPan = (BluetoothPan) connectProxy(adapter, BluetoothProfile.PAN); - assertNotNull(mPan); - ConnectPanReceiver receiver = getConnectPanReceiver(device, role, mask); - - int state = mPan.getConnectionState(device); - switch (state) { - case BluetoothPan.STATE_CONNECTED: - case BluetoothPan.STATE_CONNECTING: - start = System.currentTimeMillis(); - if (role == BluetoothPan.LOCAL_PANU_ROLE) { - assertTrue(mPan.disconnect(device)); - } - break; - case BluetoothPan.STATE_DISCONNECTED: - removeReceiver(receiver); - return; - case BluetoothPan.STATE_DISCONNECTING: - mask = 0; // Don't check for received intents since we might have missed them. - break; - default: - removeReceiver(receiver); - fail(String.format("%s invalid state: state=%d", methodName, state)); - } - - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < CONNECT_DISCONNECT_PROFILE_TIMEOUT) { - state = mPan.getConnectionState(device); - if (state == BluetoothHidHost.STATE_DISCONNECTED - && (receiver.getFiredFlags() & mask) == mask) { - long finish = receiver.getCompletedTime(); - if (start != -1 && finish != -1) { - writeOutput(String.format("%s completed in %d ms", methodName, - (finish - start))); - } else { - writeOutput(String.format("%s completed", methodName)); - } - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", - methodName, state, BluetoothHidHost.STATE_DISCONNECTED, firedFlags, mask)); - } - - /** - * Opens a SCO channel using {@link android.media.AudioManager#startBluetoothSco()} and checks - * to make sure that the channel is opened and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - */ - public void startSco(BluetoothAdapter adapter, BluetoothDevice device) { - startStopSco(adapter, device, true); - } - - /** - * Closes a SCO channel using {@link android.media.AudioManager#stopBluetoothSco()} and checks - * to make sure that the channel is closed and that the correct actions were broadcast. - * - * @param adapter The BT adapter. - * @param device The remote device. - */ - public void stopSco(BluetoothAdapter adapter, BluetoothDevice device) { - startStopSco(adapter, device, false); - } - /** - * Helper method for {@link #startSco(BluetoothAdapter, BluetoothDevice)} and - * {@link #stopSco(BluetoothAdapter, BluetoothDevice)}. - * - * @param adapter The BT adapter. - * @param device The remote device. - * @param isStart Whether the SCO channel should be opened. - */ - private void startStopSco(BluetoothAdapter adapter, BluetoothDevice device, boolean isStart) { - long start = -1; - int mask; - String methodName; - - if (isStart) { - methodName = String.format("startSco(device=%s)", device); - mask = StartStopScoReceiver.STATE_CONNECTED_FLAG; - } else { - methodName = String.format("stopSco(device=%s)", device); - mask = StartStopScoReceiver.STATE_DISCONNECTED_FLAG; - } - - if (!adapter.isEnabled()) { - fail(String.format("%s bluetooth not enabled", methodName)); - } - - if (!adapter.getBondedDevices().contains(device)) { - fail(String.format("%s device not paired", methodName)); - } - - AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - assertNotNull(manager); - - if (!manager.isBluetoothScoAvailableOffCall()) { - fail(String.format("%s device does not support SCO", methodName)); - } - - boolean isScoOn = manager.isBluetoothScoOn(); - if (isStart == isScoOn) { - return; - } - - StartStopScoReceiver receiver = getStartStopScoReceiver(mask); - start = System.currentTimeMillis(); - if (isStart) { - manager.startBluetoothSco(); - } else { - manager.stopBluetoothSco(); - } - - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < START_STOP_SCO_TIMEOUT) { - isScoOn = manager.isBluetoothScoOn(); - if (isStart == isScoOn && (receiver.getFiredFlags() & mask) == mask) { - long finish = receiver.getCompletedTime(); - if (start != -1 && finish != -1) { - writeOutput(String.format("%s completed in %d ms", methodName, - (finish - start))); - } else { - writeOutput(String.format("%s completed", methodName)); - } - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("%s timeout: on=%b (expected %b), flags=0x%x (expected 0x%x)", - methodName, isScoOn, isStart, firedFlags, mask)); - } - - /** - * Writes a string to the logcat and a file if a file has been specified in the constructor. - * - * @param s The string to be written. - */ - public void writeOutput(String s) { - Log.i(mTag, s); - if (mOutputWriter == null) { - return; - } - try { - mOutputWriter.write(s + "\n"); - mOutputWriter.flush(); - } catch (IOException e) { - Log.w(mTag, "Could not write to output file", e); - } - } - - public void mceGetUnreadMessage(BluetoothAdapter adapter, BluetoothDevice device) { - int mask; - String methodName = "getUnreadMessage"; - - if (!adapter.isEnabled()) { - fail(String.format("%s bluetooth not enabled", methodName)); - } - - if (!adapter.getBondedDevices().contains(device)) { - fail(String.format("%s device not paired", methodName)); - } - - mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT); - assertNotNull(mMce); - - if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { - fail(String.format("%s device is not connected", methodName)); - } - - mMsgHandle = null; - mask = MceSetMessageStatusReceiver.MESSAGE_RECEIVED_FLAG; - MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask); - assertTrue(mMce.getUnreadMessages(device)); - - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < GET_UNREAD_MESSAGE_TIMEOUT) { - if ((receiver.getFiredFlags() & mask) == mask) { - writeOutput(String.format("%s completed", methodName)); - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", - methodName, mMce.getConnectionState(device), BluetoothMapClient.STATE_CONNECTED, firedFlags, mask)); - } - - /** - * Set a message to read/unread/deleted/undeleted - */ - public void mceSetMessageStatus(BluetoothAdapter adapter, BluetoothDevice device, int status) { - int mask; - String methodName = "setMessageStatus"; - - if (!adapter.isEnabled()) { - fail(String.format("%s bluetooth not enabled", methodName)); - } - - if (!adapter.getBondedDevices().contains(device)) { - fail(String.format("%s device not paired", methodName)); - } - - mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT); - assertNotNull(mMce); - - if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { - fail(String.format("%s device is not connected", methodName)); - } - - assertNotNull(mMsgHandle); - mask = MceSetMessageStatusReceiver.STATUS_CHANGED_FLAG; - MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask); - - assertTrue(mMce.setMessageStatus(device, mMsgHandle, status)); - - long s = System.currentTimeMillis(); - while (System.currentTimeMillis() - s < SET_MESSAGE_STATUS_TIMEOUT) { - if ((receiver.getFiredFlags() & mask) == mask) { - writeOutput(String.format("%s completed", methodName)); - removeReceiver(receiver); - return; - } - sleep(POLL_TIME); - } - - int firedFlags = receiver.getFiredFlags(); - removeReceiver(receiver); - fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)", - methodName, mMce.getConnectionState(device), BluetoothPan.STATE_CONNECTED, firedFlags, mask)); - } - - private void addReceiver(BroadcastReceiver receiver, String[] actions) { - IntentFilter filter = new IntentFilter(); - for (String action: actions) { - filter.addAction(action); - } - mContext.registerReceiver(receiver, filter); - mReceivers.add(receiver); - } - - private BluetoothReceiver getBluetoothReceiver(int expectedFlags) { - String[] actions = { - BluetoothAdapter.ACTION_DISCOVERY_FINISHED, - BluetoothAdapter.ACTION_DISCOVERY_STARTED, - BluetoothAdapter.ACTION_SCAN_MODE_CHANGED, - BluetoothAdapter.ACTION_STATE_CHANGED}; - BluetoothReceiver receiver = new BluetoothReceiver(expectedFlags); - addReceiver(receiver, actions); - return receiver; - } - - private PairReceiver getPairReceiver(BluetoothDevice device, int passkey, byte[] pin, - int expectedFlags) { - String[] actions = { - BluetoothDevice.ACTION_PAIRING_REQUEST, - BluetoothDevice.ACTION_BOND_STATE_CHANGED}; - PairReceiver receiver = new PairReceiver(device, passkey, pin, expectedFlags); - addReceiver(receiver, actions); - return receiver; - } - - private ConnectProfileReceiver getConnectProfileReceiver(BluetoothDevice device, int profile, - int expectedFlags) { - String[] actions = { - BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, - BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED, - BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED, - BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED}; - ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile, - expectedFlags); - addReceiver(receiver, actions); - return receiver; - } - - private ConnectPanReceiver getConnectPanReceiver(BluetoothDevice device, int role, - int expectedFlags) { - String[] actions = {BluetoothPan.ACTION_CONNECTION_STATE_CHANGED}; - ConnectPanReceiver receiver = new ConnectPanReceiver(device, role, expectedFlags); - addReceiver(receiver, actions); - return receiver; - } - - private StartStopScoReceiver getStartStopScoReceiver(int expectedFlags) { - String[] actions = {AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED}; - StartStopScoReceiver receiver = new StartStopScoReceiver(expectedFlags); - addReceiver(receiver, actions); - return receiver; - } - - private MceSetMessageStatusReceiver getMceSetMessageStatusReceiver(BluetoothDevice device, - int expectedFlags) { - String[] actions = {BluetoothMapClient.ACTION_MESSAGE_RECEIVED, - BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED, - BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED}; - MceSetMessageStatusReceiver receiver = new MceSetMessageStatusReceiver(expectedFlags); - addReceiver(receiver, actions); - return receiver; - } - - private void removeReceiver(BroadcastReceiver receiver) { - mContext.unregisterReceiver(receiver); - mReceivers.remove(receiver); - } - - private BluetoothProfile connectProxy(BluetoothAdapter adapter, int profile) { - switch (profile) { - case BluetoothProfile.A2DP: - if (mA2dp != null) { - return mA2dp; - } - break; - case BluetoothProfile.HEADSET: - if (mHeadset != null) { - return mHeadset; - } - break; - case BluetoothProfile.HID_HOST: - if (mInput != null) { - return mInput; - } - break; - case BluetoothProfile.PAN: - if (mPan != null) { - return mPan; - } - case BluetoothProfile.MAP_CLIENT: - if (mMce != null) { - return mMce; - } - break; - default: - return null; - } - adapter.getProfileProxy(mContext, mServiceListener, profile); - long s = System.currentTimeMillis(); - switch (profile) { - case BluetoothProfile.A2DP: - while (mA2dp == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { - sleep(POLL_TIME); - } - return mA2dp; - case BluetoothProfile.HEADSET: - while (mHeadset == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { - sleep(POLL_TIME); - } - return mHeadset; - case BluetoothProfile.HID_HOST: - while (mInput == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { - sleep(POLL_TIME); - } - return mInput; - case BluetoothProfile.PAN: - while (mPan == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { - sleep(POLL_TIME); - } - return mPan; - case BluetoothProfile.MAP_CLIENT: - while (mMce == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) { - sleep(POLL_TIME); - } - return mMce; - default: - return null; - } - } - - private void sleep(long time) { - try { - Thread.sleep(time); - } catch (InterruptedException e) { - } - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java deleted file mode 100644 index 536d722679b6..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.os.ParcelUuid; -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -/** - * Unit test cases for {@link BluetoothUuid}. - * <p> - * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothUuidTest' -w - * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' - */ -public class BluetoothUuidTest extends TestCase { - - @SmallTest - public void testUuidParser() { - byte[] uuid16 = new byte[] { - 0x0B, 0x11 }; - assertEquals(ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"), - BluetoothUuid.parseUuidFrom(uuid16)); - - byte[] uuid32 = new byte[] { - 0x0B, 0x11, 0x33, (byte) 0xFE }; - assertEquals(ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"), - BluetoothUuid.parseUuidFrom(uuid32)); - - byte[] uuid128 = new byte[] { - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, (byte) 0xFF }; - assertEquals(ParcelUuid.fromString("FF0F0E0D-0C0B-0A09-0807-0060504030201"), - BluetoothUuid.parseUuidFrom(uuid128)); - } - - @SmallTest - public void testUuidType() { - assertTrue(BluetoothUuid.is16BitUuid( - ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"))); - assertFalse(BluetoothUuid.is32BitUuid( - ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"))); - - assertFalse(BluetoothUuid.is16BitUuid( - ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"))); - assertTrue(BluetoothUuid.is32BitUuid( - ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"))); - assertFalse(BluetoothUuid.is32BitUuid( - ParcelUuid.fromString("FE33110B-1000-1000-8000-00805F9B34FB"))); - - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java deleted file mode 100644 index e58d905357e6..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.os.Parcel; -import android.os.ParcelUuid; -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -/** - * Unit test cases for {@link AdvertiseData}. - * <p> - * To run the test, use adb shell am instrument -e class 'android.bluetooth.le.AdvertiseDataTest' -w - * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' - */ -public class AdvertiseDataTest extends TestCase { - - private AdvertiseData.Builder mAdvertiseDataBuilder; - - @Override - protected void setUp() throws Exception { - mAdvertiseDataBuilder = new AdvertiseData.Builder(); - } - - @SmallTest - public void testEmptyData() { - Parcel parcel = Parcel.obtain(); - AdvertiseData data = mAdvertiseDataBuilder.build(); - data.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - AdvertiseData dataFromParcel = - AdvertiseData.CREATOR.createFromParcel(parcel); - assertEquals(data, dataFromParcel); - } - - @SmallTest - public void testEmptyServiceUuid() { - Parcel parcel = Parcel.obtain(); - AdvertiseData data = mAdvertiseDataBuilder.setIncludeDeviceName(true).build(); - data.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - AdvertiseData dataFromParcel = - AdvertiseData.CREATOR.createFromParcel(parcel); - assertEquals(data, dataFromParcel); - } - - @SmallTest - public void testEmptyManufacturerData() { - Parcel parcel = Parcel.obtain(); - int manufacturerId = 50; - byte[] manufacturerData = new byte[0]; - AdvertiseData data = - mAdvertiseDataBuilder.setIncludeDeviceName(true) - .addManufacturerData(manufacturerId, manufacturerData).build(); - data.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - AdvertiseData dataFromParcel = - AdvertiseData.CREATOR.createFromParcel(parcel); - assertEquals(data, dataFromParcel); - } - - @SmallTest - public void testEmptyServiceData() { - Parcel parcel = Parcel.obtain(); - ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - byte[] serviceData = new byte[0]; - AdvertiseData data = - mAdvertiseDataBuilder.setIncludeDeviceName(true) - .addServiceData(uuid, serviceData).build(); - data.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - AdvertiseData dataFromParcel = - AdvertiseData.CREATOR.createFromParcel(parcel); - assertEquals(data, dataFromParcel); - } - - @SmallTest - public void testServiceUuid() { - Parcel parcel = Parcel.obtain(); - ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - - AdvertiseData data = - mAdvertiseDataBuilder.setIncludeDeviceName(true) - .addServiceUuid(uuid).addServiceUuid(uuid2).build(); - data.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - AdvertiseData dataFromParcel = - AdvertiseData.CREATOR.createFromParcel(parcel); - assertEquals(data, dataFromParcel); - } - - @SmallTest - public void testManufacturerData() { - Parcel parcel = Parcel.obtain(); - ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - - int manufacturerId = 50; - byte[] manufacturerData = new byte[] { - (byte) 0xF0, 0x00, 0x02, 0x15 }; - AdvertiseData data = - mAdvertiseDataBuilder.setIncludeDeviceName(true) - .addServiceUuid(uuid).addServiceUuid(uuid2) - .addManufacturerData(manufacturerId, manufacturerData).build(); - - data.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - AdvertiseData dataFromParcel = - AdvertiseData.CREATOR.createFromParcel(parcel); - assertEquals(data, dataFromParcel); - } - - @SmallTest - public void testServiceData() { - Parcel parcel = Parcel.obtain(); - ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - byte[] serviceData = new byte[] { - (byte) 0xF0, 0x00, 0x02, 0x15 }; - AdvertiseData data = - mAdvertiseDataBuilder.setIncludeDeviceName(true) - .addServiceData(uuid, serviceData).build(); - data.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - AdvertiseData dataFromParcel = - AdvertiseData.CREATOR.createFromParcel(parcel); - assertEquals(data, dataFromParcel); - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java deleted file mode 100644 index 35da4bceb620..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanFilterTest.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.le.ScanFilter; -import android.bluetooth.le.ScanRecord; -import android.os.Parcel; -import android.os.ParcelUuid; -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -/** - * Unit test cases for Bluetooth LE scan filters. - * <p> - * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanFilterTest' -w - * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' - */ -public class ScanFilterTest extends TestCase { - - private static final String DEVICE_MAC = "01:02:03:04:05:AB"; - private ScanResult mScanResult; - private ScanFilter.Builder mFilterBuilder; - - @Override - protected void setUp() throws Exception { - byte[] scanRecord = new byte[] { - 0x02, 0x01, 0x1a, // advertising flags - 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids - 0x04, 0x09, 0x50, 0x65, 0x64, // setName - 0x02, 0x0A, (byte) 0xec, // tx power level - 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data - 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data - 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble - }; - - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - BluetoothDevice device = adapter.getRemoteDevice(DEVICE_MAC); - mScanResult = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord), - -10, 1397545200000000L); - mFilterBuilder = new ScanFilter.Builder(); - } - - @SmallTest - public void testsetNameFilter() { - ScanFilter filter = mFilterBuilder.setDeviceName("Ped").build(); - assertTrue("setName filter fails", filter.matches(mScanResult)); - - filter = mFilterBuilder.setDeviceName("Pem").build(); - assertFalse("setName filter fails", filter.matches(mScanResult)); - - } - - @SmallTest - public void testDeviceFilter() { - ScanFilter filter = mFilterBuilder.setDeviceAddress(DEVICE_MAC).build(); - assertTrue("device filter fails", filter.matches(mScanResult)); - - filter = mFilterBuilder.setDeviceAddress("11:22:33:44:55:66").build(); - assertFalse("device filter fails", filter.matches(mScanResult)); - } - - @SmallTest - public void testsetServiceUuidFilter() { - ScanFilter filter = mFilterBuilder.setServiceUuid( - ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB")).build(); - assertTrue("uuid filter fails", filter.matches(mScanResult)); - - filter = mFilterBuilder.setServiceUuid( - ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build(); - assertFalse("uuid filter fails", filter.matches(mScanResult)); - - filter = mFilterBuilder - .setServiceUuid(ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"), - ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")) - .build(); - assertTrue("uuid filter fails", filter.matches(mScanResult)); - } - - @SmallTest - public void testsetServiceDataFilter() { - byte[] setServiceData = new byte[] { - 0x50, 0x64 }; - ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - ScanFilter filter = mFilterBuilder.setServiceData(serviceDataUuid, setServiceData).build(); - assertTrue("service data filter fails", filter.matches(mScanResult)); - - byte[] emptyData = new byte[0]; - filter = mFilterBuilder.setServiceData(serviceDataUuid, emptyData).build(); - assertTrue("service data filter fails", filter.matches(mScanResult)); - - byte[] prefixData = new byte[] { - 0x50 }; - filter = mFilterBuilder.setServiceData(serviceDataUuid, prefixData).build(); - assertTrue("service data filter fails", filter.matches(mScanResult)); - - byte[] nonMatchData = new byte[] { - 0x51, 0x64 }; - byte[] mask = new byte[] { - (byte) 0x00, (byte) 0xFF }; - filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData, mask).build(); - assertTrue("partial service data filter fails", filter.matches(mScanResult)); - - filter = mFilterBuilder.setServiceData(serviceDataUuid, nonMatchData).build(); - assertFalse("service data filter fails", filter.matches(mScanResult)); - } - - @SmallTest - public void testManufacturerSpecificData() { - byte[] setManufacturerData = new byte[] { - 0x02, 0x15 }; - int manufacturerId = 0xE0; - ScanFilter filter = - mFilterBuilder.setManufacturerData(manufacturerId, setManufacturerData).build(); - assertTrue("manufacturer data filter fails", filter.matches(mScanResult)); - - byte[] emptyData = new byte[0]; - filter = mFilterBuilder.setManufacturerData(manufacturerId, emptyData).build(); - assertTrue("manufacturer data filter fails", filter.matches(mScanResult)); - - byte[] prefixData = new byte[] { - 0x02 }; - filter = mFilterBuilder.setManufacturerData(manufacturerId, prefixData).build(); - assertTrue("manufacturer data filter fails", filter.matches(mScanResult)); - - // Test data mask - byte[] nonMatchData = new byte[] { - 0x02, 0x14 }; - filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData).build(); - assertFalse("manufacturer data filter fails", filter.matches(mScanResult)); - byte[] mask = new byte[] { - (byte) 0xFF, (byte) 0x00 - }; - filter = mFilterBuilder.setManufacturerData(manufacturerId, nonMatchData, mask).build(); - assertTrue("partial setManufacturerData filter fails", filter.matches(mScanResult)); - } - - @SmallTest - public void testReadWriteParcel() { - ScanFilter filter = mFilterBuilder.build(); - testReadWriteParcelForFilter(filter); - - filter = mFilterBuilder.setDeviceName("Ped").build(); - testReadWriteParcelForFilter(filter); - - filter = mFilterBuilder.setDeviceAddress("11:22:33:44:55:66").build(); - testReadWriteParcelForFilter(filter); - - filter = mFilterBuilder.setServiceUuid( - ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB")).build(); - testReadWriteParcelForFilter(filter); - - filter = mFilterBuilder.setServiceUuid( - ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"), - ParcelUuid.fromString("FFFFFFF0-FFFF-FFFF-FFFF-FFFFFFFFFFFF")).build(); - testReadWriteParcelForFilter(filter); - - byte[] serviceData = new byte[] { - 0x50, 0x64 }; - - ParcelUuid serviceDataUuid = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - filter = mFilterBuilder.setServiceData(serviceDataUuid, serviceData).build(); - testReadWriteParcelForFilter(filter); - - filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build(); - testReadWriteParcelForFilter(filter); - - byte[] serviceDataMask = new byte[] { - (byte) 0xFF, (byte) 0xFF }; - filter = mFilterBuilder.setServiceData(serviceDataUuid, serviceData, serviceDataMask) - .build(); - testReadWriteParcelForFilter(filter); - - byte[] manufacturerData = new byte[] { - 0x02, 0x15 }; - int manufacturerId = 0xE0; - filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData).build(); - testReadWriteParcelForFilter(filter); - - filter = mFilterBuilder.setServiceData(serviceDataUuid, new byte[0]).build(); - testReadWriteParcelForFilter(filter); - - byte[] manufacturerDataMask = new byte[] { - (byte) 0xFF, (byte) 0xFF - }; - filter = mFilterBuilder.setManufacturerData(manufacturerId, manufacturerData, - manufacturerDataMask).build(); - testReadWriteParcelForFilter(filter); - } - - private void testReadWriteParcelForFilter(ScanFilter filter) { - Parcel parcel = Parcel.obtain(); - filter.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - ScanFilter filterFromParcel = - ScanFilter.CREATOR.createFromParcel(parcel); - assertEquals(filter, filterFromParcel); - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java deleted file mode 100644 index 4e817d4a0d91..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.os.ParcelUuid; -import android.test.suitebuilder.annotation.SmallTest; - -import com.android.internal.util.HexDump; -import com.android.modules.utils.BytesMatcher; - -import junit.framework.TestCase; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.Predicate; - -/** - * Unit test cases for {@link ScanRecord}. - * <p> - * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanRecordTest' -w - * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' - */ -public class ScanRecordTest extends TestCase { - /** - * Example raw beacons captured from a Blue Charm BC011 - */ - private static final String RECORD_URL = "0201060303AAFE1716AAFE10EE01626C7565636861726D626561636F6E730009168020691E0EFE13551109426C7565436861726D5F313639363835000000"; - private static final String RECORD_UUID = "0201060303AAFE1716AAFE00EE626C7565636861726D31000000000001000009168020691E0EFE13551109426C7565436861726D5F313639363835000000"; - private static final String RECORD_TLM = "0201060303AAFE1116AAFE20000BF017000008874803FB93540916802069080EFE13551109426C7565436861726D5F313639363835000000000000000000"; - private static final String RECORD_IBEACON = "0201061AFF4C000215426C7565436861726D426561636F6E730EFE1355C509168020691E0EFE13551109426C7565436861726D5F31363936383500000000"; - - @SmallTest - public void testMatchesAnyField_Eddystone_Parser() { - final List<String> found = new ArrayList<>(); - final Predicate<byte[]> matcher = (v) -> { - found.add(HexDump.toHexString(v)); - return false; - }; - ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_URL)) - .matchesAnyField(matcher); - - assertEquals(Arrays.asList( - "020106", - "0303AAFE", - "1716AAFE10EE01626C7565636861726D626561636F6E7300", - "09168020691E0EFE1355", - "1109426C7565436861726D5F313639363835"), found); - } - - @SmallTest - public void testMatchesAnyField_Eddystone() { - final BytesMatcher matcher = BytesMatcher.decode("⊆0016AAFE/00FFFFFF"); - assertMatchesAnyField(RECORD_URL, matcher); - assertMatchesAnyField(RECORD_UUID, matcher); - assertMatchesAnyField(RECORD_TLM, matcher); - assertNotMatchesAnyField(RECORD_IBEACON, matcher); - } - - @SmallTest - public void testMatchesAnyField_iBeacon_Parser() { - final List<String> found = new ArrayList<>(); - final Predicate<byte[]> matcher = (v) -> { - found.add(HexDump.toHexString(v)); - return false; - }; - ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(RECORD_IBEACON)) - .matchesAnyField(matcher); - - assertEquals(Arrays.asList( - "020106", - "1AFF4C000215426C7565436861726D426561636F6E730EFE1355C5", - "09168020691E0EFE1355", - "1109426C7565436861726D5F313639363835"), found); - } - - @SmallTest - public void testMatchesAnyField_iBeacon() { - final BytesMatcher matcher = BytesMatcher.decode("⊆00FF4C0002/00FFFFFFFF"); - assertNotMatchesAnyField(RECORD_URL, matcher); - assertNotMatchesAnyField(RECORD_UUID, matcher); - assertNotMatchesAnyField(RECORD_TLM, matcher); - assertMatchesAnyField(RECORD_IBEACON, matcher); - } - - @SmallTest - public void testParser() { - byte[] scanRecord = new byte[] { - 0x02, 0x01, 0x1a, // advertising flags - 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids - 0x04, 0x09, 0x50, 0x65, 0x64, // name - 0x02, 0x0A, (byte) 0xec, // tx power level - 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data - 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data - 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble - }; - ScanRecord data = ScanRecord.parseFromBytes(scanRecord); - assertEquals(0x1a, data.getAdvertiseFlags()); - ParcelUuid uuid1 = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - ParcelUuid uuid2 = ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - assertTrue(data.getServiceUuids().contains(uuid1)); - assertTrue(data.getServiceUuids().contains(uuid2)); - - assertEquals("Ped", data.getDeviceName()); - assertEquals(-20, data.getTxPowerLevel()); - - assertTrue(data.getManufacturerSpecificData().get(0x00E0) != null); - assertArrayEquals(new byte[] { - 0x02, 0x15 }, data.getManufacturerSpecificData().get(0x00E0)); - - assertTrue(data.getServiceData().containsKey(uuid2)); - assertArrayEquals(new byte[] { - 0x50, 0x64 }, data.getServiceData().get(uuid2)); - } - - // Assert two byte arrays are equal. - private static void assertArrayEquals(byte[] expected, byte[] actual) { - if (!Arrays.equals(expected, actual)) { - fail("expected:<" + Arrays.toString(expected) + - "> but was:<" + Arrays.toString(actual) + ">"); - } - - } - - private static void assertMatchesAnyField(String record, BytesMatcher matcher) { - assertTrue(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record)) - .matchesAnyField(matcher)); - } - - private static void assertNotMatchesAnyField(String record, BytesMatcher matcher) { - assertFalse(ScanRecord.parseFromBytes(HexDump.hexStringToByteArray(record)) - .matchesAnyField(matcher)); - } -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java deleted file mode 100644 index 01d5c593bf27..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanResultTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.os.Parcel; -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -/** - * Unit test cases for Bluetooth LE scans. - * <p> - * To run this test, use adb shell am instrument -e class 'android.bluetooth.ScanResultTest' -w - * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' - */ -public class ScanResultTest extends TestCase { - - /** - * Test read and write parcel of ScanResult - */ - @SmallTest - public void testScanResultParceling() { - BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( - "01:02:03:04:05:06"); - byte[] scanRecord = new byte[] { - 1, 2, 3 }; - int rssi = -10; - long timestampMicros = 10000L; - - ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord), rssi, - timestampMicros); - Parcel parcel = Parcel.obtain(); - result.writeToParcel(parcel, 0); - // Need to reset parcel data position to the beginning. - parcel.setDataPosition(0); - ScanResult resultFromParcel = ScanResult.CREATOR.createFromParcel(parcel); - assertEquals(result, resultFromParcel); - } - -} diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanSettingsTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanSettingsTest.java deleted file mode 100644 index 7c42c3b46775..000000000000 --- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanSettingsTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth.le; - -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -/** - * Test for Bluetooth LE {@link ScanSettings}. - */ -public class ScanSettingsTest extends TestCase { - - @SmallTest - public void testCallbackType() { - ScanSettings.Builder builder = new ScanSettings.Builder(); - builder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES); - builder.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH); - builder.setCallbackType(ScanSettings.CALLBACK_TYPE_MATCH_LOST); - builder.setCallbackType( - ScanSettings.CALLBACK_TYPE_FIRST_MATCH | ScanSettings.CALLBACK_TYPE_MATCH_LOST); - try { - builder.setCallbackType( - ScanSettings.CALLBACK_TYPE_ALL_MATCHES | ScanSettings.CALLBACK_TYPE_MATCH_LOST); - fail("should have thrown IllegalArgumentException!"); - } catch (IllegalArgumentException e) { - // nothing to do - } - - try { - builder.setCallbackType( - ScanSettings.CALLBACK_TYPE_ALL_MATCHES | - ScanSettings.CALLBACK_TYPE_FIRST_MATCH); - fail("should have thrown IllegalArgumentException!"); - } catch (IllegalArgumentException e) { - // nothing to do - } - - try { - builder.setCallbackType( - ScanSettings.CALLBACK_TYPE_ALL_MATCHES | - ScanSettings.CALLBACK_TYPE_FIRST_MATCH | - ScanSettings.CALLBACK_TYPE_MATCH_LOST); - fail("should have thrown IllegalArgumentException!"); - } catch (IllegalArgumentException e) { - // nothing to do - } - - } -} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java index 74b6dbe76a16..93910b9c816e 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java @@ -39,6 +39,8 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import libcore.testing.io.TestIoUtils; + import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -52,8 +54,11 @@ public class BatteryUsageStatsProviderTest { private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; private static final long MINUTE_IN_MS = 60 * 1000; + private final File mHistoryDir = + TestIoUtils.createTemporaryDirectory(getClass().getSimpleName()); + @Rule - public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345) + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345, mHistoryDir) .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0); @Test diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java index 083090c54619..2c6f5fa1ae82 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java @@ -40,6 +40,7 @@ import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.mockito.stubbing.Answer; +import java.io.File; import java.util.Arrays; public class BatteryUsageStatsRule implements TestRule { @@ -57,14 +58,18 @@ public class BatteryUsageStatsRule implements TestRule { private boolean mScreenOn; public BatteryUsageStatsRule() { - this(0); + this(0, null); } public BatteryUsageStatsRule(long currentTime) { + this(currentTime, null); + } + + public BatteryUsageStatsRule(long currentTime, File historyDir) { Context context = InstrumentationRegistry.getContext(); mPowerProfile = spy(new PowerProfile(context, true /* forTest */)); mMockClocks.currentTime = currentTime; - mBatteryStats = new MockBatteryStatsImpl(mMockClocks); + mBatteryStats = new MockBatteryStatsImpl(mMockClocks, historyDir); mBatteryStats.setPowerProfile(mPowerProfile); mBatteryStats.onSystemReady(); } 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..11d20f2ebeb0 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; @@ -27,6 +29,7 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; import com.android.internal.power.MeasuredEnergyStats; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Queue; @@ -38,10 +41,16 @@ import java.util.concurrent.Future; public class MockBatteryStatsImpl extends BatteryStatsImpl { public BatteryStatsImpl.Clocks clocks; public boolean mForceOnBattery; + // The mNetworkStats will be used for both wifi and mobile categories private NetworkStats mNetworkStats; MockBatteryStatsImpl(Clocks clocks) { - super(clocks); + this(clocks, null); + } + + MockBatteryStatsImpl(Clocks clocks, File historyDirectory) { + super(clocks, historyDirectory); + this.clocks = mClocks; initTimersAndCounters(); @@ -105,10 +114,16 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { } @Override - protected NetworkStats readNetworkStatsLocked(String[] ifaces) { + protected NetworkStats readMobileNetworkStatsLocked( + @NonNull NetworkStatsManager networkStatsManager) { return mNetworkStats; } + @Override + protected NetworkStats readWifiNetworkStatsLocked( + @NonNull NetworkStatsManager networkStatsManager) { + return mNetworkStats; + } public MockBatteryStatsImpl setPowerProfile(PowerProfile powerProfile) { mPowerProfile = powerProfile; return this; 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/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index fefcebeaef20..be85fcc15ffa 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -390,6 +390,9 @@ applications that come with the platform <permission name="android.permission.SET_WALLPAPER" /> <permission name="android.permission.SET_WALLPAPER_COMPONENT" /> <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" /> + <!-- Permission required for CTS test - TrustTestCases --> + <permission name="android.permission.PROVIDE_TRUST_AGENT" /> + <permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" /> <!-- Permissions required for Incremental CTS tests --> <permission name="com.android.permission.USE_INSTALLER_V2"/> <permission name="android.permission.LOADER_USAGE_STATS"/> 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/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS new file mode 100644 index 000000000000..8446b37dbf06 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS @@ -0,0 +1,2 @@ +# window manager > wm shell > Split Screen +# Bug component: 928697 diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS new file mode 100644 index 000000000000..566acc87e42d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS @@ -0,0 +1,2 @@ +# window manager > wm shell > Bubbles +# Bug component: 555586 diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS new file mode 100644 index 000000000000..8446b37dbf06 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS @@ -0,0 +1,2 @@ +# window manager > wm shell > Split Screen +# Bug component: 928697 diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS new file mode 100644 index 000000000000..172e24bf4574 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS @@ -0,0 +1,2 @@ +# window manager > wm shell > Picture-In-Picture +# Bug component: 316251 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/AudioSystem.java b/media/java/android/media/AudioSystem.java index f0e42c0550ff..143b11f76f23 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -242,7 +242,8 @@ public class AudioSystem AUDIO_FORMAT_SBC, AUDIO_FORMAT_APTX, AUDIO_FORMAT_APTX_HD, - AUDIO_FORMAT_LDAC} + AUDIO_FORMAT_LDAC, + AUDIO_FORMAT_LC3} ) @Retention(RetentionPolicy.SOURCE) public @interface AudioFormatNativeEnumForBtCodec {} @@ -274,6 +275,7 @@ public class AudioSystem case AUDIO_FORMAT_APTX: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX; case AUDIO_FORMAT_APTX_HD: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD; case AUDIO_FORMAT_LDAC: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC; + case AUDIO_FORMAT_LC3: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3; default: Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat) + " for conversion to BT codec"); @@ -314,6 +316,8 @@ public class AudioSystem return AudioSystem.AUDIO_FORMAT_APTX_HD; case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC: return AudioSystem.AUDIO_FORMAT_LDAC; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3: + return AudioSystem.AUDIO_FORMAT_LC3; default: Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec) + " for conversion to audio format"); @@ -414,6 +418,8 @@ public class AudioSystem return "AUDIO_FORMAT_LHDC_LL"; case /* AUDIO_FORMAT_APTX_TWSP */ 0x2A000000: return "AUDIO_FORMAT_APTX_TWSP"; + case /* AUDIO_FORMAT_LC3 */ 0x2B000000: + return "AUDIO_FORMAT_LC3"; /* Aliases */ case /* AUDIO_FORMAT_PCM_16_BIT */ 0x1: 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/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 3c152fb68c0a..41c8887250ee 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -426,10 +426,30 @@ public final class MediaCodecInfo { /** @deprecated Use {@link #COLOR_Format32bitABGR8888}. */ public static final int COLOR_Format24BitABGR6666 = 43; - /** @hide - * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane - * followed by a Wx(H/2) CbCr plane. Each sample is represented by a 16-bit - * little-endian value, with the lower 6 bits set to zero. */ + /** + * P010 is 10-bit-per component 4:2:0 YCbCr semiplanar format. + * <p> + * This format uses 24 allocated bits per pixel with 15 bits of + * data per pixel. Chroma planes are subsampled by 2 both + * horizontally and vertically. Each chroma and luma component + * has 16 allocated bits in little-endian configuration with 10 + * MSB of actual data. + * + * <pre> + * byte byte + * <--------- i --------> | <------ i + 1 ------> + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | UNUSED | Y/Cb/Cr | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 0 5 6 7 0 7 + * bit + * </pre> + * + * Use this format with {@link Image}. This format corresponds + * to {@link android.graphics.ImageFormat#YCBCR_P010}. + * <p> + */ + @SuppressLint("AllUpper") public static final int COLOR_FormatYUVP010 = 54; /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */ @@ -439,6 +459,25 @@ public final class MediaCodecInfo { public static final int COLOR_FormatSurface = 0x7F000789; /** + * 64 bits per pixel RGBA color format, with 16-bit signed + * floating point red, green, blue, and alpha components. + * <p> + * + * <pre> + * byte byte byte byte + * <-- i -->|<- i+1 ->|<- i+2 ->|<- i+3 ->|<- i+4 ->|<- i+5 ->|<- i+6 ->|<- i+7 -> + * +---------+---------+-------------------+---------+---------+---------+---------+ + * | RED | GREEN | BLUE | ALPHA | + * +---------+---------+-------------------+---------+---------+---------+---------+ + * 0 7 0 7 0 7 0 7 0 7 0 7 0 7 0 7 + * </pre> + * + * This corresponds to {@link android.graphics.PixelFormat#RGBA_F16}. + */ + @SuppressLint("AllUpper") + public static final int COLOR_Format64bitABGRFloat = 0x7F000F16; + + /** * 32 bits per pixel RGBA color format, with 8-bit red, green, blue, and alpha components. * <p> * Using 32-bit little-endian representation, colors stored as Red 7:0, Green 15:8, @@ -456,6 +495,26 @@ public final class MediaCodecInfo { public static final int COLOR_Format32bitABGR8888 = 0x7F00A000; /** + * 32 bits per pixel RGBA color format, with 10-bit red, green, + * blue, and 2-bit alpha components. + * <p> + * Using 32-bit little-endian representation, colors stored as + * Red 9:0, Green 19:10, Blue 29:20, and Alpha 31:30. + * <pre> + * byte byte byte byte + * <------ i -----> | <---- i+1 ----> | <---- i+2 ----> | <---- i+3 -----> + * +-----------------+---+-------------+-------+---------+-----------+-----+ + * | RED | GREEN | BLUE |ALPHA| + * +-----------------+---+-------------+-------+---------+-----------+-----+ + * 0 7 0 1 2 7 0 3 4 7 0 5 6 7 + * </pre> + * + * This corresponds to {@link android.graphics.PixelFormat#RGBA_1010102}. + */ + @SuppressLint("AllUpper") + public static final int COLOR_Format32bitABGR2101010 = 0x7F00AAA2; + + /** * Flexible 12 bits per pixel, subsampled YUV color format with 8-bit chroma and luma * components. * <p> @@ -603,6 +662,18 @@ public final class MediaCodecInfo { public static final String FEATURE_QpBounds = "qp-bounds"; /** + * <b>video encoder only</b>: codec supports exporting encoding statistics. + * Encoders with this feature can provide the App clients with the encoding statistics + * information about the frame. + * The scope of encoding statistics is controlled by + * {@link MediaFormat#KEY_VIDEO_ENCODING_STATISTICS_LEVEL}. + * + * @see MediaFormat#KEY_VIDEO_ENCODING_STATISTICS_LEVEL + */ + @SuppressLint("AllUpper") // for consistency with other FEATURE_* constants + public static final String FEATURE_EncodingStatistics = "encoding-statistics"; + + /** * Query codec feature capabilities. * <p> * These features are supported to be used by the codec. These @@ -641,6 +712,7 @@ public final class MediaCodecInfo { new Feature(FEATURE_MultipleFrames, (1 << 1), false), new Feature(FEATURE_DynamicTimestamp, (1 << 2), false), new Feature(FEATURE_QpBounds, (1 << 3), false), + new Feature(FEATURE_EncodingStatistics, (1 << 4), false), // feature to exclude codec from REGULAR codec list new Feature(FEATURE_SpecialCodec, (1 << 30), false, true), }; diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 9bf0db52f66d..aa5c404800e8 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -1102,6 +1102,76 @@ public final class MediaFormat { public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min"; /** + * A key describing the level of encoding statistics information emitted from video encoder. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_ENCODING_STATISTICS_LEVEL = + "video-encoding-statistics-level"; + + /** + * Encoding Statistics Level None. + * Encoder generates no information about Encoding statistics. + */ + public static final int VIDEO_ENCODING_STATISTICS_LEVEL_NONE = 0; + + /** + * Encoding Statistics Level 1. + * Encoder generates {@link MediaFormat#KEY_PICTURE_TYPE} and + * {@link MediaFormat#KEY_VIDEO_QP_AVERAGE} for each frame. + */ + public static final int VIDEO_ENCODING_STATISTICS_LEVEL_1 = 1; + + /** @hide */ + @IntDef({ + VIDEO_ENCODING_STATISTICS_LEVEL_NONE, + VIDEO_ENCODING_STATISTICS_LEVEL_1, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface VideoEncodingStatisticsLevel {} + + /** + * A key describing the per-frame average block QP (Quantization Parameter). + * This is a part of a video 'Encoding Statistics' export feature. + * This value is emitted from video encoder for a video frame. + * The average value is rounded down (using floor()) to integer value. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_AVERAGE = "video-qp-average"; + + /** + * A key describing the picture type of the encoded frame. + * This is a part of a video 'Encoding Statistics' export feature. + * This value is emitted from video encoder for a video frame. + * + * The associated value is an integer. + */ + public static final String KEY_PICTURE_TYPE = "picture-type"; + + /** Picture Type is unknown. */ + public static final int PICTURE_TYPE_UNKNOWN = 0; + + /** Picture Type is I Frame. */ + public static final int PICTURE_TYPE_I = 1; + + /** Picture Type is P Frame. */ + public static final int PICTURE_TYPE_P = 2; + + /** Picture Type is B Frame. */ + public static final int PICTURE_TYPE_B = 3; + + /** @hide */ + @IntDef({ + PICTURE_TYPE_UNKNOWN, + PICTURE_TYPE_I, + PICTURE_TYPE_P, + PICTURE_TYPE_B, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PictureType {} + + /** * A key describing the audio session ID of the AudioTrack associated * to a tunneled video codec. * The associated value is an integer. diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index 3cf03417334b..86a94a9e0662 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -504,49 +504,51 @@ public class Ringtone { } private boolean playFallbackRingtone() { - if (mAudioManager.getStreamVolume(AudioAttributes.toLegacyStreamType(mAudioAttributes)) - != 0) { - int ringtoneType = RingtoneManager.getDefaultType(mUri); - if (ringtoneType == -1 || - RingtoneManager.getActualDefaultRingtoneUri(mContext, ringtoneType) != null) { - // Default ringtone, try fallback ringtone. - try { - AssetFileDescriptor afd = mContext.getResources().openRawResourceFd( - com.android.internal.R.raw.fallbackring); - if (afd != null) { - mLocalPlayer = new MediaPlayer(); - if (afd.getDeclaredLength() < 0) { - mLocalPlayer.setDataSource(afd.getFileDescriptor()); - } else { - mLocalPlayer.setDataSource(afd.getFileDescriptor(), - afd.getStartOffset(), - afd.getDeclaredLength()); - } - mLocalPlayer.setAudioAttributes(mAudioAttributes); - synchronized (mPlaybackSettingsLock) { - applyPlaybackProperties_sync(); - } - if (mVolumeShaperConfig != null) { - mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig); - } - mLocalPlayer.prepare(); - startLocalPlayer(); - afd.close(); - return true; - } else { - Log.e(TAG, "Could not load fallback ringtone"); - } - } catch (IOException ioe) { - destroyLocalPlayer(); - Log.e(TAG, "Failed to open fallback ringtone"); - } catch (NotFoundException nfe) { - Log.e(TAG, "Fallback ringtone does not exist"); - } + int streamType = AudioAttributes.toLegacyStreamType(mAudioAttributes); + if (mAudioManager.getStreamVolume(streamType) == 0) { + return false; + } + int ringtoneType = RingtoneManager.getDefaultType(mUri); + if (ringtoneType != -1 && + RingtoneManager.getActualDefaultRingtoneUri(mContext, ringtoneType) == null) { + Log.w(TAG, "not playing fallback for " + mUri); + return false; + } + // Default ringtone, try fallback ringtone. + try { + AssetFileDescriptor afd = mContext.getResources().openRawResourceFd( + com.android.internal.R.raw.fallbackring); + if (afd == null) { + Log.e(TAG, "Could not load fallback ringtone"); + return false; + } + mLocalPlayer = new MediaPlayer(); + if (afd.getDeclaredLength() < 0) { + mLocalPlayer.setDataSource(afd.getFileDescriptor()); } else { - Log.w(TAG, "not playing fallback for " + mUri); + mLocalPlayer.setDataSource(afd.getFileDescriptor(), + afd.getStartOffset(), + afd.getDeclaredLength()); } + mLocalPlayer.setAudioAttributes(mAudioAttributes); + synchronized (mPlaybackSettingsLock) { + applyPlaybackProperties_sync(); + } + if (mVolumeShaperConfig != null) { + mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig); + } + mLocalPlayer.prepare(); + startLocalPlayer(); + afd.close(); + } catch (IOException ioe) { + destroyLocalPlayer(); + Log.e(TAG, "Failed to open fallback ringtone"); + return false; + } catch (NotFoundException nfe) { + Log.e(TAG, "Fallback ringtone does not exist"); + return false; } - return false; + return true; } void setTitle(String title) { 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/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/Android.bp b/packages/ConnectivityT/framework-t/Android.bp index 90bb76a87d46..223bdcdd9c95 100644 --- a/packages/ConnectivityT/framework-t/Android.bp +++ b/packages/ConnectivityT/framework-t/Android.bp @@ -129,6 +129,11 @@ filegroup { "src/android/net/EthernetNetworkSpecifier.java", "src/android/net/IEthernetManager.aidl", "src/android/net/IEthernetServiceListener.aidl", + "src/android/net/IInternalNetworkManagementListener.aidl", + "src/android/net/InternalNetworkUpdateRequest.java", + "src/android/net/InternalNetworkUpdateRequest.aidl", + "src/android/net/InternalNetworkManagementException.java", + "src/android/net/InternalNetworkManagementException.aidl", "src/android/net/ITetheredInterfaceCallback.aidl", ], path: "src", @@ -156,8 +161,18 @@ filegroup { ":framework-connectivity-ethernet-sources", ":framework-connectivity-ipsec-sources", ":framework-connectivity-netstats-sources", + ], + visibility: ["//frameworks/base"], +} + +filegroup { + name: "framework-connectivity-tiramisu-updatable-sources", + srcs: [ ":framework-connectivity-nsd-sources", ":framework-connectivity-tiramisu-internal-sources", ], - visibility: ["//frameworks/base"], + visibility: [ + "//frameworks/base", + "//packages/modules/Connectivity:__subpackages__", + ], } 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..2b6570a6ecb0 100644 --- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java +++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java @@ -545,9 +545,18 @@ 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) { + void startHistoryUidEnumeration(int uid, int tag, int state) { mHistory = null; try { mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid, @@ -562,6 +571,20 @@ public final class NetworkStats implements AutoCloseable { } /** + * Collects history results for network and resets history enumeration index. + */ + void startHistoryDeviceEnumeration() { + try { + mHistory = mSession.getHistoryIntervalForNetwork( + mTemplate, NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp); + } catch (RemoteException e) { + Log.w(TAG, e); + mHistory = null; + } + mEnumerationIndex = 0; + } + + /** * Starts uid enumeration for current user. * @throws RemoteException */ 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..28f930ff1207 100644 --- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java +++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java @@ -17,7 +17,11 @@ package android.app.usage; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import android.Manifest; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -36,14 +40,11 @@ import android.net.NetworkStack; import android.net.NetworkStateSnapshot; import android.net.NetworkTemplate; import android.net.UnderlyingNetworkInfo; +import android.net.netstats.IUsageCallback; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.net.netstats.provider.NetworkStatsProvider; -import android.os.Binder; import android.os.Build; import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; import android.os.RemoteException; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -54,6 +55,7 @@ import com.android.net.module.util.NetworkIdentityUtils; import java.util.List; import java.util.Objects; +import java.util.concurrent.Executor; /** * Provides access to network usage history and statistics. Usage data is collected in @@ -123,6 +125,19 @@ public class NetworkStatsManager { private final Context mContext; private final INetworkStatsService mService; + /** + * Type constants for reading different types of Data Usage. + * @hide + */ + // @SystemApi(client = MODULE_LIBRARIES) + public static final String PREFIX_DEV = "dev"; + /** @hide */ + public static final String PREFIX_XT = "xt"; + /** @hide */ + public static final String PREFIX_UID = "uid"; + /** @hide */ + public static final String PREFIX_UID_TAG = "uid_tag"; + /** @hide */ public static final int FLAG_POLL_ON_OPEN = 1 << 0; /** @hide */ @@ -142,6 +157,25 @@ public class NetworkStatsManager { } /** @hide */ + public INetworkStatsService getBinder() { + return mService; + } + + /** + * 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 + */ + // The system will ignore any non-default values for non-privileged + // processes, so processes that don't hold the appropriate permissions + // can make no use of this API. + @SystemApi(client = MODULE_LIBRARIES) + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_STACK}) public void setPollOnOpen(boolean pollOnOpen) { if (pollOnOpen) { mFlags |= FLAG_POLL_ON_OPEN; @@ -195,9 +229,10 @@ public class NetworkStatsManager { */ @NonNull @WorkerThread - // @SystemApi(client = MODULE_LIBRARIES) + @SystemApi(client = MODULE_LIBRARIES) public Bucket querySummaryForDevice(@NonNull NetworkTemplate template, long startTime, long endTime) { + Objects.requireNonNull(template); try { NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); @@ -368,11 +403,12 @@ public class NetworkStatsManager { * @return Statistics which is described above. * @hide */ - @Nullable - // @SystemApi(client = MODULE_LIBRARIES) + @NonNull + @SystemApi(client = MODULE_LIBRARIES) @WorkerThread public NetworkStats querySummary(@NonNull NetworkTemplate template, long startTime, long endTime) throws SecurityException { + Objects.requireNonNull(template); try { NetworkStats result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); @@ -385,6 +421,77 @@ 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 { + Objects.requireNonNull(template); + 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 usage statistics details for networks matching a given {@link NetworkTemplate}. + * + * Result is not aggregated over time. This means buckets' start and + * end timestamps will be between 'startTime' and 'endTime' parameters. + * <p>Only includes buckets whose entire time period is included between + * startTime and endTime. Doesn't interpolate or return partial buckets. + * Since bucket length is in the order of hours, this + * method cannot be used to measure data usage on a fine grained time scale. + * 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 java.lang.System#currentTimeMillis}. + * @param endTime End of period, in milliseconds since the Unix epoch, see + * {@link java.lang.System#currentTimeMillis}. + * @return Statistics which is described above. + * @hide + */ + @NonNull + @SystemApi(client = MODULE_LIBRARIES) + @WorkerThread + public NetworkStats queryDetailsForDevice(@NonNull NetworkTemplate template, + long startTime, long endTime) { + Objects.requireNonNull(template); + try { + final NetworkStats result = + new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); + result.startHistoryDeviceEnumeration(); + 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. * @@ -450,7 +557,8 @@ public class NetworkStatsManager { * @param endTime End of period. Defined in terms of "Unix time", see * {@link java.lang.System#currentTimeMillis}. * @param uid UID of app - * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for no tags. + * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data + * across all the tags. * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate * traffic from all states. * @return Statistics object or null if an error happened during statistics collection. @@ -465,21 +573,52 @@ public class NetworkStatsManager { return queryDetailsForUidTagState(template, startTime, endTime, uid, tag, state); } - /** @hide */ - public NetworkStats queryDetailsForUidTagState(NetworkTemplate template, + /** + * Query network usage statistics details for a given template, uid, tag, and state. + * + * Only usable for uids belonging to calling user. Result is not aggregated over time. + * This means buckets' start and end timestamps are going to be between 'startTime' and + * 'endTime' parameters. The uid is going to be the same as the 'uid' parameter, the tag + * the same as the 'tag' parameter, and the state the same as the 'state' parameter. + * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, + * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and + * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}. + * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't + * interpolate across partial buckets. Since bucket length is in the order of hours, this + * method cannot be used to measure data usage on a fine grained time scale. + * 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 java.lang.System#currentTimeMillis}. + * @param endTime End of period, in milliseconds since the Unix epoch, see + * {@link java.lang.System#currentTimeMillis}. + * @param uid UID of app + * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data + * across all the tags. + * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate + * traffic from all states. + * @return Statistics which is described above. + * @hide + */ + @NonNull + @SystemApi(client = MODULE_LIBRARIES) + @WorkerThread + public NetworkStats queryDetailsForUidTagState(@NonNull NetworkTemplate template, long startTime, long endTime, int uid, int tag, int state) throws SecurityException { - - NetworkStats result; + Objects.requireNonNull(template); try { - result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); - result.startHistoryEnumeration(uid, tag, state); + final NetworkStats result = new NetworkStats( + mContext, template, mFlags, startTime, endTime, mService); + result.startHistoryUidEnumeration(uid, tag, state); + return result; } catch (RemoteException e) { Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag + " state=" + state, e); - return null; + e.rethrowFromSystemServer(); } - return result; + return null; // To make the compiler happy. } /** @@ -535,26 +674,84 @@ public class NetworkStatsManager { return result; } - /** @hide */ - public void registerUsageCallback(NetworkTemplate template, int networkType, - long thresholdBytes, UsageCallback callback, @Nullable Handler handler) { - Objects.requireNonNull(callback, "UsageCallback cannot be null"); + /** + * Query realtime mobile network usage statistics. + * + * Return a snapshot of current UID network statistics, as it applies + * to the mobile radios of the device. The snapshot will include any + * tethering traffic, video calling data usage and count of + * network operations set by {@link TrafficStats#incrementOperationCount} + * made over a mobile radio. + * The snapshot will not include any statistics that cannot be seen by + * the kernel, e.g. statistics reported by {@link NetworkStatsProvider}s. + * + * @hide + */ + @SystemApi + @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) + @NonNull public android.net.NetworkStats getMobileUidStats() { + try { + return mService.getUidStatsForTransport(TRANSPORT_CELLULAR); + } catch (RemoteException e) { + if (DBG) Log.d(TAG, "Remote exception when get Mobile uid stats"); + throw e.rethrowFromSystemServer(); + } + } - final Looper looper; - if (handler == null) { - looper = Looper.myLooper(); - } else { - looper = handler.getLooper(); + /** + * Query realtime Wi-Fi network usage statistics. + * + * Return a snapshot of current UID network statistics, as it applies + * to the Wi-Fi radios of the device. The snapshot will include any + * tethering traffic, video calling data usage and count of + * network operations set by {@link TrafficStats#incrementOperationCount} + * made over a Wi-Fi radio. + * The snapshot will not include any statistics that cannot be seen by + * the kernel, e.g. statistics reported by {@link NetworkStatsProvider}s. + * + * @hide + */ + @SystemApi + @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) + @NonNull public android.net.NetworkStats getWifiUidStats() { + try { + return mService.getUidStatsForTransport(TRANSPORT_WIFI); + } catch (RemoteException e) { + if (DBG) Log.d(TAG, "Remote exception when get WiFi uid stats"); + throw e.rethrowFromSystemServer(); } + } + + /** + * Registers to receive notifications about data usage on specified networks. + * + * <p>The callbacks will continue to be called as long as the process is alive or + * {@link #unregisterUsageCallback} is called. + * + * @param template Template used to match networks. See {@link NetworkTemplate}. + * @param thresholdBytes Threshold in bytes to be notified on. The provided value that lower + * than 2MiB will be clamped for non-privileged callers. + * @param executor The executor on which callback will be invoked. The provided {@link Executor} + * must run callback sequentially, otherwise the order of callbacks cannot be + * guaranteed. + * @param callback The {@link UsageCallback} that the system will call when data usage + * has exceeded the specified threshold. + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public void registerUsageCallback(@NonNull NetworkTemplate template, long thresholdBytes, + @NonNull @CallbackExecutor Executor executor, @NonNull UsageCallback callback) { + Objects.requireNonNull(template, "NetworkTemplate cannot be null"); + Objects.requireNonNull(callback, "UsageCallback cannot be null"); + Objects.requireNonNull(executor, "Executor cannot be null"); - DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET, + final DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET, template, thresholdBytes); try { - CallbackHandler callbackHandler = new CallbackHandler(looper, networkType, - template.getSubscriberId(), callback); + final UsageCallbackWrapper callbackWrapper = + new UsageCallbackWrapper(executor, callback); callback.request = mService.registerUsageCallback( - mContext.getOpPackageName(), request, new Messenger(callbackHandler), - new Binder()); + mContext.getOpPackageName(), request, callbackWrapper); if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request); if (callback.request == null) { @@ -607,12 +804,15 @@ public class NetworkStatsManager { NetworkTemplate template = createTemplate(networkType, subscriberId); if (DBG) { Log.d(TAG, "registerUsageCallback called with: {" - + " networkType=" + networkType - + " subscriberId=" + subscriberId - + " thresholdBytes=" + thresholdBytes - + " }"); + + " networkType=" + networkType + + " subscriberId=" + subscriberId + + " thresholdBytes=" + thresholdBytes + + " }"); } - registerUsageCallback(template, networkType, thresholdBytes, callback, handler); + + final Executor executor = handler == null ? r -> r.run() : r -> handler.post(r); + + registerUsageCallback(template, thresholdBytes, executor, callback); } /** @@ -637,6 +837,26 @@ public class NetworkStatsManager { * Base class for usage callbacks. Should be extended by applications wanting notifications. */ public static abstract class UsageCallback { + /** + * Called when data usage has reached the given threshold. + * + * Called by {@code NetworkStatsService} when the registered threshold is reached. + * If a caller implements {@link #onThresholdReached(NetworkTemplate)}, the system + * will not call {@link #onThresholdReached(int, String)}. + * + * @param template The {@link NetworkTemplate} that associated with this callback. + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public void onThresholdReached(@NonNull NetworkTemplate template) { + // Backward compatibility for those who didn't override this function. + final int networkType = networkTypeForTemplate(template); + if (networkType != ConnectivityManager.TYPE_NONE) { + final String subscriberId = template.getSubscriberIds().isEmpty() ? null + : template.getSubscriberIds().iterator().next(); + onThresholdReached(networkType, subscriberId); + } + } /** * Called when data usage has reached the given threshold. @@ -647,6 +867,25 @@ public class NetworkStatsManager { * @hide used for internal bookkeeping */ private DataUsageRequest request; + + /** + * Get network type from a template if feasible. + * + * @param template the target {@link NetworkTemplate}. + * @return legacy network type, only supports for the types which is already supported in + * {@link #registerUsageCallback(int, String, long, UsageCallback, Handler)}. + * {@link ConnectivityManager#TYPE_NONE} for other types. + */ + private static int networkTypeForTemplate(@NonNull NetworkTemplate template) { + switch (template.getMatchRule()) { + case NetworkTemplate.MATCH_MOBILE: + return ConnectivityManager.TYPE_MOBILE; + case NetworkTemplate.MATCH_WIFI: + return ConnectivityManager.TYPE_WIFI; + default: + return ConnectivityManager.TYPE_NONE; + } + } } /** @@ -765,43 +1004,111 @@ public class NetworkStatsManager { } } - private static class CallbackHandler extends Handler { - private final int mNetworkType; - private final String mSubscriberId; - private UsageCallback mCallback; + private static class UsageCallbackWrapper extends IUsageCallback.Stub { + // Null if unregistered. + private volatile UsageCallback mCallback; - CallbackHandler(Looper looper, int networkType, String subscriberId, - UsageCallback callback) { - super(looper); - mNetworkType = networkType; - mSubscriberId = subscriberId; + private final Executor mExecutor; + + UsageCallbackWrapper(@NonNull Executor executor, @NonNull UsageCallback callback) { mCallback = callback; + mExecutor = executor; } @Override - public void handleMessage(Message message) { - DataUsageRequest request = - (DataUsageRequest) getObject(message, DataUsageRequest.PARCELABLE_KEY); - - switch (message.what) { - case CALLBACK_LIMIT_REACHED: { - if (mCallback != null) { - mCallback.onThresholdReached(mNetworkType, mSubscriberId); - } else { - Log.e(TAG, "limit reached with released callback for " + request); - } - break; - } - case CALLBACK_RELEASED: { - if (DBG) Log.d(TAG, "callback released for " + request); - mCallback = null; - break; - } + public void onThresholdReached(DataUsageRequest request) { + // Copy it to a local variable in case mCallback changed inside the if condition. + final UsageCallback callback = mCallback; + if (callback != null) { + mExecutor.execute(() -> callback.onThresholdReached(request.template)); + } else { + Log.e(TAG, "onThresholdReached with released callback for " + request); } } - private static Object getObject(Message msg, String key) { - return msg.getData().getParcelable(key); + @Override + public void onCallbackReleased(DataUsageRequest request) { + if (DBG) Log.d(TAG, "callback released for " + request); + mCallback = null; + } + } + + /** + * Mark given UID as being in foreground for stats purposes. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @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(); + } + } + + /** + * Set default value of global alert bytes, the value will be clamped to [128kB, 2MB]. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + Manifest.permission.NETWORK_STACK}) + public void setDefaultGlobalAlert(long alertBytes) { + try { + // TODO: Sync internal naming with the API surface. + mService.advisePersistThreshold(alertBytes); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Force update of statistics. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @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. + * + * Asynchronicity notes : because traffic may be happening on the device at the same time, it + * doesn't make sense to wait for the warning and limit to be set – a caller still wouldn't + * know when exactly it was effective. All that can matter is that it's done quickly. Also, + * this method can't fail, so there is no status to return. All providers will see the new + * values soon. + * As such, this method returns immediately and sends the warning and limit to all providers + * as soon as possible through a one-way binder call. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @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/EthernetNetworkSpecifier.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java index 62c576144221..925d12b574a6 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java +++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java @@ -23,8 +23,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; -import com.android.internal.util.Preconditions; - import java.util.Objects; /** @@ -47,7 +45,9 @@ public final class EthernetNetworkSpecifier extends NetworkSpecifier implements * @param interfaceName Name of the ethernet interface the specifier refers to. */ public EthernetNetworkSpecifier(@NonNull String interfaceName) { - Preconditions.checkStringNotEmpty(interfaceName); + if (TextUtils.isEmpty(interfaceName)) { + throw new IllegalArgumentException(); + } mInterfaceName = interfaceName; } diff --git a/core/java/android/net/IInternalNetworkManagementListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl index 69cde3bd14e8..69cde3bd14e8 100644 --- a/core/java/android/net/IInternalNetworkManagementListener.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl index 12937b5cb2c7..efe626d3c939 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl @@ -24,6 +24,7 @@ import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.UnderlyingNetworkInfo; +import android.net.netstats.IUsageCallback; import android.net.netstats.provider.INetworkStatsProvider; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.os.IBinder; @@ -49,14 +50,8 @@ interface INetworkStatsService { @UnsupportedAppUsage NetworkStats getDataLayerSnapshotForUid(int uid); - /** Get a detailed snapshot of stats since boot for all UIDs. - * - * <p>Results will not always be limited to stats on requiredIfaces when specified: stats for - * interfaces stacked on the specified interfaces, or for interfaces on which the specified - * interfaces are stacked on, will also be included. - * @param requiredIfaces Interface names to get data for, or {@link NetworkStats#INTERFACES_ALL}. - */ - NetworkStats getDetailedUidStats(in String[] requiredIfaces); + /** Get the transport NetworkStats for all UIDs since boot. */ + NetworkStats getUidStatsForTransport(int transport); /** Return set of any ifaces associated with mobile networks since boot. */ @UnsupportedAppUsage @@ -77,7 +72,7 @@ interface INetworkStatsService { /** Registers a callback on data usage. */ DataUsageRequest registerUsageCallback(String callingPackage, - in DataUsageRequest request, in Messenger messenger, in IBinder binder); + in DataUsageRequest request, in IUsageCallback callback); /** Unregisters a callback on data usage. */ void unregisterUsageRequest(in DataUsageRequest request); @@ -94,4 +89,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..ab70be826f8e 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl @@ -32,6 +32,11 @@ interface INetworkStatsSession { /** Return historical network layer stats for traffic that matches template. */ @UnsupportedAppUsage NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields); + /** + * Return historical network layer stats for traffic that matches template, start and end + * timestamp. + */ + NetworkStatsHistory getHistoryIntervalForNetwork(in NetworkTemplate template, int fields, long start, long end); /** * Return network layer usage summary per UID for traffic that matches template. @@ -46,6 +51,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/core/java/android/net/InternalNetworkManagementException.aidl b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl index dcce706989f6..dcce706989f6 100644 --- a/core/java/android/net/InternalNetworkManagementException.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl diff --git a/core/java/android/net/InternalNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java index 7f4e403f2259..7f4e403f2259 100644 --- a/core/java/android/net/InternalNetworkManagementException.java +++ b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java diff --git a/core/java/android/net/InternalNetworkUpdateRequest.aidl b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl index da00cb97afb4..da00cb97afb4 100644 --- a/core/java/android/net/InternalNetworkUpdateRequest.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl diff --git a/core/java/android/net/InternalNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java index 6f093835fb08..f42c4b7c420d 100644 --- a/core/java/android/net/InternalNetworkUpdateRequest.java +++ b/packages/ConnectivityT/framework-t/src/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/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java index a84e7a9c6344..10a22ac360b1 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java +++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java @@ -343,7 +343,7 @@ public final class IpSecAlgorithm implements Parcelable { // Load and validate the optional algorithm resource. Undefined or duplicate algorithms in // the resource are not allowed. final String[] resourceAlgos = systemResources.getStringArray( - com.android.internal.R.array.config_optionalIpSecAlgorithms); + android.R.array.config_optionalIpSecAlgorithms); for (String str : resourceAlgos) { if (!ALGO_TO_REQUIRED_FIRST_SDK.containsKey(str) || !enabledAlgos.add(str)) { // This error should be caught by CTS and never be thrown to API callers diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java index 0d15dffae92d..a423783bc1ca 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,8 +26,8 @@ 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.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -41,6 +42,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 +91,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. * @@ -981,6 +989,29 @@ public final class IpSecManager { } /** + * @hide + */ + public IpSecTransformResponse createTransform(IpSecConfig config, IBinder binder, + String callingPackage) { + try { + return mService.createTransform(config, binder, callingPackage); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + public void deleteTransform(int resourceId) { + try { + mService.deleteTransform(resourceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Construct an instance of IpSecManager within an application context. * * @param context the application context for this manager diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java index 36199a046cfc..68ae5de4ee70 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java +++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java @@ -26,9 +26,6 @@ import android.annotation.SystemApi; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.util.Log; @@ -93,16 +90,9 @@ public final class IpSecTransform implements AutoCloseable { mResourceId = INVALID_RESOURCE_ID; } - private IIpSecService getIpSecService() { - IBinder b = ServiceManager.getService(android.content.Context.IPSEC_SERVICE); - if (b == null) { - throw new RemoteException("Failed to connect to IpSecService") - .rethrowAsRuntimeException(); - } - - return IIpSecService.Stub.asInterface(b); + private IpSecManager getIpSecManager(Context context) { + return context.getSystemService(IpSecManager.class); } - /** * Checks the result status and throws an appropriate exception if the status is not Status.OK. */ @@ -130,8 +120,7 @@ public final class IpSecTransform implements AutoCloseable { IpSecManager.SpiUnavailableException { synchronized (this) { try { - IIpSecService svc = getIpSecService(); - IpSecTransformResponse result = svc.createTransform( + IpSecTransformResponse result = getIpSecManager(mContext).createTransform( mConfig, new Binder(), mContext.getOpPackageName()); int status = result.status; checkResultStatus(status); @@ -140,8 +129,6 @@ public final class IpSecTransform implements AutoCloseable { mCloseGuard.open("build"); } catch (ServiceSpecificException e) { throw IpSecManager.rethrowUncheckedExceptionFromServiceSpecificException(e); - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); } } @@ -177,10 +164,7 @@ public final class IpSecTransform implements AutoCloseable { return; } try { - IIpSecService svc = getIpSecService(); - svc.deleteTransform(mResourceId); - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); + getIpSecManager(mContext).deleteTransform(mResourceId); } catch (Exception e) { // On close we swallow all random exceptions since failure to close is not // actionable by the user. diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java index 8f1115e065dd..77fc17192c97 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java @@ -16,18 +16,29 @@ package android.net; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; +import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.content.Context; import android.net.wifi.WifiInfo; import android.service.NetworkIdentityProto; -import android.telephony.Annotation.NetworkType; +import android.telephony.Annotation; +import android.telephony.TelephonyManager; import android.util.proto.ProtoOutputStream; +import com.android.net.module.util.CollectionUtils; import com.android.net.module.util.NetworkCapabilitiesUtils; import com.android.net.module.util.NetworkIdentityUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Objects; @@ -37,11 +48,24 @@ import java.util.Objects; * * @hide */ -public class NetworkIdentity implements Comparable<NetworkIdentity> { +@SystemApi(client = MODULE_LIBRARIES) +public class NetworkIdentity { private static final String TAG = "NetworkIdentity"; + /** @hide */ + // TODO: Remove this after migrating all callers to use + // {@link NetworkTemplate#NETWORK_TYPE_ALL} instead. public static final int SUBTYPE_COMBINED = -1; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "OEM_MANAGED_" }, flag = true, value = { + NetworkTemplate.OEM_MANAGED_NO, + NetworkTemplate.OEM_MANAGED_PAID, + NetworkTemplate.OEM_MANAGED_PRIVATE + }) + public @interface OemManaged{} + /** * Network has no {@code NetworkCapabilities#NET_CAPABILITY_OEM_*}. * @hide @@ -51,29 +75,32 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { * Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID}. * @hide */ - public static final int OEM_PAID = 0x1; + public static final int OEM_PAID = 1 << 0; /** * Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE}. * @hide */ - public static final int OEM_PRIVATE = 0x2; + public static final int OEM_PRIVATE = 1 << 1; + + private static final long SUPPORTED_OEM_MANAGED_TYPES = OEM_PAID | OEM_PRIVATE; final int mType; - final int mSubType; + final int mRatType; final String mSubscriberId; - final String mNetworkId; + final String mWifiNetworkKey; final boolean mRoaming; final boolean mMetered; final boolean mDefaultNetwork; final int mOemManaged; + /** @hide */ public NetworkIdentity( - int type, int subType, String subscriberId, String networkId, boolean roaming, - boolean metered, boolean defaultNetwork, int oemManaged) { + int type, int ratType, @Nullable String subscriberId, @Nullable String wifiNetworkKey, + boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged) { mType = type; - mSubType = subType; + mRatType = ratType; mSubscriberId = subscriberId; - mNetworkId = networkId; + mWifiNetworkKey = wifiNetworkKey; mRoaming = roaming; mMetered = metered; mDefaultNetwork = defaultNetwork; @@ -82,7 +109,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { @Override public int hashCode() { - return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered, + return Objects.hash(mType, mRatType, mSubscriberId, mWifiNetworkKey, mRoaming, mMetered, mDefaultNetwork, mOemManaged); } @@ -90,9 +117,9 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { public boolean equals(@Nullable Object obj) { if (obj instanceof NetworkIdentity) { final NetworkIdentity ident = (NetworkIdentity) obj; - return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming + return mType == ident.mType && mRatType == ident.mRatType && mRoaming == ident.mRoaming && Objects.equals(mSubscriberId, ident.mSubscriberId) - && Objects.equals(mNetworkId, ident.mNetworkId) + && Objects.equals(mWifiNetworkKey, ident.mWifiNetworkKey) && mMetered == ident.mMetered && mDefaultNetwork == ident.mDefaultNetwork && mOemManaged == ident.mOemManaged; @@ -104,18 +131,18 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { public String toString() { final StringBuilder builder = new StringBuilder("{"); builder.append("type=").append(mType); - builder.append(", subType="); - if (mSubType == SUBTYPE_COMBINED) { + builder.append(", ratType="); + if (mRatType == NETWORK_TYPE_ALL) { builder.append("COMBINED"); } else { - builder.append(mSubType); + builder.append(mRatType); } if (mSubscriberId != null) { builder.append(", subscriberId=") .append(NetworkIdentityUtils.scrubSubscriberId(mSubscriberId)); } - if (mNetworkId != null) { - builder.append(", networkId=").append(mNetworkId); + if (mWifiNetworkKey != null) { + builder.append(", wifiNetworkKey=").append(mWifiNetworkKey); } if (mRoaming) { builder.append(", ROAMING"); @@ -153,18 +180,14 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } } + /** @hide */ public void dumpDebug(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); proto.write(NetworkIdentityProto.TYPE, mType); - // Not dumping mSubType, subtypes are no longer supported. + // TODO: dump mRatType as well. - if (mSubscriberId != null) { - proto.write(NetworkIdentityProto.SUBSCRIBER_ID, - NetworkIdentityUtils.scrubSubscriberId(mSubscriberId)); - } - proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId); proto.write(NetworkIdentityProto.ROAMING, mRoaming); proto.write(NetworkIdentityProto.METERED, mMetered); proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork); @@ -173,77 +196,99 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { proto.end(start); } + /** Get the network type of this instance. */ public int getType() { return mType; } - public int getSubType() { - return mSubType; + /** Get the Radio Access Technology(RAT) type of this instance. */ + public int getRatType() { + return mRatType; } + /** Get the Subscriber Id of this instance. */ + @Nullable public String getSubscriberId() { return mSubscriberId; } - public String getNetworkId() { - return mNetworkId; + /** Get the Wifi Network Key of this instance. See {@link WifiInfo#getNetworkKey()}. */ + @Nullable + public String getWifiNetworkKey() { + return mWifiNetworkKey; } + /** @hide */ + // TODO: Remove this function after all callers are removed. public boolean getRoaming() { return mRoaming; } + /** Return whether this network is roaming. */ + public boolean isRoaming() { + return mRoaming; + } + + /** @hide */ + // TODO: Remove this function after all callers are removed. public boolean getMetered() { return mMetered; } + /** Return whether this network is metered. */ + public boolean isMetered() { + return mMetered; + } + + /** @hide */ + // TODO: Remove this function after all callers are removed. public boolean getDefaultNetwork() { return mDefaultNetwork; } + /** Return whether this network is the default network. */ + public boolean isDefaultNetwork() { + return mDefaultNetwork; + } + + /** Get the OEM managed type of this instance. */ public int getOemManaged() { return mOemManaged; } /** - * Build a {@link NetworkIdentity} from the given {@link NetworkStateSnapshot} and - * {@code subType}, assuming that any mobile networks are using the current IMSI. - * The subType if applicable, should be set as one of the TelephonyManager.NETWORK_TYPE_* - * constants, or {@link android.telephony.TelephonyManager#NETWORK_TYPE_UNKNOWN} if not. + * Assemble a {@link NetworkIdentity} from the passed arguments. + * + * This methods builds an identity based on the capabilities of the network in the + * snapshot and other passed arguments. The identity is used as a key to record data usage. + * + * @param snapshot the snapshot of network state. See {@link NetworkStateSnapshot}. + * @param defaultNetwork whether the network is a default network. + * @param ratType the Radio Access Technology(RAT) type of the network. Or + * {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if not applicable. + * See {@code TelephonyManager.NETWORK_TYPE_*}. + * @hide + * @deprecated See {@link NetworkIdentity.Builder}. */ + // TODO: Remove this after all callers are migrated to use new Api. + @Deprecated + @NonNull public static NetworkIdentity buildNetworkIdentity(Context context, - NetworkStateSnapshot snapshot, boolean defaultNetwork, @NetworkType int subType) { - final int legacyType = snapshot.getLegacyType(); - - final String subscriberId = snapshot.getSubscriberId(); - String networkId = null; - boolean roaming = !snapshot.getNetworkCapabilities().hasCapability( - NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); - boolean metered = !(snapshot.getNetworkCapabilities().hasCapability( - NetworkCapabilities.NET_CAPABILITY_NOT_METERED) - || snapshot.getNetworkCapabilities().hasCapability( - NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)); - - final int oemManaged = getOemBitfield(snapshot.getNetworkCapabilities()); - - if (legacyType == TYPE_WIFI) { - final TransportInfo transportInfo = snapshot.getNetworkCapabilities() - .getTransportInfo(); - if (transportInfo instanceof WifiInfo) { - final WifiInfo info = (WifiInfo) transportInfo; - networkId = info != null ? info.getCurrentNetworkKey() : null; - } + @NonNull NetworkStateSnapshot snapshot, + boolean defaultNetwork, @Annotation.NetworkType int ratType) { + final NetworkIdentity.Builder builder = new NetworkIdentity.Builder() + .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork); + if (snapshot.getLegacyType() == TYPE_MOBILE && ratType != NETWORK_TYPE_ALL) { + builder.setRatType(ratType); } - - return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered, - defaultNetwork, oemManaged); + return builder.build(); } /** * Builds a bitfield of {@code NetworkIdentity.OEM_*} based on {@link NetworkCapabilities}. * @hide */ - public static int getOemBitfield(NetworkCapabilities nc) { + public static int getOemBitfield(@NonNull NetworkCapabilities nc) { int oemManaged = OEM_NONE; if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)) { @@ -256,30 +301,266 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { return oemManaged; } - @Override - public int compareTo(NetworkIdentity another) { - int res = Integer.compare(mType, another.mType); + /** @hide */ + public static int compare(@NonNull NetworkIdentity left, @NonNull NetworkIdentity right) { + Objects.requireNonNull(right); + int res = Integer.compare(left.mType, right.mType); if (res == 0) { - res = Integer.compare(mSubType, another.mSubType); + res = Integer.compare(left.mRatType, right.mRatType); } - if (res == 0 && mSubscriberId != null && another.mSubscriberId != null) { - res = mSubscriberId.compareTo(another.mSubscriberId); + if (res == 0 && left.mSubscriberId != null && right.mSubscriberId != null) { + res = left.mSubscriberId.compareTo(right.mSubscriberId); } - if (res == 0 && mNetworkId != null && another.mNetworkId != null) { - res = mNetworkId.compareTo(another.mNetworkId); + if (res == 0 && left.mWifiNetworkKey != null && right.mWifiNetworkKey != null) { + res = left.mWifiNetworkKey.compareTo(right.mWifiNetworkKey); } if (res == 0) { - res = Boolean.compare(mRoaming, another.mRoaming); + res = Boolean.compare(left.mRoaming, right.mRoaming); } if (res == 0) { - res = Boolean.compare(mMetered, another.mMetered); + res = Boolean.compare(left.mMetered, right.mMetered); } if (res == 0) { - res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork); + res = Boolean.compare(left.mDefaultNetwork, right.mDefaultNetwork); } if (res == 0) { - res = Integer.compare(mOemManaged, another.mOemManaged); + res = Integer.compare(left.mOemManaged, right.mOemManaged); } return res; } + + /** + * Builder class for {@link NetworkIdentity}. + */ + public static final class Builder { + // Need to be synchronized with ConnectivityManager. + // TODO: Use {@link ConnectivityManager#MAX_NETWORK_TYPE} when this file is in the module. + private static final int MAX_NETWORK_TYPE = 18; // TYPE_TEST + private static final int MIN_NETWORK_TYPE = TYPE_MOBILE; + + private int mType; + private int mRatType; + private String mSubscriberId; + private String mWifiNetworkKey; + private boolean mRoaming; + private boolean mMetered; + private boolean mDefaultNetwork; + private int mOemManaged; + + /** + * Creates a new Builder. + */ + public Builder() { + // Initialize with default values. Will be overwritten by setters. + mType = ConnectivityManager.TYPE_NONE; + mRatType = NetworkTemplate.NETWORK_TYPE_ALL; + mSubscriberId = null; + mWifiNetworkKey = null; + mRoaming = false; + mMetered = false; + mDefaultNetwork = false; + mOemManaged = NetworkTemplate.OEM_MANAGED_NO; + } + + /** + * Add an {@link NetworkStateSnapshot} into the {@link NetworkIdentity} instance. + * This is a useful shorthand that will read from the snapshot and set the + * following fields, if they are set in the snapshot : + * - type + * - subscriberId + * - roaming + * - metered + * - oemManaged + * - wifiNetworkKey + * + * @param snapshot The target {@link NetworkStateSnapshot} object. + * @return The builder object. + */ + @SuppressLint("MissingGetterMatchingBuilder") + @NonNull + public Builder setNetworkStateSnapshot(@NonNull NetworkStateSnapshot snapshot) { + setType(snapshot.getLegacyType()); + + setSubscriberId(snapshot.getSubscriberId()); + setRoaming(!snapshot.getNetworkCapabilities().hasCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)); + setMetered(!(snapshot.getNetworkCapabilities().hasCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_METERED) + || snapshot.getNetworkCapabilities().hasCapability( + NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED))); + + setOemManaged(getOemBitfield(snapshot.getNetworkCapabilities())); + + if (mType == TYPE_WIFI) { + final TransportInfo transportInfo = snapshot.getNetworkCapabilities() + .getTransportInfo(); + if (transportInfo instanceof WifiInfo) { + final WifiInfo info = (WifiInfo) transportInfo; + setWifiNetworkKey(info.getNetworkKey()); + } + } + return this; + } + + /** + * Set the network type of the network. + * + * @param type the network type. See {@link ConnectivityManager#TYPE_*}. + * + * @return this builder. + */ + @NonNull + public Builder setType(int type) { + // Include TYPE_NONE for compatibility, type field might not be filled by some + // networks such as test networks. + if ((type < MIN_NETWORK_TYPE || MAX_NETWORK_TYPE < type) + && type != ConnectivityManager.TYPE_NONE) { + throw new IllegalArgumentException("Invalid network type: " + type); + } + mType = type; + return this; + } + + /** + * Set the Radio Access Technology(RAT) type of the network. + * + * No RAT type is specified by default. Call clearRatType to reset. + * + * @param ratType the Radio Access Technology(RAT) type if applicable. See + * {@code TelephonyManager.NETWORK_TYPE_*}. + * + * @return this builder. + */ + @NonNull + public Builder setRatType(@Annotation.NetworkType int ratType) { + if (!CollectionUtils.contains(TelephonyManager.getAllNetworkTypes(), ratType) + && ratType != TelephonyManager.NETWORK_TYPE_UNKNOWN + && ratType != NetworkTemplate.NETWORK_TYPE_5G_NSA) { + throw new IllegalArgumentException("Invalid ratType " + ratType); + } + mRatType = ratType; + return this; + } + + /** + * Clear the Radio Access Technology(RAT) type of the network. + * + * @return this builder. + */ + @NonNull + public Builder clearRatType() { + mRatType = NetworkTemplate.NETWORK_TYPE_ALL; + return this; + } + + /** + * Set the Subscriber Id. + * + * @param subscriberId the Subscriber Id of the network. Or null if not applicable. + * @return this builder. + */ + @NonNull + public Builder setSubscriberId(@Nullable String subscriberId) { + mSubscriberId = subscriberId; + return this; + } + + /** + * Set the Wifi Network Key. + * + * @param wifiNetworkKey Wifi Network Key of the network, + * see {@link WifiInfo#getNetworkKey()}. + * Or null if not applicable. + * @return this builder. + */ + @NonNull + public Builder setWifiNetworkKey(@Nullable String wifiNetworkKey) { + mWifiNetworkKey = wifiNetworkKey; + return this; + } + + /** + * Set whether this network is roaming. + * + * This field is false by default. Call with false to reset. + * + * @param roaming the roaming status of the network. + * @return this builder. + */ + @NonNull + public Builder setRoaming(boolean roaming) { + mRoaming = roaming; + return this; + } + + /** + * Set whether this network is metered. + * + * This field is false by default. Call with false to reset. + * + * @param metered the meteredness of the network. + * @return this builder. + */ + @NonNull + public Builder setMetered(boolean metered) { + mMetered = metered; + return this; + } + + /** + * Set whether this network is the default network. + * + * This field is false by default. Call with false to reset. + * + * @param defaultNetwork the default network status of the network. + * @return this builder. + */ + @NonNull + public Builder setDefaultNetwork(boolean defaultNetwork) { + mDefaultNetwork = defaultNetwork; + return this; + } + + /** + * Set the OEM managed type. + * + * @param oemManaged Type of OEM managed network or unmanaged networks. + * See {@code NetworkTemplate#OEM_MANAGED_*}. + * @return this builder. + */ + @NonNull + public Builder setOemManaged(@OemManaged int oemManaged) { + // Assert input does not contain illegal oemManage bits. + if ((~SUPPORTED_OEM_MANAGED_TYPES & oemManaged) != 0) { + throw new IllegalArgumentException("Invalid value for OemManaged : " + oemManaged); + } + mOemManaged = oemManaged; + return this; + } + + private void ensureValidParameters() { + // Assert non-mobile network cannot have a ratType. + if (mType != TYPE_MOBILE && mRatType != NetworkTemplate.NETWORK_TYPE_ALL) { + throw new IllegalArgumentException( + "Invalid ratType " + mRatType + " for type " + mType); + } + + // Assert non-wifi network cannot have a wifi network key. + if (mType != TYPE_WIFI && mWifiNetworkKey != null) { + throw new IllegalArgumentException("Invalid wifi network key for type " + mType); + } + } + + /** + * Builds the instance of the {@link NetworkIdentity}. + * + * @return the built instance of {@link NetworkIdentity}. + */ + @NonNull + public NetworkIdentity build() { + ensureValidParameters(); + return new NetworkIdentity(mType, mRatType, mSubscriberId, mWifiNetworkKey, + mRoaming, mMetered, mDefaultNetwork, mOemManaged); + } + } } diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java index abbebef85c8f..dfa347f6f12b 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java @@ -18,6 +18,7 @@ package android.net; import static android.net.ConnectivityManager.TYPE_MOBILE; +import android.annotation.NonNull; import android.service.NetworkIdentitySetProto; import android.util.proto.ProtoOutputStream; @@ -25,6 +26,8 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.HashSet; +import java.util.Objects; +import java.util.Set; /** * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity} @@ -32,8 +35,7 @@ import java.util.HashSet; * * @hide */ -public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements - Comparable<NetworkIdentitySet> { +public class NetworkIdentitySet extends HashSet<NetworkIdentity> { private static final int VERSION_INIT = 1; private static final int VERSION_ADD_ROAMING = 2; private static final int VERSION_ADD_NETWORK_ID = 3; @@ -41,9 +43,19 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements private static final int VERSION_ADD_DEFAULT_NETWORK = 5; private static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6; + /** + * Construct a {@link NetworkIdentitySet} object. + */ public NetworkIdentitySet() { + super(); } + /** @hide */ + public NetworkIdentitySet(@NonNull Set<NetworkIdentity> ident) { + super(ident); + } + + /** @hide */ public NetworkIdentitySet(DataInput in) throws IOException { final int version = in.readInt(); final int size = in.readInt(); @@ -52,7 +64,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements final int ignored = in.readInt(); } final int type = in.readInt(); - final int subType = in.readInt(); + final int ratType = in.readInt(); final String subscriberId = readOptionalString(in); final String networkId; if (version >= VERSION_ADD_NETWORK_ID) { @@ -91,63 +103,73 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements oemNetCapabilities = NetworkIdentity.OEM_NONE; } - add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered, + add(new NetworkIdentity(type, ratType, subscriberId, networkId, roaming, metered, defaultNetwork, oemNetCapabilities)); } } /** * Method to serialize this object into a {@code DataOutput}. + * @hide */ public void writeToStream(DataOutput out) throws IOException { out.writeInt(VERSION_ADD_OEM_MANAGED_NETWORK); out.writeInt(size()); for (NetworkIdentity ident : this) { out.writeInt(ident.getType()); - out.writeInt(ident.getSubType()); + out.writeInt(ident.getRatType()); writeOptionalString(out, ident.getSubscriberId()); - writeOptionalString(out, ident.getNetworkId()); - out.writeBoolean(ident.getRoaming()); - out.writeBoolean(ident.getMetered()); - out.writeBoolean(ident.getDefaultNetwork()); + writeOptionalString(out, ident.getWifiNetworkKey()); + out.writeBoolean(ident.isRoaming()); + out.writeBoolean(ident.isMetered()); + out.writeBoolean(ident.isDefaultNetwork()); out.writeInt(ident.getOemManaged()); } } - /** @return whether any {@link NetworkIdentity} in this set is considered metered. */ + /** + * @return whether any {@link NetworkIdentity} in this set is considered metered. + * @hide + */ public boolean isAnyMemberMetered() { if (isEmpty()) { return false; } for (NetworkIdentity ident : this) { - if (ident.getMetered()) { + if (ident.isMetered()) { return true; } } return false; } - /** @return whether any {@link NetworkIdentity} in this set is considered roaming. */ + /** + * @return whether any {@link NetworkIdentity} in this set is considered roaming. + * @hide + */ public boolean isAnyMemberRoaming() { if (isEmpty()) { return false; } for (NetworkIdentity ident : this) { - if (ident.getRoaming()) { + if (ident.isRoaming()) { return true; } } return false; } - /** @return whether any {@link NetworkIdentity} in this set is considered on the default - network. */ + /** + * @return whether any {@link NetworkIdentity} in this set is considered on the default + * network. + * @hide + */ public boolean areAllMembersOnDefaultNetwork() { if (isEmpty()) { return true; } for (NetworkIdentity ident : this) { - if (!ident.getDefaultNetwork()) { + if (!ident.isDefaultNetwork()) { return false; } } @@ -171,18 +193,20 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements } } - @Override - public int compareTo(NetworkIdentitySet another) { - if (isEmpty()) return -1; - if (another.isEmpty()) return 1; + public static int compare(@NonNull NetworkIdentitySet left, @NonNull NetworkIdentitySet right) { + Objects.requireNonNull(left); + Objects.requireNonNull(right); + if (left.isEmpty()) return -1; + if (right.isEmpty()) return 1; - final NetworkIdentity ident = iterator().next(); - final NetworkIdentity anotherIdent = another.iterator().next(); - return ident.compareTo(anotherIdent); + final NetworkIdentity leftIdent = left.iterator().next(); + final NetworkIdentity rightIdent = right.iterator().next(); + return NetworkIdentity.compare(leftIdent, rightIdent); } /** * Method to dump this object into proto debug file. + * @hide */ public void dumpDebug(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java index b00fea4de269..9175809d9c7c 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java @@ -41,6 +41,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -57,7 +58,7 @@ import java.util.function.Predicate; */ // @NotThreadSafe @SystemApi -public final class NetworkStats implements Parcelable { +public final class NetworkStats implements Parcelable, Iterable<NetworkStats.Entry> { private static final String TAG = "NetworkStats"; /** @@ -383,6 +384,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(); @@ -589,11 +679,40 @@ public final class NetworkStats implements Parcelable { } /** + * Iterate over Entry objects. + * + * Return an iterator of this object that will iterate through all contained Entry objects. + * + * This iterator does not support concurrent modification and makes no guarantee of fail-fast + * behavior. If any method that can mutate the contents of this object is called while + * iteration is in progress, either inside the loop or in another thread, then behavior is + * undefined. + * The remove() method is not implemented and will throw UnsupportedOperationException. + * @hide + */ + @SystemApi + @NonNull public Iterator<Entry> iterator() { + return new Iterator<Entry>() { + int mIndex = 0; + + @Override + public boolean hasNext() { + return mIndex < size; + } + + @Override + public Entry next() { + return getValues(mIndex++, null); + } + }; + } + + /** * Return specific stats entry. * @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..735c44d5c87e 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java @@ -16,6 +16,7 @@ package android.net; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.DEFAULT_NETWORK_YES; import static android.net.NetworkStats.IFACE_ALL; @@ -32,6 +33,10 @@ import static android.text.format.DateUtils.WEEK_IN_MILLIS; import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.net.NetworkStatsHistory.Entry; import android.os.Binder; import android.service.NetworkStatsCollectionKeyProto; import android.service.NetworkStatsCollectionProto; @@ -46,8 +51,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 +61,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; @@ -70,7 +74,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Set; /** * Collection of {@link NetworkStatsHistory}, stored based on combined key of @@ -78,14 +85,12 @@ import java.util.Objects; * * @hide */ +@SystemApi(client = MODULE_LIBRARIES) public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer { private static final String TAG = NetworkStatsCollection.class.getSimpleName(); /** 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; @@ -104,15 +109,23 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W private long mTotalBytes; private boolean mDirty; + /** + * Construct a {@link NetworkStatsCollection} object. + * + * @param bucketDuration duration of the buckets in this object, in milliseconds. + * @hide + */ public NetworkStatsCollection(long bucketDuration) { mBucketDuration = bucketDuration; reset(); } + /** @hide */ public void clear() { reset(); } + /** @hide */ public void reset() { mStats.clear(); mStartMillis = Long.MAX_VALUE; @@ -121,6 +134,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W mDirty = false; } + /** @hide */ public long getStartMillis() { return mStartMillis; } @@ -128,6 +142,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W /** * Return first atomic bucket in this collection, which is more conservative * than {@link #mStartMillis}. + * @hide */ public long getFirstAtomicBucketMillis() { if (mStartMillis == Long.MAX_VALUE) { @@ -137,26 +152,32 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W } } + /** @hide */ public long getEndMillis() { return mEndMillis; } + /** @hide */ public long getTotalBytes() { return mTotalBytes; } + /** @hide */ public boolean isDirty() { return mDirty; } + /** @hide */ public void clearDirty() { mDirty = false; } + /** @hide */ public boolean isEmpty() { return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE; } + /** @hide */ @VisibleForTesting public long roundUp(long time) { if (time == Long.MIN_VALUE || time == Long.MAX_VALUE @@ -172,6 +193,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W } } + /** @hide */ @VisibleForTesting public long roundDown(long time) { if (time == Long.MIN_VALUE || time == Long.MAX_VALUE @@ -186,10 +208,12 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W } } + /** @hide */ public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) { return getRelevantUids(accessLevel, Binder.getCallingUid()); } + /** @hide */ public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel, final int callerUid) { final ArrayList<Integer> uids = new ArrayList<>(); @@ -210,6 +234,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W /** * Combine all {@link NetworkStatsHistory} in this collection which match * the requested parameters. + * @hide */ public NetworkStatsHistory getHistory(NetworkTemplate template, SubscriptionPlan augmentPlan, int uid, int set, int tag, int fields, long start, long end, @@ -335,6 +360,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W * @param end - end of the range, timestamp in milliseconds since the epoch. * @param accessLevel - caller access level. * @param callerUid - caller UID. + * @hide */ public NetworkStats getSummary(NetworkTemplate template, long start, long end, @NetworkStatsAccess.Level int accessLevel, int callerUid) { @@ -381,6 +407,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W /** * Record given {@link android.net.NetworkStats.Entry} into this collection. + * @hide */ public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start, long end, NetworkStats.Entry entry) { @@ -391,8 +418,12 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W /** * Record given {@link NetworkStatsHistory} into this collection. + * + * @hide */ - private void recordHistory(Key key, NetworkStatsHistory history) { + public void recordHistory(@NonNull Key key, @NonNull NetworkStatsHistory history) { + Objects.requireNonNull(key); + Objects.requireNonNull(history); if (history.size() == 0) return; noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes()); @@ -407,8 +438,11 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W /** * Record all {@link NetworkStatsHistory} contained in the given collection * into this collection. + * + * @hide */ - public void recordCollection(NetworkStatsCollection another) { + public void recordCollection(@NonNull NetworkStatsCollection another) { + Objects.requireNonNull(another); for (int i = 0; i < another.mStats.size(); i++) { final Key key = another.mStats.keyAt(i); final NetworkStatsHistory value = another.mStats.valueAt(i); @@ -437,10 +471,10 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W } } + /** @hide */ @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 { @@ -477,11 +511,11 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W } } + /** @hide */ @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 { @@ -520,6 +554,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W * See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}. * * @deprecated + * @hide */ @Deprecated public void readLegacyNetwork(File file) throws IOException { @@ -565,6 +600,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W * See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}. * * @deprecated + * @hide */ @Deprecated public void readLegacyUid(File file, boolean onlyTags) throws IOException { @@ -635,6 +671,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W * Remove any {@link NetworkStatsHistory} attributed to the requested UID, * moving any {@link NetworkStats#TAG_NONE} series to * {@link TrafficStats#UID_REMOVED}. + * @hide */ public void removeUids(int[] uids) { final ArrayList<Key> knownKeys = new ArrayList<>(); @@ -671,10 +708,11 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W private ArrayList<Key> getSortedKeys() { final ArrayList<Key> keys = new ArrayList<>(); keys.addAll(mStats.keySet()); - Collections.sort(keys); + Collections.sort(keys, (left, right) -> Key.compare(left, right)); return keys; } + /** @hide */ public void dump(IndentingPrintWriter pw) { for (Key key : getSortedKeys()) { pw.print("ident="); pw.print(key.ident.toString()); @@ -689,6 +727,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W } } + /** @hide */ public void dumpDebug(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); @@ -712,6 +751,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W proto.end(start); } + /** @hide */ public void dumpCheckin(PrintWriter pw, long start, long end) { dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell"); dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi"); @@ -774,16 +814,102 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W return false; } - private static class Key implements Comparable<Key> { + /** + * Get the all historical stats of the collection {@link NetworkStatsCollection}. + * + * @return All {@link NetworkStatsHistory} in this collection. + */ + @NonNull + public Map<Key, NetworkStatsHistory> getEntries() { + return new ArrayMap(mStats); + } + + /** + * Builder class for {@link NetworkStatsCollection}. + */ + public static final class Builder { + private final long mBucketDuration; + private final ArrayMap<Key, NetworkStatsHistory> mEntries = new ArrayMap<>(); + + /** + * Creates a new Builder with given bucket duration. + * + * @param bucketDuration Duration of the buckets of the object, in milliseconds. + */ + public Builder(long bucketDuration) { + mBucketDuration = bucketDuration; + } + + /** + * Add association of the history with the specified key in this map. + * + * @param key The object used to identify a network, see {@link Key}. + * @param history {@link NetworkStatsHistory} instance associated to the given {@link Key}. + * @return The builder object. + */ + @NonNull + public NetworkStatsCollection.Builder addEntry(@NonNull Key key, + @NonNull NetworkStatsHistory history) { + Objects.requireNonNull(key); + Objects.requireNonNull(history); + final List<Entry> historyEntries = history.getEntries(); + + final NetworkStatsHistory.Builder historyBuilder = + new NetworkStatsHistory.Builder(mBucketDuration, historyEntries.size()); + for (Entry entry : historyEntries) { + historyBuilder.addEntry(entry); + } + + mEntries.put(key, historyBuilder.build()); + return this; + } + + /** + * Builds the instance of the {@link NetworkStatsCollection}. + * + * @return the built instance of {@link NetworkStatsCollection}. + */ + @NonNull + public NetworkStatsCollection build() { + final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration); + for (int i = 0; i < mEntries.size(); i++) { + collection.recordHistory(mEntries.keyAt(i), mEntries.valueAt(i)); + } + return collection; + } + } + + /** + * the identifier that associate with the {@link NetworkStatsHistory} object to identify + * a certain record in the {@link NetworkStatsCollection} object. + */ + public static class Key { + /** @hide */ public final NetworkIdentitySet ident; + /** @hide */ public final int uid; + /** @hide */ public final int set; + /** @hide */ public final int tag; private final int mHashCode; - Key(NetworkIdentitySet ident, int uid, int set, int tag) { - this.ident = ident; + /** + * Construct a {@link Key} object. + * + * @param ident a Set of {@link NetworkIdentity} that associated with the record. + * @param uid Uid of the record. + * @param set Set of the record, see {@code NetworkStats#SET_*}. + * @param tag Tag of the record, see {@link TrafficStats#setThreadStatsTag(int)}. + */ + public Key(@NonNull Set<NetworkIdentity> ident, int uid, int set, int tag) { + this(new NetworkIdentitySet(Objects.requireNonNull(ident)), uid, set, tag); + } + + /** @hide */ + public Key(@NonNull NetworkIdentitySet ident, int uid, int set, int tag) { + this.ident = Objects.requireNonNull(ident); this.uid = uid; this.set = set; this.tag = tag; @@ -796,7 +922,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof Key) { final Key key = (Key) obj; return uid == key.uid && set == key.set && tag == key.tag @@ -805,20 +931,22 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W return false; } - @Override - public int compareTo(Key another) { + /** @hide */ + public static int compare(@NonNull Key left, @NonNull Key right) { + Objects.requireNonNull(left); + Objects.requireNonNull(right); int res = 0; - if (ident != null && another.ident != null) { - res = ident.compareTo(another.ident); + if (left.ident != null && right.ident != null) { + res = NetworkIdentitySet.compare(left.ident, right.ident); } if (res == 0) { - res = Integer.compare(uid, another.uid); + res = Integer.compare(left.uid, right.uid); } if (res == 0) { - res = Integer.compare(set, another.set); + res = Integer.compare(left.set, right.set); } if (res == 0) { - res = Integer.compare(tag, another.tag); + res = Integer.compare(left.tag, right.tag); } return res; } diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java index 428bc6df266a..78c137073aaa 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java @@ -16,6 +16,7 @@ package android.net; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.TAG_NONE; @@ -30,6 +31,8 @@ import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -50,7 +53,9 @@ import java.io.DataOutput; import java.io.IOException; import java.io.PrintWriter; import java.net.ProtocolException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Random; /** @@ -64,18 +69,25 @@ import java.util.Random; * * @hide */ -public class NetworkStatsHistory implements Parcelable { +@SystemApi(client = MODULE_LIBRARIES) +public final class NetworkStatsHistory implements Parcelable { private static final int VERSION_INIT = 1; private static final int VERSION_ADD_PACKETS = 2; private static final int VERSION_ADD_ACTIVE = 3; + /** @hide */ public static final int FIELD_ACTIVE_TIME = 0x01; + /** @hide */ public static final int FIELD_RX_BYTES = 0x02; + /** @hide */ public static final int FIELD_RX_PACKETS = 0x04; + /** @hide */ public static final int FIELD_TX_BYTES = 0x08; + /** @hide */ public static final int FIELD_TX_PACKETS = 0x10; + /** @hide */ public static final int FIELD_OPERATIONS = 0x20; - + /** @hide */ public static final int FIELD_ALL = 0xFFFFFFFF; private long bucketDuration; @@ -89,34 +101,171 @@ public class NetworkStatsHistory implements Parcelable { private long[] operations; private long totalBytes; - public static class Entry { + /** @hide */ + public NetworkStatsHistory(long bucketDuration, long[] bucketStart, long[] activeTime, + long[] rxBytes, long[] rxPackets, long[] txBytes, long[] txPackets, + long[] operations, int bucketCount, long totalBytes) { + this.bucketDuration = bucketDuration; + this.bucketStart = bucketStart; + this.activeTime = activeTime; + this.rxBytes = rxBytes; + this.rxPackets = rxPackets; + this.txBytes = txBytes; + this.txPackets = txPackets; + this.operations = operations; + this.bucketCount = bucketCount; + this.totalBytes = totalBytes; + } + + /** + * An instance to represent a single record in a {@link NetworkStatsHistory} object. + */ + public static final class Entry { + /** @hide */ public static final long UNKNOWN = -1; + /** @hide */ + // TODO: Migrate all callers to get duration from the history object and remove this field. @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public long bucketDuration; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public long bucketStart; + /** @hide */ public long activeTime; + /** @hide */ @UnsupportedAppUsage public long rxBytes; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public long rxPackets; + /** @hide */ @UnsupportedAppUsage public long txBytes; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public long txPackets; + /** @hide */ public long operations; + /** @hide */ + Entry() {} + + /** + * Construct a {@link Entry} instance to represent a single record in a + * {@link NetworkStatsHistory} object. + * + * @param bucketStart Start of period for this {@link Entry}, in milliseconds since the + * Unix epoch, see {@link java.lang.System#currentTimeMillis}. + * @param activeTime Active time for this {@link Entry}, in milliseconds. + * @param rxBytes Number of bytes received for this {@link Entry}. Statistics should + * represent the contents of IP packets, including IP headers. + * @param rxPackets Number of packets received for this {@link Entry}. Statistics should + * represent the contents of IP packets, including IP headers. + * @param txBytes Number of bytes transmitted for this {@link Entry}. Statistics should + * represent the contents of IP packets, including IP headers. + * @param txPackets Number of bytes transmitted for this {@link Entry}. Statistics should + * represent the contents of IP packets, including IP headers. + * @param operations count of network operations performed for this {@link Entry}. This can + * be used to derive bytes-per-operation. + */ + public Entry(long bucketStart, long activeTime, long rxBytes, + long rxPackets, long txBytes, long txPackets, long operations) { + this.bucketStart = bucketStart; + this.activeTime = activeTime; + this.rxBytes = rxBytes; + this.rxPackets = rxPackets; + this.txBytes = txBytes; + this.txPackets = txPackets; + this.operations = operations; + } + + /** + * Get start timestamp of the bucket's time interval, in milliseconds since the Unix epoch. + */ + public long getBucketStart() { + return bucketStart; + } + + /** + * Get active time of the bucket's time interval, in milliseconds. + */ + public long getActiveTime() { + return activeTime; + } + + /** Get number of bytes received for this {@link Entry}. */ + public long getRxBytes() { + return rxBytes; + } + + /** Get number of packets received for this {@link Entry}. */ + public long getRxPackets() { + return rxPackets; + } + + /** Get number of bytes transmitted for this {@link Entry}. */ + public long getTxBytes() { + return txBytes; + } + + /** Get number of packets transmitted for this {@link Entry}. */ + public long getTxPackets() { + return txPackets; + } + + /** Get count of network operations performed for this {@link Entry}. */ + public long getOperations() { + return operations; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o.getClass() != getClass()) return false; + Entry entry = (Entry) o; + return bucketStart == entry.bucketStart + && activeTime == entry.activeTime && rxBytes == entry.rxBytes + && rxPackets == entry.rxPackets && txBytes == entry.txBytes + && txPackets == entry.txPackets && operations == entry.operations; + } + + @Override + public int hashCode() { + return (int) (bucketStart * 2 + + activeTime * 3 + + rxBytes * 5 + + rxPackets * 7 + + txBytes * 11 + + txPackets * 13 + + operations * 17); + } + + @Override + public String toString() { + return "Entry{" + + "bucketStart=" + bucketStart + + ", activeTime=" + activeTime + + ", rxBytes=" + rxBytes + + ", rxPackets=" + rxPackets + + ", txBytes=" + txBytes + + ", txPackets=" + txPackets + + ", operations=" + operations + + "}"; + } } + /** @hide */ @UnsupportedAppUsage public NetworkStatsHistory(long bucketDuration) { this(bucketDuration, 10, FIELD_ALL); } + /** @hide */ public NetworkStatsHistory(long bucketDuration, int initialSize) { this(bucketDuration, initialSize, FIELD_ALL); } + /** @hide */ public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) { this.bucketDuration = bucketDuration; bucketStart = new long[initialSize]; @@ -130,11 +279,13 @@ public class NetworkStatsHistory implements Parcelable { totalBytes = 0; } + /** @hide */ public NetworkStatsHistory(NetworkStatsHistory existing, long bucketDuration) { this(bucketDuration, existing.estimateResizeBuckets(bucketDuration)); recordEntireHistory(existing); } + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public NetworkStatsHistory(Parcel in) { bucketDuration = in.readLong(); @@ -150,7 +301,7 @@ public class NetworkStatsHistory implements Parcelable { } @Override - public void writeToParcel(Parcel out, int flags) { + public void writeToParcel(@NonNull Parcel out, int flags) { out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); writeLongArray(out, activeTime, bucketCount); @@ -162,6 +313,7 @@ public class NetworkStatsHistory implements Parcelable { out.writeLong(totalBytes); } + /** @hide */ public NetworkStatsHistory(DataInput in) throws IOException { final int version = in.readInt(); switch (version) { @@ -204,6 +356,7 @@ public class NetworkStatsHistory implements Parcelable { } } + /** @hide */ public void writeToStream(DataOutput out) throws IOException { out.writeInt(VERSION_ADD_ACTIVE); out.writeLong(bucketDuration); @@ -221,15 +374,18 @@ public class NetworkStatsHistory implements Parcelable { return 0; } + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int size() { return bucketCount; } + /** @hide */ public long getBucketDuration() { return bucketDuration; } + /** @hide */ @UnsupportedAppUsage public long getStart() { if (bucketCount > 0) { @@ -239,6 +395,7 @@ public class NetworkStatsHistory implements Parcelable { } } + /** @hide */ @UnsupportedAppUsage public long getEnd() { if (bucketCount > 0) { @@ -250,6 +407,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Return total bytes represented by this history. + * @hide */ public long getTotalBytes() { return totalBytes; @@ -258,6 +416,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Return index of bucket that contains or is immediately before the * requested time. + * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getIndexBefore(long time) { @@ -273,6 +432,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Return index of bucket that contains or is immediately after the * requested time. + * @hide */ public int getIndexAfter(long time) { int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time); @@ -286,6 +446,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Return specific stats entry. + * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Entry getValues(int i, Entry recycle) { @@ -301,6 +462,23 @@ public class NetworkStatsHistory implements Parcelable { return entry; } + /** + * Get List of {@link Entry} of the {@link NetworkStatsHistory} instance. + * + * @return + */ + @NonNull + public List<Entry> getEntries() { + // TODO: Return a wrapper that uses this list instead, to prevent the returned result + // from being changed. + final ArrayList<Entry> ret = new ArrayList<>(size()); + for (int i = 0; i < size(); i++) { + ret.add(getValues(i, null /* recycle */)); + } + return ret; + } + + /** @hide */ public void setValues(int i, Entry entry) { // Unwind old values if (rxBytes != null) totalBytes -= rxBytes[i]; @@ -322,6 +500,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Record that data traffic occurred in the given time range. Will * distribute across internal buckets, creating new buckets as needed. + * @hide */ @Deprecated public void recordData(long start, long end, long rxBytes, long txBytes) { @@ -332,6 +511,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Record that data traffic occurred in the given time range. Will * distribute across internal buckets, creating new buckets as needed. + * @hide */ public void recordData(long start, long end, NetworkStats.Entry entry) { long rxBytes = entry.rxBytes; @@ -392,6 +572,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Record an entire {@link NetworkStatsHistory} into this history. Usually * for combining together stats for external reporting. + * @hide */ @UnsupportedAppUsage public void recordEntireHistory(NetworkStatsHistory input) { @@ -402,6 +583,7 @@ public class NetworkStatsHistory implements Parcelable { * Record given {@link NetworkStatsHistory} into this history, copying only * buckets that atomically occur in the inclusive time range. Doesn't * interpolate across partial buckets. + * @hide */ public void recordHistory(NetworkStatsHistory input, long start, long end) { final NetworkStats.Entry entry = new NetworkStats.Entry( @@ -483,6 +665,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Clear all data stored in this object. + * @hide */ public void clear() { bucketStart = EmptyArray.LONG; @@ -498,9 +681,10 @@ public class NetworkStatsHistory implements Parcelable { /** * Remove buckets older than requested cutoff. + * @hide */ - @Deprecated public void removeBucketsBefore(long cutoff) { + // TODO: Consider use getIndexBefore. int i; for (i = 0; i < bucketCount; i++) { final long curStart = bucketStart[i]; @@ -522,7 +706,9 @@ public class NetworkStatsHistory implements Parcelable { if (operations != null) operations = Arrays.copyOfRange(operations, i, length); bucketCount -= i; - // TODO: subtract removed values from totalBytes + totalBytes = 0; + if (rxBytes != null) totalBytes += CollectionUtils.total(rxBytes); + if (txBytes != null) totalBytes += CollectionUtils.total(txBytes); } } @@ -536,6 +722,7 @@ public class NetworkStatsHistory implements Parcelable { * @param start - start of the range, timestamp in milliseconds since the epoch. * @param end - end of the range, timestamp in milliseconds since the epoch. * @param recycle - entry instance for performance, could be null. + * @hide */ @UnsupportedAppUsage public Entry getValues(long start, long end, Entry recycle) { @@ -550,6 +737,7 @@ public class NetworkStatsHistory implements Parcelable { * @param end - end of the range, timestamp in milliseconds since the epoch. * @param now - current timestamp in milliseconds since the epoch (wall clock). * @param recycle - entry instance for performance, could be null. + * @hide */ @UnsupportedAppUsage public Entry getValues(long start, long end, long now, Entry recycle) { @@ -613,6 +801,7 @@ public class NetworkStatsHistory implements Parcelable { /** * @deprecated only for temporary testing + * @hide */ @Deprecated public void generateRandom(long start, long end, long bytes) { @@ -631,6 +820,7 @@ public class NetworkStatsHistory implements Parcelable { /** * @deprecated only for temporary testing + * @hide */ @Deprecated public void generateRandom(long start, long end, long rxBytes, long rxPackets, long txBytes, @@ -660,12 +850,14 @@ public class NetworkStatsHistory implements Parcelable { } } + /** @hide */ public static long randomLong(Random r, long start, long end) { return (long) (start + (r.nextFloat() * (end - start))); } /** * Quickly determine if this history intersects with given window. + * @hide */ public boolean intersects(long start, long end) { final long dataStart = getStart(); @@ -677,6 +869,7 @@ public class NetworkStatsHistory implements Parcelable { return false; } + /** @hide */ public void dump(IndentingPrintWriter pw, boolean fullHistory) { pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration / SECOND_IN_MILLIS); @@ -700,6 +893,7 @@ public class NetworkStatsHistory implements Parcelable { pw.decreaseIndent(); } + /** @hide */ public void dumpCheckin(PrintWriter pw) { pw.print("d,"); pw.print(bucketDuration / SECOND_IN_MILLIS); @@ -717,6 +911,7 @@ public class NetworkStatsHistory implements Parcelable { } } + /** @hide */ public void dumpDebug(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); @@ -776,6 +971,7 @@ public class NetworkStatsHistory implements Parcelable { if (array != null) array[i] += value; } + /** @hide */ public int estimateResizeBuckets(long newBucketDuration) { return (int) (size() * getBucketDuration() / newBucketDuration); } @@ -783,6 +979,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Utility methods for interacting with {@link DataInputStream} and * {@link DataOutputStream}, mostly dealing with writing partial arrays. + * @hide */ public static class DataStreamUtils { @Deprecated @@ -857,6 +1054,7 @@ public class NetworkStatsHistory implements Parcelable { /** * Utility methods for interacting with {@link Parcel} structures, mostly * dealing with writing partial arrays. + * @hide */ public static class ParcelUtils { public static long[] readLongArray(Parcel in) { @@ -884,4 +1082,80 @@ public class NetworkStatsHistory implements Parcelable { } } + /** + * Builder class for {@link NetworkStatsHistory}. + */ + public static final class Builder { + private final long mBucketDuration; + private final List<Long> mBucketStart; + private final List<Long> mActiveTime; + private final List<Long> mRxBytes; + private final List<Long> mRxPackets; + private final List<Long> mTxBytes; + private final List<Long> mTxPackets; + private final List<Long> mOperations; + + /** + * Creates a new Builder with given bucket duration and initial capacity to construct + * {@link NetworkStatsHistory} objects. + * + * @param bucketDuration Duration of the buckets of the object, in milliseconds. + * @param initialCapacity Estimated number of records. + */ + public Builder(long bucketDuration, int initialCapacity) { + mBucketDuration = bucketDuration; + mBucketStart = new ArrayList<>(initialCapacity); + mActiveTime = new ArrayList<>(initialCapacity); + mRxBytes = new ArrayList<>(initialCapacity); + mRxPackets = new ArrayList<>(initialCapacity); + mTxBytes = new ArrayList<>(initialCapacity); + mTxPackets = new ArrayList<>(initialCapacity); + mOperations = new ArrayList<>(initialCapacity); + } + + /** + * Add an {@link Entry} into the {@link NetworkStatsHistory} instance. + * + * @param entry The target {@link Entry} object. + * @return The builder object. + */ + @NonNull + public Builder addEntry(@NonNull Entry entry) { + mBucketStart.add(entry.bucketStart); + mActiveTime.add(entry.activeTime); + mRxBytes.add(entry.rxBytes); + mRxPackets.add(entry.rxPackets); + mTxBytes.add(entry.txBytes); + mTxPackets.add(entry.txPackets); + mOperations.add(entry.operations); + return this; + } + + private static long sum(@NonNull List<Long> list) { + long sum = 0; + for (long entry : list) { + sum += entry; + } + return sum; + } + + /** + * Builds the instance of the {@link NetworkStatsHistory}. + * + * @return the built instance of {@link NetworkStatsHistory}. + */ + @NonNull + public NetworkStatsHistory build() { + return new NetworkStatsHistory(mBucketDuration, + CollectionUtils.toLongArray(mBucketStart), + CollectionUtils.toLongArray(mActiveTime), + CollectionUtils.toLongArray(mRxBytes), + CollectionUtils.toLongArray(mRxPackets), + CollectionUtils.toLongArray(mTxBytes), + CollectionUtils.toLongArray(mTxPackets), + CollectionUtils.toLongArray(mOperations), + mBucketStart.size(), + sum(mRxBytes) + sum(mTxBytes)); + } + } } diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java index e9084b019668..dba39913300c 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java @@ -263,7 +263,7 @@ public final class NetworkTemplate implements Parcelable { * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the * given key of the wifi network. * - * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()} + * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()} * to know details about the key. * @hide */ @@ -283,7 +283,7 @@ public final class NetworkTemplate implements Parcelable { * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code wifiNetworkKey} to get result regardless * of key of the wifi network. * - * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()} + * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()} * to know details about the key. * @param subscriberId the IMSI associated to this wifi network. * @@ -364,7 +364,7 @@ public final class NetworkTemplate implements Parcelable { private final int mMetered; private final int mRoaming; private final int mDefaultNetwork; - private final int mSubType; + private final int mRatType; /** * The subscriber Id match rule defines how the template should match networks with * specific subscriberId(s). See NetworkTemplate#SUBSCRIBER_ID_MATCH_RULE_* for more detail. @@ -413,18 +413,18 @@ public final class NetworkTemplate implements Parcelable { /** @hide */ // TODO: Remove it after updating all of the caller. public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, - String wifiNetworkKey, int metered, int roaming, int defaultNetwork, int subType, + String wifiNetworkKey, int metered, int roaming, int defaultNetwork, int ratType, int oemManaged) { this(matchRule, subscriberId, matchSubscriberIds, wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0], - metered, roaming, defaultNetwork, subType, oemManaged, + metered, roaming, defaultNetwork, ratType, oemManaged, NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT); } /** @hide */ public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, String[] matchWifiNetworkKeys, int metered, int roaming, - int defaultNetwork, int subType, int oemManaged, int subscriberIdMatchRule) { + int defaultNetwork, int ratType, int oemManaged, int subscriberIdMatchRule) { Objects.requireNonNull(matchWifiNetworkKeys); mMatchRule = matchRule; mSubscriberId = subscriberId; @@ -435,7 +435,7 @@ public final class NetworkTemplate implements Parcelable { mMetered = metered; mRoaming = roaming; mDefaultNetwork = defaultNetwork; - mSubType = subType; + mRatType = ratType; mOemManaged = oemManaged; mSubscriberIdMatchRule = subscriberIdMatchRule; checkValidSubscriberIdMatchRule(matchRule, subscriberIdMatchRule); @@ -453,7 +453,7 @@ public final class NetworkTemplate implements Parcelable { mMetered = in.readInt(); mRoaming = in.readInt(); mDefaultNetwork = in.readInt(); - mSubType = in.readInt(); + mRatType = in.readInt(); mOemManaged = in.readInt(); mSubscriberIdMatchRule = in.readInt(); } @@ -467,7 +467,7 @@ public final class NetworkTemplate implements Parcelable { dest.writeInt(mMetered); dest.writeInt(mRoaming); dest.writeInt(mDefaultNetwork); - dest.writeInt(mSubType); + dest.writeInt(mRatType); dest.writeInt(mOemManaged); dest.writeInt(mSubscriberIdMatchRule); } @@ -500,8 +500,8 @@ public final class NetworkTemplate implements Parcelable { builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString( mDefaultNetwork)); } - if (mSubType != NETWORK_TYPE_ALL) { - builder.append(", subType=").append(mSubType); + if (mRatType != NETWORK_TYPE_ALL) { + builder.append(", ratType=").append(mRatType); } if (mOemManaged != OEM_MANAGED_ALL) { builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged)); @@ -514,7 +514,7 @@ public final class NetworkTemplate implements Parcelable { @Override public int hashCode() { return Objects.hash(mMatchRule, mSubscriberId, Arrays.hashCode(mMatchWifiNetworkKeys), - mMetered, mRoaming, mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule); + mMetered, mRoaming, mDefaultNetwork, mRatType, mOemManaged, mSubscriberIdMatchRule); } @Override @@ -526,7 +526,7 @@ public final class NetworkTemplate implements Parcelable { && mMetered == other.mMetered && mRoaming == other.mRoaming && mDefaultNetwork == other.mDefaultNetwork - && mSubType == other.mSubType + && mRatType == other.mRatType && mOemManaged == other.mOemManaged && mSubscriberIdMatchRule == other.mSubscriberIdMatchRule && Arrays.equals(mMatchWifiNetworkKeys, other.mMatchWifiNetworkKeys); @@ -593,7 +593,7 @@ public final class NetworkTemplate implements Parcelable { /** * Get the set of Wifi Network Keys of the template. - * See {@link WifiInfo#getCurrentNetworkKey()}. + * See {@link WifiInfo#getNetworkKey()}. */ @NonNull public Set<String> getWifiNetworkKeys() { @@ -635,7 +635,7 @@ public final class NetworkTemplate implements Parcelable { * Get the Radio Access Technology(RAT) type filter of the template. */ public int getRatType() { - return mSubType; + return mRatType; } /** @@ -652,7 +652,9 @@ public final class NetworkTemplate implements Parcelable { * * @hide */ - public boolean matches(NetworkIdentity ident) { + @SystemApi(client = MODULE_LIBRARIES) + public boolean matches(@NonNull NetworkIdentity ident) { + Objects.requireNonNull(ident); if (!matchesMetered(ident)) return false; if (!matchesRoaming(ident)) return false; if (!matchesDefaultNetwork(ident)) return false; @@ -708,8 +710,8 @@ public final class NetworkTemplate implements Parcelable { } private boolean matchesCollapsedRatType(NetworkIdentity ident) { - return mSubType == NETWORK_TYPE_ALL - || getCollapsedRatType(mSubType) == getCollapsedRatType(ident.mSubType); + return mRatType == NETWORK_TYPE_ALL + || getCollapsedRatType(mRatType) == getCollapsedRatType(ident.mRatType); } /** @@ -729,7 +731,7 @@ public final class NetworkTemplate implements Parcelable { * Returns true when the key matches, or when {@code mMatchWifiNetworkKeys} is * empty. * - * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()} + * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()} * to know details about the key. */ private boolean matchesWifiNetworkKey(@NonNull String wifiNetworkKey) { @@ -837,7 +839,7 @@ public final class NetworkTemplate implements Parcelable { switch (ident.mType) { case TYPE_WIFI: return matchesSubscriberId(ident.mSubscriberId) - && matchesWifiNetworkKey(ident.mNetworkId); + && matchesWifiNetworkKey(ident.mWifiNetworkKey); default: return false; } @@ -1059,9 +1061,9 @@ public final class NetworkTemplate implements Parcelable { * the intention of matching any Wifi Network Key. * * @param wifiNetworkKeys the list of Wifi Network Key, - * see {@link WifiInfo#getCurrentNetworkKey()}. + * see {@link WifiInfo#getNetworkKey()}. * Or an empty list to match all networks. - * Note that {@code getCurrentNetworkKey()} might get null key + * Note that {@code getNetworkKey()} might get null key * when wifi disconnects. However, the caller should never invoke * this function with a null Wifi Network Key since such statistics * never exists. diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java index d8feb88f0fe4..c2f0cdfb048c 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java +++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java @@ -16,6 +16,8 @@ package android.net; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; + import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -26,9 +28,10 @@ import android.app.usage.NetworkStatsManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.media.MediaPlayer; +import android.os.Binder; import android.os.Build; import android.os.RemoteException; -import android.os.ServiceManager; +import android.util.Log; import com.android.server.NetworkManagementSocketTagger; @@ -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,8 +177,8 @@ 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)); + throw new IllegalStateException("TrafficStats not initialized, uid=" + + Binder.getCallingUid()); } return sStatsService; } @@ -193,6 +197,45 @@ public class TrafficStats { private static final String LOOPBACK_IFACE = "lo"; /** + * Initialization {@link TrafficStats} with the context, to + * allow {@link TrafficStats} to fetch the needed binder. + * + * @param context a long-lived context, such as the application context or system + * server context. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @SuppressLint("VisiblySynchronized") + public static synchronized void init(@NonNull final Context context) { + if (sStatsService != null) { + throw new IllegalStateException("TrafficStats is already initialized, uid=" + + Binder.getCallingUid()); + } + final NetworkStatsManager statsManager = + context.getSystemService(NetworkStatsManager.class); + if (statsManager == null) { + // TODO: Currently Process.isSupplemental is not working yet, because it depends on + // process to run in a certain UID range, which is not true for now. Change this + // to Log.wtf once Process.isSupplemental is ready. + Log.e(TAG, "TrafficStats not initialized, uid=" + Binder.getCallingUid()); + return; + } + sStatsService = statsManager.getBinder(); + } + + /** + * Attach the socket tagger implementation to the current process, to + * get notified when a socket's {@link FileDescriptor} is assigned to + * a thread. See {@link SocketTagger#set(SocketTagger)}. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public static void attachSocketTagger() { + NetworkManagementSocketTagger.install(); + } + + /** * Set active tag to use when accounting {@link Socket} traffic originating * from the current thread. Only one active tag per thread is supported. * <p> @@ -265,6 +308,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/android/net/netstats/IUsageCallback.aidl b/packages/ConnectivityT/framework-t/src/android/net/netstats/IUsageCallback.aidl new file mode 100644 index 000000000000..4e8a5b23093a --- /dev/null +++ b/packages/ConnectivityT/framework-t/src/android/net/netstats/IUsageCallback.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 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.netstats; + +import android.net.DataUsageRequest; + +/** + * Interface for NetworkStatsService to notify events to the callers of registerUsageCallback. + * + * @hide + */ +oneway interface IUsageCallback { + void onThresholdReached(in DataUsageRequest request); + void onCallbackReleased(in DataUsageRequest request); +} 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/Android.bp b/packages/ConnectivityT/service/Android.bp index 97dfb64b33dd..24bc91d91ef7 100644 --- a/packages/ConnectivityT/service/Android.bp +++ b/packages/ConnectivityT/service/Android.bp @@ -26,6 +26,8 @@ filegroup { srcs: [ "src/com/android/server/net/NetworkIdentity*.java", "src/com/android/server/net/NetworkStats*.java", + "src/com/android/server/net/BpfInterfaceMapUpdater.java", + "src/com/android/server/net/InterfaceMapValue.java", ], path: "src", visibility: [ @@ -66,6 +68,7 @@ filegroup { filegroup { name: "services.connectivity-ethernet-sources", srcs: [ + "src/com/android/server/net/DelayedDiskWrite.java", "src/com/android/server/net/IpConfigStore.java", ], path: "src", @@ -82,8 +85,43 @@ filegroup { ":services.connectivity-ethernet-sources", ":services.connectivity-ipsec-sources", ":services.connectivity-netstats-sources", - ":services.connectivity-nsd-sources", ], path: "src", visibility: ["//frameworks/base/services/core"], } + +filegroup { + name: "services.connectivity-tiramisu-updatable-sources", + srcs: [ + ":services.connectivity-nsd-sources", + ], + path: "src", + visibility: [ + "//packages/modules/Connectivity:__subpackages__", + ], +} + +cc_library_shared { + name: "libcom_android_net_module_util_jni", + min_sdk_version: "30", + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-Wthread-safety", + ], + srcs: [ + "jni/onload.cpp", + ], + stl: "libc++_static", + static_libs: [ + "libnet_utils_device_common_bpfjni", + ], + shared_libs: [ + "liblog", + "libnativehelper", + ], + apex_available: [ + "//apex_available:platform", + ], +} diff --git a/packages/ConnectivityT/service/jni/onload.cpp b/packages/ConnectivityT/service/jni/onload.cpp new file mode 100644 index 000000000000..bca469756095 --- /dev/null +++ b/packages/ConnectivityT/service/jni/onload.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <nativehelper/JNIHelp.h> +#include <log/log.h> + +namespace android { + +int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name); + +extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + ALOGE("GetEnv failed"); + return JNI_ERR; + } + + if (register_com_android_net_module_util_BpfMap(env, + "com/android/net/module/util/BpfMap") < 0) return JNI_ERR; + + return JNI_VERSION_1_6; +} + +}; + diff --git a/packages/ConnectivityT/service/src/com/android/server/net/BpfInterfaceMapUpdater.java b/packages/ConnectivityT/service/src/com/android/server/net/BpfInterfaceMapUpdater.java new file mode 100644 index 000000000000..25c88eb6bdb2 --- /dev/null +++ b/packages/ConnectivityT/service/src/com/android/server/net/BpfInterfaceMapUpdater.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2022 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.content.Context; +import android.net.INetd; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.system.ErrnoException; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.net.module.util.BaseNetdUnsolicitedEventListener; +import com.android.net.module.util.BpfMap; +import com.android.net.module.util.IBpfMap; +import com.android.net.module.util.InterfaceParams; +import com.android.net.module.util.Struct.U32; + +/** + * Monitor interface added (without removed) and right interface name and its index to bpf map. + */ +public class BpfInterfaceMapUpdater { + private static final String TAG = BpfInterfaceMapUpdater.class.getSimpleName(); + // This is current path but may be changed soon. + private static final String IFACE_INDEX_NAME_MAP_PATH = + "/sys/fs/bpf/map_netd_iface_index_name_map"; + private final IBpfMap<U32, InterfaceMapValue> mBpfMap; + private final INetd mNetd; + private final Handler mHandler; + private final Dependencies mDeps; + + public BpfInterfaceMapUpdater(Context ctx, Handler handler) { + this(ctx, handler, new Dependencies()); + } + + @VisibleForTesting + public BpfInterfaceMapUpdater(Context ctx, Handler handler, Dependencies deps) { + mDeps = deps; + mBpfMap = deps.getInterfaceMap(); + mNetd = deps.getINetd(ctx); + mHandler = handler; + } + + /** + * Dependencies of BpfInerfaceMapUpdater, for injection in tests. + */ + @VisibleForTesting + public static class Dependencies { + /** Create BpfMap for updating interface and index mapping. */ + public IBpfMap<U32, InterfaceMapValue> getInterfaceMap() { + try { + return new BpfMap<>(IFACE_INDEX_NAME_MAP_PATH, BpfMap.BPF_F_RDWR, + U32.class, InterfaceMapValue.class); + } catch (ErrnoException e) { + Log.e(TAG, "Cannot create interface map: " + e); + return null; + } + } + + /** Get InterfaceParams for giving interface name. */ + public InterfaceParams getInterfaceParams(String ifaceName) { + return InterfaceParams.getByName(ifaceName); + } + + /** Get INetd binder object. */ + public INetd getINetd(Context ctx) { + return INetd.Stub.asInterface((IBinder) ctx.getSystemService(Context.NETD_SERVICE)); + } + } + + /** + * Start listening interface update event. + * Query current interface names before listening. + */ + public void start() { + mHandler.post(() -> { + if (mBpfMap == null) { + Log.wtf(TAG, "Fail to start: Null bpf map"); + return; + } + + try { + // TODO: use a NetlinkMonitor and listen for RTM_NEWLINK messages instead. + mNetd.registerUnsolicitedEventListener(new InterfaceChangeObserver()); + } catch (RemoteException e) { + Log.wtf(TAG, "Unable to register netd UnsolicitedEventListener, " + e); + } + + final String[] ifaces; + try { + // TODO: use a netlink dump to get the current interface list. + ifaces = mNetd.interfaceGetList(); + } catch (RemoteException | ServiceSpecificException e) { + Log.wtf(TAG, "Unable to query interface names by netd, " + e); + return; + } + + for (String ifaceName : ifaces) { + addInterface(ifaceName); + } + }); + } + + private void addInterface(String ifaceName) { + final InterfaceParams iface = mDeps.getInterfaceParams(ifaceName); + if (iface == null) { + Log.e(TAG, "Unable to get InterfaceParams for " + ifaceName); + return; + } + + try { + mBpfMap.updateEntry(new U32(iface.index), new InterfaceMapValue(ifaceName)); + } catch (ErrnoException e) { + Log.e(TAG, "Unable to update entry for " + ifaceName + ", " + e); + } + } + + private class InterfaceChangeObserver extends BaseNetdUnsolicitedEventListener { + @Override + public void onInterfaceAdded(String ifName) { + mHandler.post(() -> addInterface(ifName)); + } + } +} diff --git a/services/core/java/com/android/server/net/DelayedDiskWrite.java b/packages/ConnectivityT/service/src/com/android/server/net/DelayedDiskWrite.java index 8f09eb7c19ab..35dc4557252c 100644 --- a/services/core/java/com/android/server/net/DelayedDiskWrite.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/DelayedDiskWrite.java @@ -26,21 +26,37 @@ import java.io.DataOutputStream; import java.io.FileOutputStream; import java.io.IOException; +/** + * This class provides APIs to do a delayed data write to a given {@link OutputStream}. + */ public class DelayedDiskWrite { + private static final String TAG = "DelayedDiskWrite"; + private HandlerThread mDiskWriteHandlerThread; private Handler mDiskWriteHandler; /* Tracks multiple writes on the same thread */ private int mWriteSequence = 0; - private final String TAG = "DelayedDiskWrite"; + /** + * Used to do a delayed data write to a given {@link OutputStream}. + */ public interface Writer { - public void onWriteCalled(DataOutputStream out) throws IOException; + /** + * write data to a given {@link OutputStream}. + */ + void onWriteCalled(DataOutputStream out) throws IOException; } + /** + * Do a delayed data write to a given output stream opened from filePath. + */ public void write(final String filePath, final Writer w) { write(filePath, w, true); } + /** + * Do a delayed data write to a given output stream opened from filePath. + */ public void write(final String filePath, final Writer w, final boolean open) { if (TextUtils.isEmpty(filePath)) { throw new IllegalArgumentException("empty file path"); @@ -77,7 +93,7 @@ public class DelayedDiskWrite { if (out != null) { try { out.close(); - } catch (Exception e) {} + } catch (Exception e) { } } // Quit if no more writes sent diff --git a/packages/ConnectivityT/service/src/com/android/server/net/InterfaceMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/InterfaceMapValue.java new file mode 100644 index 000000000000..061f323447b0 --- /dev/null +++ b/packages/ConnectivityT/service/src/com/android/server/net/InterfaceMapValue.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.net; + +import com.android.net.module.util.Struct; +import com.android.net.module.util.Struct.Field; +import com.android.net.module.util.Struct.Type; + +/** + * The value of bpf interface index map which is used for NetworkStatsService. + */ +public class InterfaceMapValue extends Struct { + @Field(order = 0, type = Type.ByteArray, arraysize = 16) + public final byte[] interfaceName; + + public InterfaceMapValue(String iface) { + final byte[] ifaceArray = iface.getBytes(); + interfaceName = new byte[16]; + // All array bytes after the interface name, if any, must be 0. + System.arraycopy(ifaceArray, 0, interfaceName, 0, ifaceArray.length); + } +} diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java index bb123a38ebc0..17f3455d20a2 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java @@ -26,10 +26,10 @@ import static com.android.server.NetworkManagementSocketTagger.kernelToTag; import android.annotation.NonNull; import android.annotation.Nullable; -import android.net.INetd; +import android.content.Context; +import android.net.ConnectivityManager; import android.net.NetworkStats; import android.net.UnderlyingNetworkInfo; -import android.os.RemoteException; import android.os.StrictMode; import android.os.SystemClock; @@ -70,7 +70,7 @@ public class NetworkStatsFactory { private final boolean mUseBpfStats; - private final INetd mNetd; + private final Context mContext; /** * Guards persistent data access in this class @@ -158,12 +158,12 @@ public class NetworkStatsFactory { NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces); } - public NetworkStatsFactory(@NonNull INetd netd) { - this(new File("/proc/"), true, netd); + public NetworkStatsFactory(@NonNull Context ctx) { + this(ctx, new File("/proc/"), true); } @VisibleForTesting - public NetworkStatsFactory(File procRoot, boolean useBpfStats, @NonNull INetd netd) { + public NetworkStatsFactory(@NonNull Context ctx, File procRoot, boolean useBpfStats) { mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all"); mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt"); mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats"); @@ -172,7 +172,7 @@ public class NetworkStatsFactory { mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1); mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1); } - mNetd = netd; + mContext = ctx; } public NetworkStats readBpfNetworkStatsDev() throws IOException { @@ -295,11 +295,12 @@ public class NetworkStatsFactory { } @GuardedBy("mPersistentDataLock") - private void requestSwapActiveStatsMapLocked() throws RemoteException { - // Ask netd to do a active map stats swap. When the binder call successfully returns, + private void requestSwapActiveStatsMapLocked() { + // Do a active map stats swap. When the binder call successfully returns, // the system server should be able to safely read and clean the inactive map // without race problem. - mNetd.trafficSwapActiveStatsMap(); + final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); + cm.swapActiveStatsMap(); } /** @@ -327,7 +328,7 @@ public class NetworkStatsFactory { if (mUseBpfStats) { try { requestSwapActiveStatsMapLocked(); - } catch (RemoteException e) { + } catch (RuntimeException e) { throw new IOException(e); } // Stats are always read from the inactive map, so they must be read after the 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/NetworkStatsObservers.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java index b57a4f920b60..19536247b23b 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java @@ -26,13 +26,12 @@ import android.net.NetworkStatsAccess; import android.net.NetworkStatsCollection; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; -import android.os.Bundle; +import android.net.netstats.IUsageCallback; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.Messenger; import android.os.Process; import android.os.RemoteException; import android.util.ArrayMap; @@ -75,10 +74,10 @@ class NetworkStatsObservers { * * @return the normalized request wrapped within {@link RequestInfo}. */ - public DataUsageRequest register(DataUsageRequest inputRequest, Messenger messenger, - IBinder binder, int callingUid, @NetworkStatsAccess.Level int accessLevel) { - DataUsageRequest request = buildRequest(inputRequest); - RequestInfo requestInfo = buildRequestInfo(request, messenger, binder, callingUid, + public DataUsageRequest register(DataUsageRequest inputRequest, IUsageCallback callback, + int callingUid, @NetworkStatsAccess.Level int accessLevel) { + DataUsageRequest request = buildRequest(inputRequest, callingUid); + RequestInfo requestInfo = buildRequestInfo(request, callback, callingUid, accessLevel); if (LOGV) Log.v(TAG, "Registering observer for " + request); @@ -195,10 +194,12 @@ class NetworkStatsObservers { } } - private DataUsageRequest buildRequest(DataUsageRequest request) { - // Cap the minimum threshold to a safe default to avoid too many callbacks - long thresholdInBytes = Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes); - if (thresholdInBytes < request.thresholdInBytes) { + private DataUsageRequest buildRequest(DataUsageRequest request, int callingUid) { + // For non-system uid, cap the minimum threshold to a safe default to avoid too + // many callbacks. + long thresholdInBytes = (callingUid == Process.SYSTEM_UID ? request.thresholdInBytes + : Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes)); + if (thresholdInBytes > request.thresholdInBytes) { Log.w(TAG, "Threshold was too low for " + request + ". Overriding to a safer default of " + thresholdInBytes + " bytes"); } @@ -206,11 +207,10 @@ class NetworkStatsObservers { request.template, thresholdInBytes); } - private RequestInfo buildRequestInfo(DataUsageRequest request, - Messenger messenger, IBinder binder, int callingUid, - @NetworkStatsAccess.Level int accessLevel) { + private RequestInfo buildRequestInfo(DataUsageRequest request, IUsageCallback callback, + int callingUid, @NetworkStatsAccess.Level int accessLevel) { if (accessLevel <= NetworkStatsAccess.Level.USER) { - return new UserUsageRequestInfo(this, request, messenger, binder, callingUid, + return new UserUsageRequestInfo(this, request, callback, callingUid, accessLevel); } else { // Safety check in case a new access level is added and we forgot to update this @@ -218,7 +218,7 @@ class NetworkStatsObservers { throw new IllegalArgumentException( "accessLevel " + accessLevel + " is less than DEVICESUMMARY."); } - return new NetworkUsageRequestInfo(this, request, messenger, binder, callingUid, + return new NetworkUsageRequestInfo(this, request, callback, callingUid, accessLevel); } } @@ -230,25 +230,23 @@ class NetworkStatsObservers { private abstract static class RequestInfo implements IBinder.DeathRecipient { private final NetworkStatsObservers mStatsObserver; protected final DataUsageRequest mRequest; - private final Messenger mMessenger; - private final IBinder mBinder; + private final IUsageCallback mCallback; protected final int mCallingUid; protected final @NetworkStatsAccess.Level int mAccessLevel; protected NetworkStatsRecorder mRecorder; protected NetworkStatsCollection mCollection; RequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request, - Messenger messenger, IBinder binder, int callingUid, + IUsageCallback callback, int callingUid, @NetworkStatsAccess.Level int accessLevel) { mStatsObserver = statsObserver; mRequest = request; - mMessenger = messenger; - mBinder = binder; + mCallback = callback; mCallingUid = callingUid; mAccessLevel = accessLevel; try { - mBinder.linkToDeath(this, 0); + mCallback.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { binderDied(); } @@ -257,7 +255,7 @@ class NetworkStatsObservers { @Override public void binderDied() { if (LOGV) { - Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mBinder + ")"); + Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mCallback + ")"); } mStatsObserver.unregister(mRequest, Process.SYSTEM_UID); callCallback(NetworkStatsManager.CALLBACK_RELEASED); @@ -270,9 +268,7 @@ class NetworkStatsObservers { } private void unlinkDeathRecipient() { - if (mBinder != null) { - mBinder.unlinkToDeath(this, 0); - } + mCallback.asBinder().unlinkToDeath(this, 0); } /** @@ -294,17 +290,19 @@ class NetworkStatsObservers { } private void callCallback(int callbackType) { - Bundle bundle = new Bundle(); - bundle.putParcelable(DataUsageRequest.PARCELABLE_KEY, mRequest); - Message msg = Message.obtain(); - msg.what = callbackType; - msg.setData(bundle); try { if (LOGV) { Log.v(TAG, "sending notification " + callbackTypeToName(callbackType) + " for " + mRequest); } - mMessenger.send(msg); + switch (callbackType) { + case NetworkStatsManager.CALLBACK_LIMIT_REACHED: + mCallback.onThresholdReached(mRequest); + break; + case NetworkStatsManager.CALLBACK_RELEASED: + mCallback.onCallbackReleased(mRequest); + break; + } } catch (RemoteException e) { // May occur naturally in the race of binder death. Log.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest); @@ -334,9 +332,9 @@ class NetworkStatsObservers { private static class NetworkUsageRequestInfo extends RequestInfo { NetworkUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request, - Messenger messenger, IBinder binder, int callingUid, + IUsageCallback callback, int callingUid, @NetworkStatsAccess.Level int accessLevel) { - super(statsObserver, request, messenger, binder, callingUid, accessLevel); + super(statsObserver, request, callback, callingUid, accessLevel); } @Override @@ -376,9 +374,9 @@ class NetworkStatsObservers { private static class UserUsageRequestInfo extends RequestInfo { UserUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request, - Messenger messenger, IBinder binder, int callingUid, + IUsageCallback callback, int callingUid, @NetworkStatsAccess.Level int accessLevel) { - super(statsObserver, request, messenger, binder, callingUid, accessLevel); + super(statsObserver, request, callback, callingUid, accessLevel); } @Override 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..243d62164705 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java @@ -19,12 +19,16 @@ package com.android.server.net; import static android.Manifest.permission.NETWORK_STATS_PROVIDER; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.Manifest.permission.UPDATE_DEVICE_STATS; +import static android.app.usage.NetworkStatsManager.PREFIX_DEV; +import static android.app.usage.NetworkStatsManager.PREFIX_UID; +import static android.app.usage.NetworkStatsManager.PREFIX_UID_TAG; +import static android.app.usage.NetworkStatsManager.PREFIX_XT; import static android.content.Intent.ACTION_SHUTDOWN; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.net.NetworkIdentity.SUBTYPE_COMBINED; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.IFACE_VT; @@ -44,25 +48,9 @@ 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; -import static android.provider.Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED; -import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION; -import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE; -import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES; -import static android.provider.Settings.Global.NETSTATS_DEV_ROTATE_AGE; -import static android.provider.Settings.Global.NETSTATS_GLOBAL_ALERT_BYTES; -import static android.provider.Settings.Global.NETSTATS_POLL_INTERVAL; -import static android.provider.Settings.Global.NETSTATS_SAMPLE_ENABLED; -import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION; -import static android.provider.Settings.Global.NETSTATS_UID_DELETE_AGE; -import static android.provider.Settings.Global.NETSTATS_UID_PERSIST_BYTES; -import static android.provider.Settings.Global.NETSTATS_UID_ROTATE_AGE; -import static android.provider.Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION; -import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE; -import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES; -import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; @@ -70,7 +58,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 +77,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,10 +94,12 @@ 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; import android.net.Uri; +import android.net.netstats.IUsageCallback; import android.net.netstats.provider.INetworkStatsProvider; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.net.netstats.provider.NetworkStatsProvider; @@ -117,15 +107,13 @@ import android.os.Binder; import android.os.DropBoxManager; import android.os.Environment; 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,13 +136,13 @@ 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.LocationPermissionChecker; 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; import java.io.FileDescriptor; @@ -207,8 +195,19 @@ 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; + + // TODO: Replace the hardcoded string and move it into ConnectivitySettingsManager. + private static final String NETSTATS_COMBINE_SUBTYPE_ENABLED = + "netstats_combine_subtype_enabled"; + private final Context mContext; - private final INetworkManagementService mNetworkManager; private final NetworkStatsFactory mStatsFactory; private final AlarmManager mAlarmManager; private final Clock mClock; @@ -223,6 +222,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"; @@ -231,11 +233,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private PendingIntent mPollIntent; - private static final String PREFIX_DEV = "dev"; - private static final String PREFIX_XT = "xt"; - private static final String PREFIX_UID = "uid"; - private static final String PREFIX_UID_TAG = "uid_tag"; - /** * Settings that can be changed externally. */ @@ -245,9 +242,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { boolean getSampleEnabled(); boolean getAugmentEnabled(); /** - * When enabled, all mobile data is reported under {@link NetworkIdentity#SUBTYPE_COMBINED}. - * When disabled, mobile data is broken down by a granular subtype representative of the - * actual subtype. {@see NetworkTemplate#getCollapsedRatType}. + * When enabled, all mobile data is reported under {@link NetworkTemplate#NETWORK_TYPE_ALL}. + * When disabled, mobile data is broken down by a granular ratType representative of the + * actual ratType. {@see NetworkTemplate#getCollapsedRatType}. * Enabling this decreases the level of detail but saves performance, disk space and * amount of data logged. */ @@ -294,6 +291,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** Set of any ifaces associated with mobile networks since boot. */ private volatile String[] mMobileIfaces = new String[0]; + /** Set of any ifaces associated with wifi networks since boot. */ + private volatile String[] mWifiIfaces = new String[0]; + /** Set of all ifaces currently used by traffic that does not explicitly specify a Network. */ @GuardedBy("mStatsLock") private Network[] mDefaultNetworks = new Network[0]; @@ -354,6 +354,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @NonNull private final NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor; + @NonNull + private final LocationPermissionChecker mLocationPermissionChecker; + + @NonNull + private final BpfInterfaceMapUpdater mInterfaceMapUpdater; + private static @NonNull File getDefaultSystemDir() { return new File(Environment.getDataDirectory(), "system"); } @@ -405,20 +411,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 DefaultNetworkStatsSettings(), new NetworkStatsFactory(context), new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(), new Dependencies()); - service.registerLocalService(); return service; } @@ -426,14 +432,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"); @@ -448,10 +452,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { handlerThread.start(); mHandler = new NetworkStatsHandler(handlerThread.getLooper()); mNetworkStatsSubscriptionsMonitor = deps.makeSubscriptionsMonitor(mContext, - new HandlerExecutor(mHandler), this); + (command) -> mHandler.post(command) , this); mContentResolver = mContext.getContentResolver(); mContentObserver = mDeps.makeContentObserver(mHandler, mSettings, mNetworkStatsSubscriptionsMonitor); + mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext); + mInterfaceMapUpdater = mDeps.makeBpfInterfaceMapUpdater(mContext, mHandler); + mInterfaceMapUpdater.start(); } /** @@ -499,11 +506,40 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } }; } + + /** + * @see LocationPermissionChecker + */ + public LocationPermissionChecker makeLocationPermissionChecker(final Context context) { + return new LocationPermissionChecker(context); + } + + /** Create BpfInterfaceMapUpdater to update bpf interface map. */ + @NonNull + public BpfInterfaceMapUpdater makeBpfInterfaceMapUpdater( + @NonNull Context ctx, @NonNull Handler handler) { + return new BpfInterfaceMapUpdater(ctx, handler); + } } - 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() { @@ -532,7 +568,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // watch for tethering changes final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class); tetheringManager.registerTetheringEventCallback( - new HandlerExecutor(mHandler), mTetherListener); + (command) -> mHandler.post(command), mTetherListener); // listen for periodic polling events final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL); @@ -551,9 +587,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()}. @@ -566,13 +602,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mSettings.getPollInterval(), pollIntent); mContentResolver.registerContentObserver(Settings.Global - .getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED), + .getUriFor(NETSTATS_COMBINE_SUBTYPE_ENABLED), false /* notifyForDescendants */, mContentObserver); // Post a runnable on handler thread to call onChange(). It's for getting current value of // NETSTATS_COMBINE_SUBTYPE_ENABLED to decide start or stop monitoring RAT type changes. mHandler.post(() -> mContentObserver.onChange(false, Settings.Global - .getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED))); + .getUriFor(NETSTATS_COMBINE_SUBTYPE_ENABLED))); registerGlobalAlert(); } @@ -641,13 +677,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) { @@ -683,12 +719,25 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return now - lastCallTime < POLL_RATE_LIMIT_MS; } - private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) { + private int restrictFlagsForCaller(int flags) { + // All non-privileged callers are not allowed to turn off POLL_ON_OPEN. + final boolean isPrivileged = PermissionUtils.checkAnyPermissionOf(mContext, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_STACK); + if (!isPrivileged) { + flags |= NetworkStatsManager.FLAG_POLL_ON_OPEN; + } + // Non-system uids are rate limited for POLL_ON_OPEN. final int callingUid = Binder.getCallingUid(); - final int usedFlags = isRateLimitedForPoll(callingUid) + flags = isRateLimitedForPoll(callingUid) ? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN) : flags; - if ((usedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN + return flags; + } + + private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) { + final int restrictedFlags = restrictFlagsForCaller(flags); + if ((restrictedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN | NetworkStatsManager.FLAG_POLL_FORCE)) != 0) { final long ident = Binder.clearCallingIdentity(); try { @@ -702,7 +751,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // for its lifetime; when caller closes only weak references remain. return new INetworkStatsSession.Stub() { - private final int mCallingUid = callingUid; + private final int mCallingUid = Binder.getCallingUid(); private final String mCallingPackage = callingPackage; private final @NetworkStatsAccess.Level int mAccessLevel = checkAccessLevel( callingPackage); @@ -736,26 +785,41 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStats getDeviceSummaryForNetwork( NetworkTemplate template, long start, long end) { - return internalGetSummaryForNetwork(template, usedFlags, start, end, mAccessLevel, - mCallingUid); + enforceTemplatePermissions(template, callingPackage); + return internalGetSummaryForNetwork(template, restrictedFlags, start, end, + mAccessLevel, mCallingUid); } @Override public NetworkStats getSummaryForNetwork( NetworkTemplate template, long start, long end) { - return internalGetSummaryForNetwork(template, usedFlags, start, end, mAccessLevel, - mCallingUid); + enforceTemplatePermissions(template, callingPackage); + return internalGetSummaryForNetwork(template, restrictedFlags, start, end, + mAccessLevel, mCallingUid); } + // TODO: Remove this after all callers are removed. @Override public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) { - return internalGetHistoryForNetwork(template, usedFlags, fields, mAccessLevel, - mCallingUid); + enforceTemplatePermissions(template, callingPackage); + return internalGetHistoryForNetwork(template, restrictedFlags, fields, + mAccessLevel, mCallingUid, Long.MIN_VALUE, Long.MAX_VALUE); + } + + @Override + public NetworkStatsHistory getHistoryIntervalForNetwork(NetworkTemplate template, + int fields, long start, long end) { + enforceTemplatePermissions(template, callingPackage); + // TODO(b/200768422): Redact returned history if the template is location + // sensitive but the caller is not privileged. + return internalGetHistoryForNetwork(template, restrictedFlags, fields, + mAccessLevel, mCallingUid, start, end); } @Override public NetworkStats getSummaryForAllUid( NetworkTemplate template, long start, long end, boolean includeTags) { + enforceTemplatePermissions(template, callingPackage); try { final NetworkStats stats = getUidComplete() .getSummary(template, start, end, mAccessLevel, mCallingUid); @@ -766,8 +830,19 @@ 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) { + enforceTemplatePermissions(template, callingPackage); + try { + final NetworkStats tagStats = getUidTagComplete() + .getSummary(template, start, end, mAccessLevel, mCallingUid); + return tagStats; + } catch (NullPointerException e) { throw e; } } @@ -775,6 +850,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStatsHistory getHistoryForUid( NetworkTemplate template, int uid, int set, int tag, int fields) { + enforceTemplatePermissions(template, callingPackage); // NOTE: We don't augment UID-level statistics if (tag == TAG_NONE) { return getUidComplete().getHistory(template, null, uid, set, tag, fields, @@ -789,6 +865,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public NetworkStatsHistory getHistoryIntervalForUid( NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end) { + enforceTemplatePermissions(template, callingPackage); + // TODO(b/200768422): Redact returned history if the template is location + // sensitive but the caller is not privileged. // NOTE: We don't augment UID-level statistics if (tag == TAG_NONE) { return getUidComplete().getHistory(template, null, uid, set, tag, fields, @@ -810,6 +889,26 @@ public class NetworkStatsService extends INetworkStatsService.Stub { }; } + private void enforceTemplatePermissions(@NonNull NetworkTemplate template, + @NonNull String callingPackage) { + // For a template with wifi network keys, it is possible for a malicious + // client to track the user locations via querying data usage. Thus, enforce + // fine location permission check. + if (!template.getWifiNetworkKeys().isEmpty()) { + final boolean canAccessFineLocation = mLocationPermissionChecker + .checkCallersLocationPermission(callingPackage, + null /* featureId */, + Binder.getCallingUid(), + false /* coarseForTargetSdkLessThanQ */, + null /* message */); + if (!canAccessFineLocation) { + throw new SecurityException("Access fine location is required when querying" + + " with wifi network keys, make sure the app has the necessary" + + "permissions and the location toggle is on."); + } + } + } + private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) { return NetworkStatsAccess.checkAccessLevel( mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage); @@ -827,7 +926,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); @@ -846,7 +945,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // We've been using pure XT stats long enough that we no longer need to // splice DEV and XT together. final NetworkStatsHistory history = internalGetHistoryForNetwork(template, flags, FIELD_ALL, - accessLevel, callingUid); + accessLevel, callingUid, start, end); final long now = System.currentTimeMillis(); final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null); @@ -863,14 +962,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * appropriate. */ private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, - int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid) { + int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid, + long start, long end) { // We've been using pure XT stats long enough that we no longer need to // splice DEV and XT together. final SubscriptionPlan augmentPlan = resolveSubscriptionPlan(template, flags); synchronized (mStatsLock) { return mXtStatsCached.getHistory(template, augmentPlan, - UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE, - accessLevel, callingUid); + UID_ALL, SET_ALL, TAG_NONE, fields, start, end, accessLevel, callingUid); } } @@ -901,8 +1000,17 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } // TODO: switch to data layer stats once kernel exports - // for now, read network layer stats and flatten across all ifaces - final NetworkStats networkLayer = readNetworkStatsUidDetail(uid, INTERFACES_ALL, TAG_ALL); + // for now, read network layer stats and flatten across all ifaces. + // This function is used to query NeworkStats for calle's uid. The only caller method + // TrafficStats#getDataLayerSnapshotForUid alrady claim no special permission to query + // its own NetworkStats. + final long ident = Binder.clearCallingIdentity(); + final NetworkStats networkLayer; + try { + networkLayer = readNetworkStatsUidDetail(uid, INTERFACES_ALL, TAG_ALL); + } finally { + Binder.restoreCallingIdentity(ident); + } // splice in operation counts networkLayer.spliceOperationsFrom(mUidOperations); @@ -921,10 +1029,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override - public NetworkStats getDetailedUidStats(String[] requiredIfaces) { + public NetworkStats getUidStatsForTransport(int transport) { + enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); try { + final String[] relevantIfaces = + transport == TRANSPORT_WIFI ? mWifiIfaces : mMobileIfaces; + // TODO(b/215633405) : mMobileIfaces and mWifiIfaces already contain the stacked + // interfaces, so this is not useful, remove it. final String[] ifacesToQuery = - mStatsFactory.augmentWithStackedInterfaces(requiredIfaces); + mStatsFactory.augmentWithStackedInterfaces(relevantIfaces); return getNetworkStatsUidDetail(ifacesToQuery); } catch (RemoteException e) { Log.wtf(TAG, "Error compiling UID stats", e); @@ -965,7 +1078,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 +1115,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 +1125,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); @@ -1041,21 +1157,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override - public DataUsageRequest registerUsageCallback(String callingPackage, - DataUsageRequest request, Messenger messenger, IBinder binder) { + public DataUsageRequest registerUsageCallback(@NonNull String callingPackage, + @NonNull DataUsageRequest request, @NonNull IUsageCallback callback) { Objects.requireNonNull(callingPackage, "calling package is null"); Objects.requireNonNull(request, "DataUsageRequest is null"); Objects.requireNonNull(request.template, "NetworkTemplate is null"); - Objects.requireNonNull(messenger, "messenger is null"); - Objects.requireNonNull(binder, "binder is null"); + Objects.requireNonNull(callback, "callback is null"); int callingUid = Binder.getCallingUid(); @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(callingPackage); DataUsageRequest normalizedRequest; final long token = Binder.clearCallingIdentity(); try { - normalizedRequest = mStatsObservers.register(request, messenger, binder, - callingUid, accessLevel); + normalizedRequest = mStatsObservers.register( + request, callback, callingUid, accessLevel); } finally { Binder.restoreCallingIdentity(token); } @@ -1226,26 +1341,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 @@ -1300,16 +1395,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled(); final ArraySet<String> mobileIfaces = new ArraySet<>(); + final ArraySet<String> wifiIfaces = new ArraySet<>(); for (NetworkStateSnapshot snapshot : snapshots) { final int displayTransport = getDisplayTransport(snapshot.getNetworkCapabilities().getTransportTypes()); final boolean isMobile = (NetworkCapabilities.TRANSPORT_CELLULAR == displayTransport); + final boolean isWifi = (NetworkCapabilities.TRANSPORT_WIFI == displayTransport); final boolean isDefault = CollectionUtils.contains( mDefaultNetworks, snapshot.getNetwork()); - final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED - : getSubTypeForStateSnapshot(snapshot); + final int ratType = combineSubtypeEnabled ? NetworkTemplate.NETWORK_TYPE_ALL + : getRatTypeForStateSnapshot(snapshot); final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot, - isDefault, subType); + isDefault, ratType); // Traffic occurring on the base interface is always counted for // both total usage and UID details. @@ -1324,12 +1421,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // VT is considered always metered in framework's layer. If VT is not metered // per carrier's policy, modem will report 0 usage for VT calls. if (snapshot.getNetworkCapabilities().hasCapability( - NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) { + NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.isMetered()) { // Copy the identify from IMS one but mark it as metered. NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(), - ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(), - ident.getRoaming(), true /* metered */, + ident.getRatType(), ident.getSubscriberId(), ident.getWifiNetworkKey(), + ident.isRoaming(), true /* metered */, true /* onDefaultNetwork */, ident.getOemManaged()); final String ifaceVt = IFACE_VT + getSubIdForMobile(snapshot); findOrCreateNetworkIdentitySet(mActiveIfaces, ifaceVt).add(vtIdent); @@ -1339,6 +1436,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { if (isMobile) { mobileIfaces.add(baseIface); } + if (isWifi) { + wifiIfaces.add(baseIface); + } } // Traffic occurring on stacked interfaces is usually clatd. @@ -1380,6 +1480,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { if (isMobile) { mobileIfaces.add(iface); } + if (isWifi) { + wifiIfaces.add(iface); + } mStatsFactory.noteStackedIface(iface, baseIface); } @@ -1387,11 +1490,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } mMobileIfaces = mobileIfaces.toArray(new String[0]); + mWifiIfaces = wifiIfaces.toArray(new String[0]); // TODO (b/192758557): Remove debug log. if (CollectionUtils.contains(mMobileIfaces, null)) { throw new NullPointerException( "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces)); } + if (CollectionUtils.contains(mWifiIfaces, null)) { + throw new NullPointerException( + "null element in mWifiIfaces: " + Arrays.toString(mWifiIfaces)); + } } private static int getSubIdForMobile(@NonNull NetworkStateSnapshot state) { @@ -1409,11 +1517,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } /** - * For networks with {@code TRANSPORT_CELLULAR}, get subType that was obtained through + * For networks with {@code TRANSPORT_CELLULAR}, get ratType that was obtained through * {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different * transport types do not actually fill this value. */ - private int getSubTypeForStateSnapshot(@NonNull NetworkStateSnapshot state) { + private int getRatTypeForStateSnapshot(@NonNull NetworkStateSnapshot state) { if (!state.getNetworkCapabilities().hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { return 0; } @@ -1610,7 +1718,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 +1730,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 +1776,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 +2031,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 +2092,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 +2137,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 +2152,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 +2162,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mSemaphore = semaphore; mAlertObserver = alertObserver; mStatsProviderCbList = cbList; + mNetworkPolicyManager = networkPolicyManager; } @NonNull @@ -2106,15 +2202,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.notifyStatsProviderWarningOrLimitReached()); } @Override @@ -2173,24 +2268,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * {@link android.provider.Settings.Global}. */ private static class DefaultNetworkStatsSettings implements NetworkStatsSettings { - private final ContentResolver mResolver; - - public DefaultNetworkStatsSettings(Context context) { - mResolver = Objects.requireNonNull(context.getContentResolver()); - // TODO: adjust these timings for production builds - } - - private long getGlobalLong(String name, long def) { - return Settings.Global.getLong(mResolver, name, def); - } - private boolean getGlobalBoolean(String name, boolean def) { - final int defInt = def ? 1 : 0; - return Settings.Global.getInt(mResolver, name, defInt) != 0; - } + DefaultNetworkStatsSettings() {} @Override public long getPollInterval() { - return getGlobalLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS); + return 30 * MINUTE_IN_MILLIS; } @Override public long getPollDelay() { @@ -2198,25 +2280,23 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override public long getGlobalAlertBytes(long def) { - return getGlobalLong(NETSTATS_GLOBAL_ALERT_BYTES, def); + return def; } @Override public boolean getSampleEnabled() { - return getGlobalBoolean(NETSTATS_SAMPLE_ENABLED, true); + return true; } @Override public boolean getAugmentEnabled() { - return getGlobalBoolean(NETSTATS_AUGMENT_ENABLED, true); + return true; } @Override public boolean getCombineSubtypeEnabled() { - return getGlobalBoolean(NETSTATS_COMBINE_SUBTYPE_ENABLED, false); + return false; } @Override public Config getDevConfig() { - return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS), - getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS), - getGlobalLong(NETSTATS_DEV_DELETE_AGE, 90 * DAY_IN_MILLIS)); + return new Config(HOUR_IN_MILLIS, 15 * DAY_IN_MILLIS, 90 * DAY_IN_MILLIS); } @Override public Config getXtConfig() { @@ -2224,31 +2304,27 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override public Config getUidConfig() { - return new Config(getGlobalLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS), - getGlobalLong(NETSTATS_UID_ROTATE_AGE, 15 * DAY_IN_MILLIS), - getGlobalLong(NETSTATS_UID_DELETE_AGE, 90 * DAY_IN_MILLIS)); + return new Config(2 * HOUR_IN_MILLIS, 15 * DAY_IN_MILLIS, 90 * DAY_IN_MILLIS); } @Override public Config getUidTagConfig() { - return new Config(getGlobalLong(NETSTATS_UID_TAG_BUCKET_DURATION, 2 * HOUR_IN_MILLIS), - getGlobalLong(NETSTATS_UID_TAG_ROTATE_AGE, 5 * DAY_IN_MILLIS), - getGlobalLong(NETSTATS_UID_TAG_DELETE_AGE, 15 * DAY_IN_MILLIS)); + return new Config(2 * HOUR_IN_MILLIS, 5 * DAY_IN_MILLIS, 15 * DAY_IN_MILLIS); } @Override public long getDevPersistBytes(long def) { - return getGlobalLong(NETSTATS_DEV_PERSIST_BYTES, def); + return def; } @Override public long getXtPersistBytes(long def) { - return getDevPersistBytes(def); + return def; } @Override public long getUidPersistBytes(long def) { - return getGlobalLong(NETSTATS_UID_PERSIST_BYTES, def); + return def; } @Override public long getUidTagPersistBytes(long def) { - return getGlobalLong(NETSTATS_UID_TAG_PERSIST_BYTES, def); + return def; } } diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index a3eb0eccad9d..ce58ff6fc59d 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -236,7 +236,8 @@ public class ExternalStorageProvider extends FileSystemProvider { root.flags |= Root.FLAG_REMOVABLE_USB; } - if (volume.getType() != VolumeInfo.TYPE_EMULATED) { + if (volume.getType() != VolumeInfo.TYPE_EMULATED + && volume.getType() != VolumeInfo.TYPE_STUB) { root.flags |= Root.FLAG_SUPPORTS_EJECT; } diff --git a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java index f5aa652f3194..b4e84dd54654 100644 --- a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java +++ b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java @@ -27,9 +27,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.net.NetworkTemplate; -import android.net.wifi.WifiInfo; import android.os.AsyncTask; -import android.text.TextUtils; import android.util.RecurrenceRule; import com.google.android.collect.Lists; @@ -124,7 +122,7 @@ public class NetworkPolicyEditor { if (policy != null) { return policy; } else { - return getPolicy(buildUnquotedNetworkTemplate(template)); + return getPolicy(template); } } @@ -207,21 +205,4 @@ public class NetworkPolicyEditor { policy.clearSnooze(); writeAsync(); } - - /** - * Build a revised {@link NetworkTemplate} that matches the same rule, but - * with an unquoted {@link NetworkTemplate#getNetworkId()}. Used to work - * around legacy bugs. - */ - private static NetworkTemplate buildUnquotedNetworkTemplate(NetworkTemplate template) { - if (template == null) return null; - final String networkId = template.getNetworkId(); - final String strippedNetworkId = WifiInfo.sanitizeSsid(networkId); - if (!TextUtils.equals(strippedNetworkId, networkId)) { - return new NetworkTemplate( - template.getMatchRule(), template.getSubscriberId(), strippedNetworkId); - } else { - return null; - } - } } diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/OWNERS b/packages/SettingsLib/src/com/android/settingslib/mobile/OWNERS new file mode 100644 index 000000000000..ab9b5dc3ff7a --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/mobile/OWNERS @@ -0,0 +1,4 @@ +# Default reviewers for this and subdirectories. +bonianchen@google.com + +# Emergency approvers in case the above are not available diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java index cff45c6be0e0..30c6645193c0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java @@ -16,7 +16,6 @@ package com.android.settingslib.net; -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; import static android.text.format.DateUtils.FORMAT_SHOW_DATE; @@ -49,6 +48,7 @@ public class DataUsageController { 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 static final long MB_IN_BYTES = 1024 * 1024; private final Context mContext; private final NetworkPolicyManager mPolicyManager; @@ -237,10 +237,8 @@ public class DataUsageController { final int matchRule = networkTemplate.getMatchRule(); switch (matchRule) { case NetworkTemplate.MATCH_MOBILE: - case NetworkTemplate.MATCH_MOBILE_WILDCARD: return ConnectivityManager.TYPE_MOBILE; case NetworkTemplate.MATCH_WIFI: - case NetworkTemplate.MATCH_WIFI_WILDCARD: return ConnectivityManager.TYPE_WIFI; case NetworkTemplate.MATCH_ETHERNET: return ConnectivityManager.TYPE_ETHERNET; diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java index afd44d5bbc90..386a47ae29b0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java @@ -63,14 +63,32 @@ public class DataUsageUtils { private static NetworkTemplate getNormalizedMobileTemplate( TelephonyManager telephonyManager, int subId) { final NetworkTemplate mobileTemplate = getMobileTemplateForSubId(telephonyManager, subId); - final String[] mergedSubscriberIds = telephonyManager - .createForSubscriptionId(subId).getMergedImsisFromGroup(); + final Set<String> mergedSubscriberIds = Set.of(telephonyManager + .createForSubscriptionId(subId).getMergedImsisFromGroup()); if (ArrayUtils.isEmpty(mergedSubscriberIds)) { Log.i(TAG, "mergedSubscriberIds is null."); return mobileTemplate; } - return NetworkTemplate.normalize(mobileTemplate, mergedSubscriberIds); + return normalizeMobileTemplate(mobileTemplate, mergedSubscriberIds); + } + + private static NetworkTemplate normalizeMobileTemplate( + NetworkTemplate template, Set<String> mergedSet) { + if (template.getSubscriberIds().isEmpty()) return template; + // The input template should have at most 1 subscriberId. + final String subscriberId = template.getSubscriberIds().iterator().next(); + + if (mergedSet.contains(subscriberId)) { + // Requested template subscriber is part of the merge group; return + // a template that matches all merged subscribers. + return new NetworkTemplate.Builder(template.getMatchRule()) + .setSubscriberIds(mergedSet) + .setWifiNetworkKeys(template.getWifiNetworkKeys()) + .setMeteredness(NetworkStats.METERED_YES).build(); + } + + return template; } private static NetworkTemplate getMobileTemplateForSubId( diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java index 43c05b8b64d6..504390cd0b63 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java @@ -53,8 +53,9 @@ public class NetworkCycleDataForUidLoader extends long totalUsage = 0L; long totalForeground = 0L; for (int uid : mUids) { - final NetworkStats stats = mNetworkStatsManager.queryDetailsForUid( - mNetworkTemplate, start, end, uid); + final NetworkStats stats = mNetworkStatsManager.queryDetailsForUidTagState( + mNetworkTemplate, start, end, uid, NetworkStats.Bucket.TAG_NONE, + NetworkStats.Bucket.STATE_ALL); final long usage = getTotalUsage(stats); if (usage > 0L) { totalUsage += usage; diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java index 3e95b01824cc..5e9ac5a59091 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java @@ -16,23 +16,16 @@ package com.android.settingslib.net; -import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; -import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; - +import android.annotation.NonNull; import android.app.usage.NetworkStats; import android.app.usage.NetworkStatsManager; import android.content.Context; -import android.net.INetworkStatsService; -import android.net.INetworkStatsSession; import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; -import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; -import android.net.TrafficStats; -import android.os.RemoteException; -import android.os.ServiceManager; import android.text.format.DateUtils; import android.util.Pair; +import android.util.Range; import androidx.annotation.VisibleForTesting; import androidx.loader.content.AsyncTaskLoader; @@ -52,8 +45,6 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { protected final NetworkTemplate mNetworkTemplate; private final NetworkPolicy mPolicy; private final ArrayList<Long> mCycles; - @VisibleForTesting - final INetworkStatsService mNetworkStatsService; protected NetworkCycleDataLoader(Builder<?> builder) { super(builder.mContext); @@ -61,8 +52,6 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { mCycles = builder.mCycles; mNetworkStatsManager = (NetworkStatsManager) builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE); - mNetworkStatsService = INetworkStatsService.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); final NetworkPolicyEditor policyEditor = new NetworkPolicyEditor(NetworkPolicyManager.from(builder.mContext)); policyEditor.read(); @@ -112,23 +101,20 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { @VisibleForTesting void loadFourWeeksData() { + if (mNetworkTemplate == null) return; + final NetworkStats stats = mNetworkStatsManager.queryDetailsForDevice( + mNetworkTemplate, Long.MIN_VALUE, Long.MAX_VALUE); try { - final INetworkStatsSession networkSession = mNetworkStatsService.openSession(); - final NetworkStatsHistory networkHistory = networkSession.getHistoryForNetwork( - mNetworkTemplate, FIELD_RX_BYTES | FIELD_TX_BYTES); - final long historyStart = networkHistory.getStart(); - final long historyEnd = networkHistory.getEnd(); - - long cycleEnd = historyEnd; - while (cycleEnd > historyStart) { + final Range<Long> historyTimeRange = getTimeRangeOf(stats); + + long cycleEnd = historyTimeRange.getUpper(); + while (cycleEnd > historyTimeRange.getLower()) { final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4); recordUsage(cycleStart, cycleEnd); cycleEnd = cycleStart; } - - TrafficStats.closeQuietly(networkSession); - } catch (RemoteException e) { - throw new RuntimeException(e); + } catch (IllegalArgumentException e) { + // Empty history, ignore. } } @@ -169,6 +155,32 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { return bytes; } + @NonNull + @VisibleForTesting + Range getTimeRangeOf(@NonNull NetworkStats stats) { + long start = Long.MAX_VALUE; + long end = Long.MIN_VALUE; + while (hasNextBucket(stats)) { + final NetworkStats.Bucket bucket = getNextBucket(stats); + start = Math.min(start, bucket.getStartTimeStamp()); + end = Math.max(end, bucket.getEndTimeStamp()); + } + return new Range(start, end); + } + + @VisibleForTesting + boolean hasNextBucket(@NonNull NetworkStats stats) { + return stats.hasNextBucket(); + } + + @NonNull + @VisibleForTesting + NetworkStats.Bucket getNextBucket(@NonNull NetworkStats stats) { + NetworkStats.Bucket bucket = new NetworkStats.Bucket(); + stats.getNextBucket(bucket); + return bucket; + } + @VisibleForTesting(otherwise = VisibleForTesting.NONE) public ArrayList<Long> getCycles() { return mCycles; diff --git a/packages/SettingsLib/src/com/android/settingslib/net/OWNERS b/packages/SettingsLib/src/com/android/settingslib/net/OWNERS new file mode 100644 index 000000000000..ab9b5dc3ff7a --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/net/OWNERS @@ -0,0 +1,4 @@ +# Default reviewers for this and subdirectories. +bonianchen@google.com + +# Emergency approvers in case the above are not available diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java index 02326ea85ff6..623eb33f9c0d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java @@ -17,6 +17,7 @@ package com.android.settingslib.net; import android.app.AppGlobals; +import android.app.usage.NetworkStats; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; @@ -116,13 +117,13 @@ public class UidDetailProvider { detail.label = res.getString(R.string.process_kernel_label); detail.icon = pm.getDefaultActivityIcon(); return detail; - case TrafficStats.UID_REMOVED: + case NetworkStats.Bucket.UID_REMOVED: detail.label = res.getString(UserManager.supportsMultipleUsers() ? R.string.data_usage_uninstalled_apps_users : R.string.data_usage_uninstalled_apps); detail.icon = pm.getDefaultActivityIcon(); return detail; - case TrafficStats.UID_TETHERING: + case NetworkStats.Bucket.UID_TETHERING: final TetheringManager tm = mContext.getSystemService(TetheringManager.class); detail.label = res.getString(Utils.getTetheringLabel(tm)); detail.icon = pm.getDefaultActivityIcon(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java index 74b91510cf3f..c79440e58e17 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java @@ -16,24 +16,24 @@ package com.android.settingslib.net; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.nullable; +import static android.app.usage.NetworkStats.Bucket.UID_ALL; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.doNothing; 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.annotation.NonNull; +import android.app.usage.NetworkStats; 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.NetworkStatsHistory; import android.net.NetworkTemplate; -import android.os.RemoteException; import android.text.format.DateUtils; import android.util.Range; @@ -49,6 +49,8 @@ import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; @RunWith(RobolectricTestRunner.class) public class NetworkCycleDataLoaderTest { @@ -63,8 +65,6 @@ public class NetworkCycleDataLoaderTest { private NetworkPolicy mPolicy; @Mock private Iterator<Range<ZonedDateTime>> mIterator; - @Mock - private INetworkStatsService mNetworkStatsService; private NetworkCycleDataTestLoader mLoader; @@ -132,20 +132,24 @@ public class NetworkCycleDataLoaderTest { verify(mLoader).recordUsage(nowInMs, nowInMs); } + private NetworkStats.Bucket makeMockBucket(int uid, long rxBytes, long txBytes, + long start, long end) { + NetworkStats.Bucket ret = mock(NetworkStats.Bucket.class); + when(ret.getUid()).thenReturn(uid); + when(ret.getRxBytes()).thenReturn(rxBytes); + when(ret.getTxBytes()).thenReturn(txBytes); + when(ret.getStartTimeStamp()).thenReturn(start); + when(ret.getEndTimeStamp()).thenReturn(end); + return ret; + } + @Test - public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() throws RemoteException { + public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() { mLoader = spy(new NetworkCycleDataTestLoader(mContext)); - ReflectionHelpers.setField(mLoader, "mNetworkStatsService", mNetworkStatsService); - final INetworkStatsSession networkSession = mock(INetworkStatsSession.class); - when(mNetworkStatsService.openSession()).thenReturn(networkSession); - final NetworkStatsHistory networkHistory = mock(NetworkStatsHistory.class); - when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt())) - .thenReturn(networkHistory); final long now = System.currentTimeMillis(); final long fourWeeksAgo = now - (DateUtils.WEEK_IN_MILLIS * 4); final long twoDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 2); - when(networkHistory.getStart()).thenReturn(twoDaysAgo); - when(networkHistory.getEnd()).thenReturn(now); + mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, twoDaysAgo, now)); mLoader.loadFourWeeksData(); @@ -173,10 +177,31 @@ public class NetworkCycleDataLoaderTest { verify(mLoader).recordUsage(thirtyDaysAgo, twentyDaysAgo); } + @Test + public void getTimeRangeOf() { + mLoader = spy(new NetworkCycleDataTestLoader(mContext)); + // If empty, new Range(MAX_VALUE, MIN_VALUE) will be constructed. Hence, the function + // should throw. + assertThrows(IllegalArgumentException.class, + () -> mLoader.getTimeRangeOf(mock(NetworkStats.class))); + + mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 0, 10)); + // Feed the function with unused NetworkStats. The actual data injection is + // done by addBucket. + assertEquals(new Range(0L, 10L), mLoader.getTimeRangeOf(mock(NetworkStats.class))); + + mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 0, 10)); + mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 30, 40)); + mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 10, 25)); + assertEquals(new Range(0L, 40L), mLoader.getTimeRangeOf(mock(NetworkStats.class))); + } + public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> { + private final Queue<NetworkStats.Bucket> mMockedBuckets = new LinkedBlockingQueue<>(); private NetworkCycleDataTestLoader(Context context) { - super(NetworkCycleDataLoader.builder(mContext)); + super(NetworkCycleDataLoader.builder(mContext) + .setNetworkTemplate(mock(NetworkTemplate.class))); mContext = context; } @@ -188,5 +213,19 @@ public class NetworkCycleDataLoaderTest { List<NetworkCycleData> getCycleUsage() { return null; } + + public void addBucket(NetworkStats.Bucket bucket) { + mMockedBuckets.add(bucket); + } + + @Override + public boolean hasNextBucket(@NonNull NetworkStats unused) { + return !mMockedBuckets.isEmpty(); + } + + @Override + public NetworkStats.Bucket getNextBucket(@NonNull NetworkStats unused) { + return mMockedBuckets.remove(); + } } } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 618a37e2e40f..96310b8597d2 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -245,6 +245,7 @@ <uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" /> <uses-permission android:name="android.permission.MANAGE_SEARCH_UI" /> <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" /> + <uses-permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION" /> <uses-permission android:name="android.permission.MANAGE_UI_TRANSLATION" /> <uses-permission android:name="android.permission.NETWORK_SETTINGS" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> @@ -340,6 +341,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" /> @@ -524,6 +528,10 @@ <!-- Permission required for CTS test - PeopleManagerTest --> <uses-permission android:name="android.permission.READ_PEOPLE_DATA" /> + <!-- Permissions required for CTS test - TrustTestCases --> + <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" /> + <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" /> + <!-- Permission required for CTS test - CtsGameManagerTestCases --> <uses-permission android:name="android.permission.MANAGE_GAME_MODE" /> diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS index e1da74466b55..8323e326dea1 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -11,8 +11,10 @@ asc@google.com awickham@google.com beverlyt@google.com brockman@google.com +brzezinski@google.com brycelee@google.com ccassidy@google.com +chrisgollner@google.com cinek@google.com cwren@google.com dupin@google.com @@ -43,6 +45,8 @@ mpietal@google.com mrcasey@google.com mrenouf@google.com nesciosquid@google.com +nickchameyev@google.com +nicomazz@google.com ogunwale@google.com peanutbutter@google.com pinyaoting@google.com @@ -67,6 +71,7 @@ winsonc@google.com yurilin@google.com xuqiu@google.com zakcohen@google.com +jernej@google.com #Android Auto hseog@google.com diff --git a/services/Android.bp b/services/Android.bp index 841edc7ed509..8947393849c1 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -74,6 +74,7 @@ filegroup { ":services.appwidget-sources", ":services.autofill-sources", ":services.backup-sources", + ":services.bluetooth-sources", // TODO(b/214988855) : Remove once apex/service-bluetooth jar is ready ":backuplib-sources", ":services.companion-sources", ":services.contentcapture-sources", @@ -175,6 +176,10 @@ cc_library_shared { name: "libandroid_servers", defaults: ["libservices.core-libs"], whole_static_libs: ["libservices.core"], + required: [ + // TODO: remove after NetworkStatsService is moved to the mainline module. + "libcom_android_net_module_util_jni", + ], } platform_compat_config { diff --git a/services/core/Android.bp b/services/core/Android.bp index 7a4f1de95e0c..9d190087e300 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -93,6 +93,7 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [ ":statslog-art-java-gen", + ":services.bluetooth-sources", // TODO(b/214988855) : Remove once apex is ready ":services.core-sources", ":services.core.protologsrc", ":dumpstate_aidl", @@ -165,6 +166,9 @@ java_library_static { "overlayable_policy_aidl-java", "SurfaceFlingerProperties", "com.android.sysprop.watchdog", + // This is used for services.connectivity-tiramisu-sources. + // TODO: delete when NetworkStatsService is moved to the mainline module. + "net-utils-device-common-bpf", ], javac_shard_size: 50, } diff --git a/services/core/java/android/app/usage/OWNERS b/services/core/java/android/app/usage/OWNERS new file mode 100644 index 000000000000..3a555143b11d --- /dev/null +++ b/services/core/java/android/app/usage/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/core/java/android/app/usage/OWNERS diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java deleted file mode 100644 index 380b1f37b981..000000000000 --- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.annotation.RequiresPermission; -import android.content.Context; -import android.database.ContentObserver; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.provider.Settings; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -/** - * The BluetoothAirplaneModeListener handles system airplane mode change callback and checks - * whether we need to inform BluetoothManagerService on this change. - * - * The information of airplane mode turns on would not be passed to the BluetoothManagerService - * when Bluetooth is on and Bluetooth is in one of the following situations: - * 1. Bluetooth A2DP is connected. - * 2. Bluetooth Hearing Aid profile is connected. - * 3. Bluetooth LE Audio is connected - */ -class BluetoothAirplaneModeListener { - private static final String TAG = "BluetoothAirplaneModeListener"; - @VisibleForTesting static final String TOAST_COUNT = "bluetooth_airplane_toast_count"; - - private static final int MSG_AIRPLANE_MODE_CHANGED = 0; - - @VisibleForTesting static final int MAX_TOAST_COUNT = 10; // 10 times - - private final BluetoothManagerService mBluetoothManager; - private final BluetoothAirplaneModeHandler mHandler; - private BluetoothModeChangeHelper mAirplaneHelper; - - @VisibleForTesting int mToastCount = 0; - - BluetoothAirplaneModeListener(BluetoothManagerService service, Looper looper, Context context) { - mBluetoothManager = service; - - mHandler = new BluetoothAirplaneModeHandler(looper); - context.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, - mAirplaneModeObserver); - } - - private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { - @Override - public void onChange(boolean unused) { - // Post from system main thread to android_io thread. - Message msg = mHandler.obtainMessage(MSG_AIRPLANE_MODE_CHANGED); - mHandler.sendMessage(msg); - } - }; - - private class BluetoothAirplaneModeHandler extends Handler { - BluetoothAirplaneModeHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_AIRPLANE_MODE_CHANGED: - handleAirplaneModeChange(); - break; - default: - Log.e(TAG, "Invalid message: " + msg.what); - break; - } - } - } - - /** - * Call after boot complete - */ - @VisibleForTesting - void start(BluetoothModeChangeHelper helper) { - Log.i(TAG, "start"); - mAirplaneHelper = helper; - mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT); - } - - @VisibleForTesting - boolean shouldPopToast() { - if (mToastCount >= MAX_TOAST_COUNT) { - return false; - } - mToastCount++; - mAirplaneHelper.setSettingsInt(TOAST_COUNT, mToastCount); - return true; - } - - @VisibleForTesting - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - void handleAirplaneModeChange() { - if (shouldSkipAirplaneModeChange()) { - Log.i(TAG, "Ignore airplane mode change"); - // Airplane mode enabled when Bluetooth is being used for audio/headering aid. - // Bluetooth is not disabled in such case, only state is changed to - // BLUETOOTH_ON_AIRPLANE mode. - mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON, - BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); - if (shouldPopToast()) { - mAirplaneHelper.showToastMessage(); - } - return; - } - if (mAirplaneHelper != null) { - mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager); - } - } - - @VisibleForTesting - boolean shouldSkipAirplaneModeChange() { - if (mAirplaneHelper == null) { - return false; - } - if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn() - || !mAirplaneHelper.isMediaProfileConnected()) { - return false; - } - return true; - } -} diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java deleted file mode 100644 index 611a37de70f4..000000000000 --- a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.provider.DeviceConfig; -import android.util.Slog; - -import java.util.ArrayList; - -/** - * The BluetoothDeviceConfigListener handles system device config change callback and checks - * whether we need to inform BluetoothManagerService on this change. - * - * The information of device config change would not be passed to the BluetoothManagerService - * when Bluetooth is on and Bluetooth is in one of the following situations: - * 1. Bluetooth A2DP is connected. - * 2. Bluetooth Hearing Aid profile is connected. - */ -class BluetoothDeviceConfigListener { - private static final String TAG = "BluetoothDeviceConfigListener"; - - private final BluetoothManagerService mService; - private final boolean mLogDebug; - - BluetoothDeviceConfigListener(BluetoothManagerService service, boolean logDebug) { - mService = service; - mLogDebug = logDebug; - DeviceConfig.addOnPropertiesChangedListener( - DeviceConfig.NAMESPACE_BLUETOOTH, - (Runnable r) -> r.run(), - mDeviceConfigChangedListener); - } - - private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener = - new DeviceConfig.OnPropertiesChangedListener() { - @Override - public void onPropertiesChanged(DeviceConfig.Properties properties) { - if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) { - return; - } - if (mLogDebug) { - ArrayList<String> flags = new ArrayList<>(); - for (String name : properties.getKeyset()) { - flags.add(name + "='" + properties.getString(name, "") + "'"); - } - Slog.d(TAG, "onPropertiesChanged: " + String.join(",", flags)); - } - boolean foundInit = false; - for (String name : properties.getKeyset()) { - if (name.startsWith("INIT_")) { - foundInit = true; - break; - } - } - if (!foundInit) { - return; - } - mService.onInitFlagsChanged(); - } - }; - -} diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java deleted file mode 100644 index 450e9881bef2..000000000000 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ /dev/null @@ -1,2955 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.content.PermissionChecker.PERMISSION_HARD_DENIED; -import static android.content.PermissionChecker.PID_UNKNOWN; -import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED; -import static android.os.UserHandle.USER_SYSTEM; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; -import android.app.ActivityManager; -import android.app.AppGlobals; -import android.app.AppOpsManager; -import android.app.BroadcastOptions; -import android.bluetooth.BluetoothA2dp; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothHearingAid; -import android.bluetooth.BluetoothLeAudio; -import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothProtoEnums; -import android.bluetooth.IBluetooth; -import android.bluetooth.IBluetoothCallback; -import android.bluetooth.IBluetoothGatt; -import android.bluetooth.IBluetoothHeadset; -import android.bluetooth.IBluetoothManager; -import android.bluetooth.IBluetoothManagerCallback; -import android.bluetooth.IBluetoothProfileServiceConnection; -import android.bluetooth.IBluetoothStateChangeCallback; -import android.content.ActivityNotFoundException; -import android.content.AttributionSource; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.PermissionChecker; -import android.content.ServiceConnection; -import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; -import android.content.pm.UserInfo; -import android.database.ContentObserver; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.PowerExemptionManager; -import android.os.Process; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; -import android.text.TextUtils; -import android.util.FeatureFlagUtils; -import android.util.Log; -import android.util.Slog; -import android.util.proto.ProtoOutputStream; - -import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.DumpUtils; -import com.android.internal.util.FrameworkStatsLog; -import com.android.server.pm.UserManagerInternal; -import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; -import com.android.server.pm.UserRestrictionsUtils; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Locale; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -class BluetoothManagerService extends IBluetoothManager.Stub { - private static final String TAG = "BluetoothManagerService"; - private static final boolean DBG = true; - - private static final String BLUETOOTH_PRIVILEGED = - android.Manifest.permission.BLUETOOTH_PRIVILEGED; - - private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid"; - private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS = "bluetooth_address"; - private static final String SECURE_SETTINGS_BLUETOOTH_NAME = "bluetooth_name"; - - private static final int ACTIVE_LOG_MAX_SIZE = 20; - private static final int CRASH_LOG_MAX_SIZE = 100; - - private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind - //Maximum msec to wait for service restart - private static final int SERVICE_RESTART_TIME_MS = 400; - //Maximum msec to wait for restart due to error - private static final int ERROR_RESTART_TIME_MS = 3000; - //Maximum msec to delay MESSAGE_USER_SWITCHED - private static final int USER_SWITCHED_TIME_MS = 200; - // Delay for the addProxy function in msec - private static final int ADD_PROXY_DELAY_MS = 100; - // Delay for retrying enable and disable in msec - private static final int ENABLE_DISABLE_DELAY_MS = 300; - private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300; - private static final int DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS = 86400; - - private static final int MESSAGE_ENABLE = 1; - private static final int MESSAGE_DISABLE = 2; - private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3; - private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4; - private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30; - private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31; - private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40; - private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41; - private static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42; - private static final int MESSAGE_BLUETOOTH_STATE_CHANGE = 60; - private static final int MESSAGE_TIMEOUT_BIND = 100; - private static final int MESSAGE_TIMEOUT_UNBIND = 101; - private static final int MESSAGE_GET_NAME_AND_ADDRESS = 200; - private static final int MESSAGE_USER_SWITCHED = 300; - private static final int MESSAGE_USER_UNLOCKED = 301; - private static final int MESSAGE_ADD_PROXY_DELAYED = 400; - private static final int MESSAGE_BIND_PROFILE_SERVICE = 401; - private static final int MESSAGE_RESTORE_USER_SETTING = 500; - private static final int MESSAGE_INIT_FLAGS_CHANGED = 600; - - private static final int RESTORE_SETTING_TO_ON = 1; - private static final int RESTORE_SETTING_TO_OFF = 0; - - private static final int MAX_ERROR_RESTART_RETRIES = 6; - private static final int MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES = 10; - - // Bluetooth persisted setting is off - private static final int BLUETOOTH_OFF = 0; - // Bluetooth persisted setting is on - // and Airplane mode won't affect Bluetooth state at start up - private static final int BLUETOOTH_ON_BLUETOOTH = 1; - // Bluetooth persisted setting is on - // but Airplane mode will affect Bluetooth state at start up - // and Airplane mode will have higher priority. - @VisibleForTesting - static final int BLUETOOTH_ON_AIRPLANE = 2; - - private static final int SERVICE_IBLUETOOTH = 1; - private static final int SERVICE_IBLUETOOTHGATT = 2; - - private final Context mContext; - - // Locks are not provided for mName and mAddress. - // They are accessed in handler or broadcast receiver, same thread context. - private String mAddress; - private String mName; - private final ContentResolver mContentResolver; - private final int mUserId; - private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks; - private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks; - private IBinder mBluetoothBinder; - private IBluetooth mBluetooth; - private IBluetoothGatt mBluetoothGatt; - private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock(); - private boolean mBinding; - private boolean mUnbinding; - - private BluetoothModeChangeHelper mBluetoothModeChangeHelper; - - private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; - - private BluetoothDeviceConfigListener mBluetoothDeviceConfigListener; - - // used inside handler thread - private boolean mQuietEnable = false; - private boolean mEnable; - - private static CharSequence timeToLog(long timestamp) { - return android.text.format.DateFormat.format("MM-dd HH:mm:ss", timestamp); - } - - /** - * Used for tracking apps that enabled / disabled Bluetooth. - */ - private class ActiveLog { - private int mReason; - private String mPackageName; - private boolean mEnable; - private long mTimestamp; - - ActiveLog(int reason, String packageName, boolean enable, long timestamp) { - mReason = reason; - mPackageName = packageName; - mEnable = enable; - mTimestamp = timestamp; - } - - public String toString() { - return timeToLog(mTimestamp) + (mEnable ? " Enabled " : " Disabled ") - + " due to " + getEnableDisableReasonString(mReason) + " by " + mPackageName; - } - - void dump(ProtoOutputStream proto) { - proto.write(BluetoothManagerServiceDumpProto.ActiveLog.TIMESTAMP_MS, mTimestamp); - proto.write(BluetoothManagerServiceDumpProto.ActiveLog.ENABLE, mEnable); - proto.write(BluetoothManagerServiceDumpProto.ActiveLog.PACKAGE_NAME, mPackageName); - proto.write(BluetoothManagerServiceDumpProto.ActiveLog.REASON, mReason); - } - } - - private final LinkedList<ActiveLog> mActiveLogs = new LinkedList<>(); - private final LinkedList<Long> mCrashTimestamps = new LinkedList<>(); - private int mCrashes; - private long mLastEnabledTime; - - // configuration from external IBinder call which is used to - // synchronize with broadcast receiver. - private boolean mQuietEnableExternal; - private boolean mEnableExternal; - - // Map of apps registered to keep BLE scanning on. - private Map<IBinder, ClientDeathRecipient> mBleApps = - new ConcurrentHashMap<IBinder, ClientDeathRecipient>(); - - private int mState; - private final BluetoothHandler mHandler; - private int mErrorRecoveryRetryCounter; - private final int mSystemUiUid; - - private boolean mIsHearingAidProfileSupported; - - private AppOpsManager mAppOps; - - // Save a ProfileServiceConnections object for each of the bound - // bluetooth profile services - private final Map<Integer, ProfileServiceConnections> mProfileServices = new HashMap<>(); - - private final boolean mWirelessConsentRequired; - - private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { - @Override - public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { - Message msg = - mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState); - mHandler.sendMessage(msg); - } - }; - - private final UserRestrictionsListener mUserRestrictionsListener = - new UserRestrictionsListener() { - @Override - public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, - Bundle prevRestrictions) { - - if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions, - UserManager.DISALLOW_BLUETOOTH_SHARING)) { - updateOppLauncherComponentState(userId, - newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING)); - } - - // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user. - if (userId == USER_SYSTEM - && UserRestrictionsUtils.restrictionsChanged(prevRestrictions, - newRestrictions, UserManager.DISALLOW_BLUETOOTH)) { - if (userId == USER_SYSTEM && newRestrictions.getBoolean( - UserManager.DISALLOW_BLUETOOTH)) { - updateOppLauncherComponentState(userId, true); // Sharing disallowed - sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED, - mContext.getPackageName()); - } else { - updateOppLauncherComponentState(userId, newRestrictions.getBoolean( - UserManager.DISALLOW_BLUETOOTH_SHARING)); - } - } - } - }; - - @VisibleForTesting - public void onInitFlagsChanged() { - mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); - mHandler.sendEmptyMessageDelayed( - MESSAGE_INIT_FLAGS_CHANGED, - DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS); - } - - public boolean onFactoryReset(AttributionSource attributionSource) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, - "Need BLUETOOTH_PRIVILEGED permission"); - - // Wait for stable state if bluetooth is temporary state. - int state = getState(); - if (state == BluetoothAdapter.STATE_BLE_TURNING_ON - || state == BluetoothAdapter.STATE_TURNING_ON - || state == BluetoothAdapter.STATE_TURNING_OFF) { - if (!waitForState(Set.of(BluetoothAdapter.STATE_BLE_ON, BluetoothAdapter.STATE_ON))) { - return false; - } - } - - // Clear registered LE apps to force shut-off Bluetooth - clearBleApps(); - state = getState(); - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth == null) { - return false; - } - if (state == BluetoothAdapter.STATE_BLE_ON) { - addActiveLog( - BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, - mContext.getPackageName(), false); - mBluetooth.onBrEdrDown(attributionSource); - return true; - } else if (state == BluetoothAdapter.STATE_ON) { - addActiveLog( - BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET, - mContext.getPackageName(), false); - mBluetooth.disable(attributionSource); - return true; - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to shutdown Bluetooth", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - return false; - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public void onAirplaneModeChanged() { - synchronized (this) { - if (isBluetoothPersistedStateOn()) { - if (isAirplaneModeOn()) { - persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); - } else { - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - } - } - - int st = BluetoothAdapter.STATE_OFF; - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - st = mBluetooth.getState(); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call getState", e); - return; - } finally { - mBluetoothLock.readLock().unlock(); - } - - Slog.d(TAG, - "Airplane Mode change - current state: " + BluetoothAdapter.nameForState( - st) + ", isAirplaneModeOn()=" + isAirplaneModeOn()); - - if (isAirplaneModeOn()) { - // Clear registered LE apps to force shut-off - clearBleApps(); - - // If state is BLE_ON make sure we trigger disableBLE - if (st == BluetoothAdapter.STATE_BLE_ON) { - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - addActiveLog( - BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, - mContext.getPackageName(), false); - mBluetooth.onBrEdrDown(mContext.getAttributionSource()); - mEnable = false; - mEnableExternal = false; - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBrEdrDown", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - } else if (st == BluetoothAdapter.STATE_ON) { - sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, - mContext.getPackageName()); - } - } else if (mEnableExternal) { - sendEnableMsg(mQuietEnableExternal, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, - mContext.getPackageName()); - } - } - } - - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) { - String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME); - if (DBG) { - Slog.d(TAG, "Bluetooth Adapter name changed to " + newName + " by " - + mContext.getPackageName()); - } - if (newName != null) { - storeNameAndAddress(newName, null); - } - } else if (BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED.equals(action)) { - String newAddress = intent.getStringExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS); - if (newAddress != null) { - if (DBG) { - Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress); - } - storeNameAndAddress(null, newAddress); - } else { - if (DBG) { - Slog.e(TAG, "No Bluetooth Adapter address parameter found"); - } - } - } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) { - final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME); - if (Settings.Global.BLUETOOTH_ON.equals(name)) { - // The Bluetooth On state may be changed during system restore. - final String prevValue = - intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE); - final String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE); - - if (DBG) { - Slog.d(TAG, - "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" + prevValue - + ", newValue=" + newValue); - } - - if ((newValue != null) && (prevValue != null) && !prevValue.equals(newValue)) { - Message msg = mHandler.obtainMessage(MESSAGE_RESTORE_USER_SETTING, - newValue.equals("0") ? RESTORE_SETTING_TO_OFF - : RESTORE_SETTING_TO_ON, 0); - mHandler.sendMessage(msg); - } - } - } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action) - || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action) - || BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED.equals(action)) { - final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, - BluetoothProfile.STATE_CONNECTED); - if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED) - && state == BluetoothProfile.STATE_DISCONNECTED - && !mBluetoothModeChangeHelper.isMediaProfileConnected()) { - Slog.i(TAG, "Device disconnected, reactivating pending flag changes"); - onInitFlagsChanged(); - } - } - } - }; - - BluetoothManagerService(Context context) { - mHandler = new BluetoothHandler(IoThread.get().getLooper()); - - mContext = context; - - mWirelessConsentRequired = context.getResources() - .getBoolean(com.android.internal.R.bool.config_wirelessConsentRequired); - - mCrashes = 0; - mBluetooth = null; - mBluetoothBinder = null; - mBluetoothGatt = null; - mBinding = false; - mUnbinding = false; - mEnable = false; - mState = BluetoothAdapter.STATE_OFF; - mQuietEnableExternal = false; - mEnableExternal = false; - mAddress = null; - mName = null; - mErrorRecoveryRetryCounter = 0; - mContentResolver = context.getContentResolver(); - mUserId = mContentResolver.getUserId(); - // Observe BLE scan only mode settings change. - registerForBleScanModeChange(); - mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>(); - mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>(); - - mIsHearingAidProfileSupported = context.getResources() - .getBoolean(com.android.internal.R.bool.config_hearing_aid_profile_supported); - - // TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils - String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS); - if (!TextUtils.isEmpty(value)) { - boolean isHearingAidEnabled = Boolean.parseBoolean(value); - Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled); - FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled); - if (isHearingAidEnabled && !mIsHearingAidProfileSupported) { - // Overwrite to enable support by FeatureFlag - mIsHearingAidProfileSupported = true; - } - } - - IntentFilter filter = new IntentFilter(); - filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); - filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); - filter.addAction(Intent.ACTION_SETTING_RESTORED); - filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); - filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - mContext.registerReceiver(mReceiver, filter); - - loadStoredNameAndAddress(); - if (isBluetoothPersistedStateOn()) { - if (DBG) { - Slog.d(TAG, "Startup: Bluetooth persisted state is ON."); - } - mEnableExternal = true; - } - - String airplaneModeRadios = - Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS); - if (airplaneModeRadios == null || airplaneModeRadios.contains( - Settings.Global.RADIO_BLUETOOTH)) { - mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener( - this, IoThread.get().getLooper(), context); - } - - int systemUiUid = -1; - // Check if device is configured with no home screen, which implies no SystemUI. - boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen); - if (!noHome) { - PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); - systemUiUid = pm.getPackageUid(pm.getSystemUiServiceComponent().getPackageName(), - MATCH_SYSTEM_ONLY, USER_SYSTEM); - } - if (systemUiUid >= 0) { - Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid)); - } else { - // Some platforms, such as wearables do not have a system ui. - Slog.w(TAG, "Unable to resolve SystemUI's UID."); - } - mSystemUiUid = systemUiUid; - } - - /** - * Returns true if airplane mode is currently on - */ - private boolean isAirplaneModeOn() { - return Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 0) == 1; - } - - private boolean supportBluetoothPersistedState() { - return mContext.getResources().getBoolean(R.bool.config_supportBluetoothPersistedState); - } - - /** - * Returns true if the Bluetooth saved state is "on" - */ - private boolean isBluetoothPersistedStateOn() { - if (!supportBluetoothPersistedState()) { - return false; - } - int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1); - if (DBG) { - Slog.d(TAG, "Bluetooth persisted state: " + state); - } - return state != BLUETOOTH_OFF; - } - - private boolean isBluetoothPersistedStateOnAirplane() { - if (!supportBluetoothPersistedState()) { - return false; - } - int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1); - if (DBG) { - Slog.d(TAG, "Bluetooth persisted state: " + state); - } - return state == BLUETOOTH_ON_AIRPLANE; - } - - /** - * Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH - */ - private boolean isBluetoothPersistedStateOnBluetooth() { - if (!supportBluetoothPersistedState()) { - return false; - } - return Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, - BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH; - } - - /** - * Save the Bluetooth on/off state - */ - private void persistBluetoothSetting(int value) { - if (DBG) { - Slog.d(TAG, "Persisting Bluetooth Setting: " + value); - } - // waive WRITE_SECURE_SETTINGS permission check - final long callingIdentity = Binder.clearCallingIdentity(); - try { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.BLUETOOTH_ON, value); - } finally { - Binder.restoreCallingIdentity(callingIdentity); - } - } - - /** - * Returns true if the Bluetooth Adapter's name and address is - * locally cached - * @return - */ - private boolean isNameAndAddressSet() { - return mName != null && mAddress != null && mName.length() > 0 && mAddress.length() > 0; - } - - /** - * Retrieve the Bluetooth Adapter's name and address and save it in - * in the local cache - */ - private void loadStoredNameAndAddress() { - if (DBG) { - Slog.d(TAG, "Loading stored name and address"); - } - if (mContext.getResources() - .getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation) - && Settings.Secure.getIntForUser(mContentResolver, - SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0, mUserId) - == 0) { - // if the valid flag is not set, don't load the address and name - if (DBG) { - Slog.d(TAG, "invalid bluetooth name and address stored"); - } - return; - } - mName = Settings.Secure.getStringForUser( - mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, mUserId); - mAddress = Settings.Secure.getStringForUser( - mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, mUserId); - if (DBG) { - Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress); - } - } - - /** - * Save the Bluetooth name and address in the persistent store. - * Only non-null values will be saved. - * @param name - * @param address - */ - private void storeNameAndAddress(String name, String address) { - if (name != null) { - Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name, - mUserId); - mName = name; - if (DBG) { - Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getStringForUser( - mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, - mUserId)); - } - } - - if (address != null) { - Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, - address, mUserId); - mAddress = address; - if (DBG) { - Slog.d(TAG, - "Stored Bluetoothaddress: " + Settings.Secure.getStringForUser( - mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, - mUserId)); - } - } - - if ((name != null) && (address != null)) { - Settings.Secure.putIntForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1, - mUserId); - } - } - - public IBluetooth registerAdapter(IBluetoothManagerCallback callback) { - if (callback == null) { - Slog.w(TAG, "Callback is null in registerAdapter"); - return null; - } - synchronized (mCallbacks) { - mCallbacks.register(callback); - } - return mBluetooth; - } - - public void unregisterAdapter(IBluetoothManagerCallback callback) { - if (callback == null) { - Slog.w(TAG, "Callback is null in unregisterAdapter"); - return; - } - synchronized (mCallbacks) { - mCallbacks.unregister(callback); - } - } - - public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { - if (callback == null) { - Slog.w(TAG, "registerStateChangeCallback: Callback is null!"); - return; - } - Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK); - msg.obj = callback; - mHandler.sendMessage(msg); - } - - public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) { - if (callback == null) { - Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!"); - return; - } - Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK); - msg.obj = callback; - mHandler.sendMessage(msg); - } - - public boolean isEnabled() { - return getState() == BluetoothAdapter.STATE_ON; - } - - public int getState() { - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG, "getState(): report OFF for non-active and non system user"); - return BluetoothAdapter.STATE_OFF; - } - - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - return mBluetooth.getState(); - } - } catch (RemoteException e) { - Slog.e(TAG, "getState()", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - return BluetoothAdapter.STATE_OFF; - } - - class ClientDeathRecipient implements IBinder.DeathRecipient { - private String mPackageName; - - ClientDeathRecipient(String packageName) { - mPackageName = packageName; - } - - public void binderDied() { - if (DBG) { - Slog.d(TAG, "Binder is dead - unregister " + mPackageName); - } - - for (Map.Entry<IBinder, ClientDeathRecipient> entry : mBleApps.entrySet()) { - IBinder token = entry.getKey(); - ClientDeathRecipient deathRec = entry.getValue(); - if (deathRec.equals(this)) { - updateBleAppCount(token, false, mPackageName); - break; - } - } - } - - public String getPackageName() { - return mPackageName; - } - } - - @Override - public boolean isBleScanAlwaysAvailable() { - if (isAirplaneModeOn() && !mEnable) { - return false; - } - try { - return Settings.Global.getInt(mContentResolver, - Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE) != 0; - } catch (SettingNotFoundException e) { - } - return false; - } - - @Override - public boolean isHearingAidProfileSupported() { - return mIsHearingAidProfileSupported; - } - - private boolean isDeviceProvisioned() { - return Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED, - 0) != 0; - } - - // Monitor change of BLE scan only mode settings. - private void registerForProvisioningStateChange() { - ContentObserver contentObserver = new ContentObserver(null) { - @Override - public void onChange(boolean selfChange) { - if (!isDeviceProvisioned()) { - if (DBG) { - Slog.d(TAG, "DEVICE_PROVISIONED setting changed, but device is not " - + "provisioned"); - } - return; - } - if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)) { - Slog.i(TAG, "Device provisioned, reactivating pending flag changes"); - onInitFlagsChanged(); - } - } - }; - - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), false, - contentObserver); - } - - // Monitor change of BLE scan only mode settings. - private void registerForBleScanModeChange() { - ContentObserver contentObserver = new ContentObserver(null) { - @Override - public void onChange(boolean selfChange) { - if (isBleScanAlwaysAvailable()) { - // Nothing to do - return; - } - // BLE scan is not available. - disableBleScanMode(); - clearBleApps(); - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, - mContext.getPackageName(), false); - mBluetooth.onBrEdrDown(mContext.getAttributionSource()); - } - } catch (RemoteException e) { - Slog.e(TAG, "error when disabling bluetooth", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - } - }; - - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE), false, - contentObserver); - } - - // Disable ble scan only mode. - private void disableBleScanMode() { - try { - mBluetoothLock.writeLock().lock(); - if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) { - if (DBG) { - Slog.d(TAG, "Reseting the mEnable flag for clean disable"); - } - mEnable = false; - } - } catch (RemoteException e) { - Slog.e(TAG, "getState()", e); - } finally { - mBluetoothLock.writeLock().unlock(); - } - } - - private int updateBleAppCount(IBinder token, boolean enable, String packageName) { - ClientDeathRecipient r = mBleApps.get(token); - if (r == null && enable) { - ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName); - try { - token.linkToDeath(deathRec, 0); - } catch (RemoteException ex) { - throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!"); - } - mBleApps.put(token, deathRec); - if (DBG) { - Slog.d(TAG, "Registered for death of " + packageName); - } - } else if (!enable && r != null) { - // Unregister death recipient as the app goes away. - token.unlinkToDeath(r, 0); - mBleApps.remove(token); - if (DBG) { - Slog.d(TAG, "Unregistered for death of " + packageName); - } - } - int appCount = mBleApps.size(); - if (DBG) { - Slog.d(TAG, appCount + " registered Ble Apps"); - } - return appCount; - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private boolean checkBluetoothPermissions(AttributionSource attributionSource, String message, - boolean requireForeground) { - if (isBluetoothDisallowed()) { - if (DBG) { - Slog.d(TAG, "checkBluetoothPermissions: bluetooth disallowed"); - } - return false; - } - // Check if packageName belongs to callingUid - final int callingUid = Binder.getCallingUid(); - final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if (!isCallerSystem) { - checkPackage(callingUid, attributionSource.getPackageName()); - - if (requireForeground && !checkIfCallerIsForegroundUser()) { - Slog.w(TAG, "Not allowed for non-active and non system user"); - return false; - } - - if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, message)) { - return false; - } - } - return true; - } - - public boolean enableBle(AttributionSource attributionSource, IBinder token) - throws RemoteException { - final String packageName = attributionSource.getPackageName(); - if (!checkBluetoothPermissions(attributionSource, "enableBle", false)) { - if (DBG) { - Slog.d(TAG, "enableBle(): bluetooth disallowed"); - } - return false; - } - - if (DBG) { - Slog.d(TAG, "enableBle(" + packageName + "): mBluetooth =" + mBluetooth - + " mBinding = " + mBinding + " mState = " - + BluetoothAdapter.nameForState(mState)); - } - updateBleAppCount(token, true, packageName); - - if (mState == BluetoothAdapter.STATE_ON - || mState == BluetoothAdapter.STATE_BLE_ON - || mState == BluetoothAdapter.STATE_TURNING_ON - || mState == BluetoothAdapter.STATE_TURNING_OFF - || mState == BluetoothAdapter.STATE_BLE_TURNING_ON) { - Log.d(TAG, "enableBLE(): Bluetooth is already enabled or is turning on"); - return true; - } - synchronized (mReceiver) { - // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false, BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, - packageName, true); - } - return true; - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean disableBle(AttributionSource attributionSource, IBinder token) - throws RemoteException { - final String packageName = attributionSource.getPackageName(); - if (!checkBluetoothPermissions(attributionSource, "disableBle", false)) { - if (DBG) { - Slog.d(TAG, "disableBLE(): bluetooth disallowed"); - } - return false; - } - - if (DBG) { - Slog.d(TAG, "disableBle(" + packageName + "): mBluetooth =" + mBluetooth - + " mBinding = " + mBinding + " mState = " - + BluetoothAdapter.nameForState(mState)); - } - - if (mState == BluetoothAdapter.STATE_OFF) { - Slog.d(TAG, "disableBLE(): Already disabled"); - return false; - } - updateBleAppCount(token, false, packageName); - - if (mState == BluetoothAdapter.STATE_BLE_ON && !isBleAppPresent()) { - if (mEnable) { - disableBleScanMode(); - } - if (!mEnableExternal) { - addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, - packageName, false); - sendBrEdrDownCallback(attributionSource); - } - } - return true; - } - - // Clear all apps using BLE scan only mode. - private void clearBleApps() { - mBleApps.clear(); - } - - /** @hide */ - public boolean isBleAppPresent() { - if (DBG) { - Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size()); - } - return mBleApps.size() > 0; - } - - /** - * Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on, - * call IBluetooth.onBrEdrDown() to disable if Bluetooth should be off. - */ - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - private void continueFromBleOnState() { - if (DBG) { - Slog.d(TAG, "continueFromBleOnState()"); - } - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth == null) { - Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!"); - return; - } - if (!mEnableExternal && !isBleAppPresent()) { - Slog.i(TAG, "Bluetooth was disabled while enabling BLE, disable BLE now"); - mEnable = false; - mBluetooth.onBrEdrDown(mContext.getAttributionSource()); - return; - } - if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { - // This triggers transition to STATE_ON - mBluetooth.onLeServiceUp(mContext.getAttributionSource()); - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onServiceUp", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - } - - /** - * Inform BluetoothAdapter instances that BREDR part is down - * and turn off all service and stack if no LE app needs it - */ - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - private void sendBrEdrDownCallback(AttributionSource attributionSource) { - if (DBG) { - Slog.d(TAG, "Calling sendBrEdrDownCallback callbacks"); - } - - if (mBluetooth == null) { - Slog.w(TAG, "Bluetooth handle is null"); - return; - } - - if (isBleAppPresent()) { - // Need to stay at BLE ON. Disconnect all Gatt connections - try { - mBluetoothGatt.unregAll(attributionSource); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to disconnect all apps.", e); - } - } else { - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - mBluetooth.onBrEdrDown(attributionSource); - } - } catch (RemoteException e) { - Slog.e(TAG, "Call to onBrEdrDown() failed.", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - } - - } - - public boolean enableNoAutoConnect(AttributionSource attributionSource) { - final String packageName = attributionSource.getPackageName(); - if (!checkBluetoothPermissions(attributionSource, "enableNoAutoConnect", false)) { - if (DBG) { - Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed"); - } - return false; - } - - if (DBG) { - Slog.d(TAG, "enableNoAutoConnect(): mBluetooth =" + mBluetooth + " mBinding = " - + mBinding); - } - - int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); - if (callingAppId != Process.NFC_UID) { - throw new SecurityException("no permission to enable Bluetooth quietly"); - } - - synchronized (mReceiver) { - mQuietEnableExternal = true; - mEnableExternal = true; - sendEnableMsg(true, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); - } - return true; - } - - public boolean enable(AttributionSource attributionSource) throws RemoteException { - final String packageName = attributionSource.getPackageName(); - if (!checkBluetoothPermissions(attributionSource, "enable", true)) { - if (DBG) { - Slog.d(TAG, "enable(): not enabling - bluetooth disallowed"); - } - return false; - } - - final int callingUid = Binder.getCallingUid(); - final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if (!callerSystem && !isEnabled() && mWirelessConsentRequired - && startConsentUiIfNeeded(packageName, - callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) { - return false; - } - - if (DBG) { - Slog.d(TAG, "enable(" + packageName + "): mBluetooth =" + mBluetooth + " mBinding = " - + mBinding + " mState = " + BluetoothAdapter.nameForState(mState)); - } - - synchronized (mReceiver) { - mQuietEnableExternal = false; - mEnableExternal = true; - // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); - } - if (DBG) { - Slog.d(TAG, "enable returning"); - } - return true; - } - - public boolean disable(AttributionSource attributionSource, boolean persist) - throws RemoteException { - if (!persist) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, - "Need BLUETOOTH_PRIVILEGED permission"); - } - - final String packageName = attributionSource.getPackageName(); - if (!checkBluetoothPermissions(attributionSource, "disable", true)) { - if (DBG) { - Slog.d(TAG, "disable(): not disabling - bluetooth disallowed"); - } - return false; - } - - final int callingUid = Binder.getCallingUid(); - final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if (!callerSystem && isEnabled() && mWirelessConsentRequired - && startConsentUiIfNeeded(packageName, - callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) { - return false; - } - - if (DBG) { - Slog.d(TAG, "disable(): mBluetooth = " + mBluetooth + " mBinding = " + mBinding); - } - - synchronized (mReceiver) { - if (!isBluetoothPersistedStateOnAirplane()) { - if (persist) { - persistBluetoothSetting(BLUETOOTH_OFF); - } - mEnableExternal = false; - } - sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, - packageName); - } - return true; - } - - private boolean startConsentUiIfNeeded(String packageName, - int callingUid, String intentAction) throws RemoteException { - if (checkBluetoothPermissionWhenWirelessConsentRequired()) { - return false; - } - try { - // Validate the package only if we are going to use it - ApplicationInfo applicationInfo = mContext.getPackageManager() - .getApplicationInfoAsUser(packageName, - PackageManager.MATCH_DEBUG_TRIAGED_MISSING, - UserHandle.getUserId(callingUid)); - if (applicationInfo.uid != callingUid) { - throw new SecurityException("Package " + packageName - + " not in uid " + callingUid); - } - - Intent intent = new Intent(intentAction); - intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); - intent.setFlags( - Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - try { - mContext.startActivity(intent); - } catch (ActivityNotFoundException e) { - // Shouldn't happen - Slog.e(TAG, "Intent to handle action " + intentAction + " missing"); - return false; - } - return true; - } catch (PackageManager.NameNotFoundException e) { - throw new RemoteException(e.getMessage()); - } - } - - /** - * Check if AppOpsManager is available and the packageName belongs to uid - * - * A null package belongs to any uid - */ - private void checkPackage(int uid, String packageName) { - if (mAppOps == null) { - Slog.w(TAG, "checkPackage(): called before system boot up, uid " - + uid + ", packageName " + packageName); - throw new IllegalStateException("System has not boot yet"); - } - if (packageName == null) { - Slog.w(TAG, "checkPackage(): called with null packageName from " + uid); - return; - } - try { - mAppOps.checkPackage(uid, packageName); - } catch (SecurityException e) { - Slog.w(TAG, "checkPackage(): " + packageName + " does not belong to uid " + uid); - throw new SecurityException(e.getMessage()); - } - } - - /** - * Check if the caller must still pass permission check or if the caller is exempted - * from the consent UI via the MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED check. - * - * Commands from some callers may be exempted from triggering the consent UI when - * enabling bluetooth. This exemption is checked via the - * MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED and allows calls to skip - * the consent UI where it may otherwise be required. - * - * @hide - */ - @SuppressLint("AndroidFrameworkRequiresPermission") - private boolean checkBluetoothPermissionWhenWirelessConsentRequired() { - int result = mContext.checkCallingPermission( - android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED); - return result == PackageManager.PERMISSION_GRANTED; - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public void unbindAndFinish() { - if (DBG) { - Slog.d(TAG, "unbindAndFinish(): " + mBluetooth + " mBinding = " + mBinding - + " mUnbinding = " + mUnbinding); - } - - try { - mBluetoothLock.writeLock().lock(); - if (mUnbinding) { - return; - } - mUnbinding = true; - mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); - mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE); - if (mBluetooth != null) { - //Unregister callback object - try { - mBluetooth.unregisterCallback(mBluetoothCallback, - mContext.getAttributionSource()); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to unregister BluetoothCallback", re); - } - mBluetoothBinder = null; - mBluetooth = null; - mContext.unbindService(mConnection); - mUnbinding = false; - mBinding = false; - } else { - mUnbinding = false; - } - mBluetoothGatt = null; - } finally { - mBluetoothLock.writeLock().unlock(); - } - } - - public IBluetoothGatt getBluetoothGatt() { - // sync protection - return mBluetoothGatt; - } - - @Override - public boolean bindBluetoothProfileService(int bluetoothProfile, - IBluetoothProfileServiceConnection proxy) { - if (mState != BluetoothAdapter.STATE_ON) { - if (DBG) { - Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile - + ", while Bluetooth was disabled"); - } - return false; - } - synchronized (mProfileServices) { - ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile)); - if (psc == null) { - if (DBG) { - Slog.d(TAG, "Creating new ProfileServiceConnections object for" + " profile: " - + bluetoothProfile); - } - - if (bluetoothProfile != BluetoothProfile.HEADSET) { - return false; - } - - Intent intent = new Intent(IBluetoothHeadset.class.getName()); - psc = new ProfileServiceConnections(intent); - if (!psc.bindService()) { - return false; - } - - mProfileServices.put(new Integer(bluetoothProfile), psc); - } - } - - // Introducing a delay to give the client app time to prepare - Message addProxyMsg = mHandler.obtainMessage(MESSAGE_ADD_PROXY_DELAYED); - addProxyMsg.arg1 = bluetoothProfile; - addProxyMsg.obj = proxy; - mHandler.sendMessageDelayed(addProxyMsg, ADD_PROXY_DELAY_MS); - return true; - } - - @Override - public void unbindBluetoothProfileService(int bluetoothProfile, - IBluetoothProfileServiceConnection proxy) { - synchronized (mProfileServices) { - Integer profile = new Integer(bluetoothProfile); - ProfileServiceConnections psc = mProfileServices.get(profile); - if (psc == null) { - return; - } - psc.removeProxy(proxy); - if (psc.isEmpty()) { - // All prxoies are disconnected, unbind with the service. - try { - mContext.unbindService(psc); - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e); - } - mProfileServices.remove(profile); - } - } - } - - private void unbindAllBluetoothProfileServices() { - synchronized (mProfileServices) { - for (Integer i : mProfileServices.keySet()) { - ProfileServiceConnections psc = mProfileServices.get(i); - try { - mContext.unbindService(psc); - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e); - } - psc.removeAllProxies(); - } - mProfileServices.clear(); - } - } - - /** - * Send enable message and set adapter name and address. Called when the boot phase becomes - * PHASE_SYSTEM_SERVICES_READY. - */ - public void handleOnBootPhase() { - if (DBG) { - Slog.d(TAG, "Bluetooth boot completed"); - } - mAppOps = mContext.getSystemService(AppOpsManager.class); - UserManagerInternal userManagerInternal = - LocalServices.getService(UserManagerInternal.class); - userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); - final boolean isBluetoothDisallowed = isBluetoothDisallowed(); - if (isBluetoothDisallowed) { - return; - } - final boolean isSafeMode = mContext.getPackageManager().isSafeMode(); - if (mEnableExternal && isBluetoothPersistedStateOnBluetooth() && !isSafeMode) { - if (DBG) { - Slog.d(TAG, "Auto-enabling Bluetooth."); - } - sendEnableMsg(mQuietEnableExternal, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT, - mContext.getPackageName()); - } else if (!isNameAndAddressSet()) { - if (DBG) { - Slog.d(TAG, "Getting adapter name and address"); - } - Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); - mHandler.sendMessage(getMsg); - } - - mBluetoothModeChangeHelper = new BluetoothModeChangeHelper(mContext); - if (mBluetoothAirplaneModeListener != null) { - mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper); - } - registerForProvisioningStateChange(); - mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this, DBG); - } - - /** - * Called when switching to a different foreground user. - */ - public void handleOnSwitchUser(int userHandle) { - if (DBG) { - Slog.d(TAG, "User " + userHandle + " switched"); - } - mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0).sendToTarget(); - } - - /** - * Called when user is unlocked. - */ - public void handleOnUnlockUser(int userHandle) { - if (DBG) { - Slog.d(TAG, "User " + userHandle + " unlocked"); - } - mHandler.obtainMessage(MESSAGE_USER_UNLOCKED, userHandle, 0).sendToTarget(); - } - - /** - * This class manages the clients connected to a given ProfileService - * and maintains the connection with that service. - */ - private final class ProfileServiceConnections - implements ServiceConnection, IBinder.DeathRecipient { - final RemoteCallbackList<IBluetoothProfileServiceConnection> mProxies = - new RemoteCallbackList<IBluetoothProfileServiceConnection>(); - IBinder mService; - ComponentName mClassName; - Intent mIntent; - boolean mInvokingProxyCallbacks = false; - - ProfileServiceConnections(Intent intent) { - mService = null; - mClassName = null; - mIntent = intent; - } - - private boolean bindService() { - int state = BluetoothAdapter.STATE_OFF; - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - state = mBluetooth.getState(); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call getState", e); - return false; - } finally { - mBluetoothLock.readLock().unlock(); - } - - if (state != BluetoothAdapter.STATE_ON) { - if (DBG) { - Slog.d(TAG, "Unable to bindService while Bluetooth is disabled"); - } - return false; - } - - if (mIntent != null && mService == null && doBind(mIntent, this, 0, - UserHandle.CURRENT_OR_SELF)) { - Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE); - msg.obj = this; - mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS); - return true; - } - Slog.w(TAG, "Unable to bind with intent: " + mIntent); - return false; - } - - private void addProxy(IBluetoothProfileServiceConnection proxy) { - mProxies.register(proxy); - if (mService != null) { - try { - proxy.onServiceConnected(mClassName, mService); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to connect to proxy", e); - } - } else { - if (!mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE, this)) { - Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE); - msg.obj = this; - mHandler.sendMessage(msg); - } - } - } - - private void removeProxy(IBluetoothProfileServiceConnection proxy) { - if (proxy != null) { - if (mProxies.unregister(proxy)) { - try { - proxy.onServiceDisconnected(mClassName); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to disconnect proxy", e); - } - } - } else { - Slog.w(TAG, "Trying to remove a null proxy"); - } - } - - private void removeAllProxies() { - onServiceDisconnected(mClassName); - mProxies.kill(); - } - - private boolean isEmpty() { - return mProxies.getRegisteredCallbackCount() == 0; - } - - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - // remove timeout message - mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE, this); - mService = service; - mClassName = className; - try { - mService.linkToDeath(this, 0); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to linkToDeath", e); - } - - if (mInvokingProxyCallbacks) { - Slog.e(TAG, "Proxy callbacks already in progress."); - return; - } - mInvokingProxyCallbacks = true; - - final int n = mProxies.beginBroadcast(); - try { - for (int i = 0; i < n; i++) { - try { - mProxies.getBroadcastItem(i).onServiceConnected(className, service); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to connect to proxy", e); - } - } - } finally { - mProxies.finishBroadcast(); - mInvokingProxyCallbacks = false; - } - } - - @Override - public void onServiceDisconnected(ComponentName className) { - if (mService == null) { - return; - } - try { - mService.unlinkToDeath(this, 0); - } catch (NoSuchElementException e) { - Log.e(TAG, "error unlinking to death", e); - } - mService = null; - mClassName = null; - - if (mInvokingProxyCallbacks) { - Slog.e(TAG, "Proxy callbacks already in progress."); - return; - } - mInvokingProxyCallbacks = true; - - final int n = mProxies.beginBroadcast(); - try { - for (int i = 0; i < n; i++) { - try { - mProxies.getBroadcastItem(i).onServiceDisconnected(className); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to disconnect from proxy", e); - } - } - } finally { - mProxies.finishBroadcast(); - mInvokingProxyCallbacks = false; - } - } - - @Override - public void binderDied() { - if (DBG) { - Slog.w(TAG, "Profile service for profile: " + mClassName + " died."); - } - onServiceDisconnected(mClassName); - // Trigger rebind - Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE); - msg.obj = this; - mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS); - } - } - - private void sendBluetoothStateCallback(boolean isUp) { - try { - int n = mStateChangeCallbacks.beginBroadcast(); - if (DBG) { - Slog.d(TAG, "Broadcasting onBluetoothStateChange(" + isUp + ") to " + n - + " receivers."); - } - for (int i = 0; i < n; i++) { - try { - mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i, e); - } - } - } finally { - mStateChangeCallbacks.finishBroadcast(); - } - } - - /** - * Inform BluetoothAdapter instances that Adapter service is up - */ - private void sendBluetoothServiceUpCallback() { - synchronized (mCallbacks) { - try { - int n = mCallbacks.beginBroadcast(); - Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers."); - for (int i = 0; i < n; i++) { - try { - mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e); - } - } - } finally { - mCallbacks.finishBroadcast(); - } - } - } - - /** - * Inform BluetoothAdapter instances that Adapter service is down - */ - private void sendBluetoothServiceDownCallback() { - synchronized (mCallbacks) { - try { - int n = mCallbacks.beginBroadcast(); - Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers."); - for (int i = 0; i < n; i++) { - try { - mCallbacks.getBroadcastItem(i).onBluetoothServiceDown(); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e); - } - } - } finally { - mCallbacks.finishBroadcast(); - } - } - } - - public String getAddress(AttributionSource attributionSource) { - if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getAddress")) { - return null; - } - - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG, "getAddress(): not allowed for non-active and non system user"); - return null; - } - - if (mContext.checkCallingOrSelfPermission(Manifest.permission.LOCAL_MAC_ADDRESS) - != PackageManager.PERMISSION_GRANTED) { - return BluetoothAdapter.DEFAULT_MAC_ADDRESS; - } - - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - return mBluetooth.getAddressWithAttribution(attributionSource); - } - } catch (RemoteException e) { - Slog.e(TAG, - "getAddress(): Unable to retrieve address remotely. Returning cached address", - e); - } finally { - mBluetoothLock.readLock().unlock(); - } - - // mAddress is accessed from outside. - // It is alright without a lock. Here, bluetooth is off, no other thread is - // changing mAddress - return mAddress; - } - - public String getName(AttributionSource attributionSource) { - if (!checkConnectPermissionForDataDelivery(mContext, attributionSource, "getName")) { - return null; - } - - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG, "getName(): not allowed for non-active and non system user"); - return null; - } - - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - return mBluetooth.getName(attributionSource); - } - } catch (RemoteException e) { - Slog.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - - // mName is accessed from outside. - // It alright without a lock. Here, bluetooth is off, no other thread is - // changing mName - return mName; - } - - private class BluetoothServiceConnection implements ServiceConnection { - public void onServiceConnected(ComponentName componentName, IBinder service) { - String name = componentName.getClassName(); - if (DBG) { - Slog.d(TAG, "BluetoothServiceConnection: " + name); - } - Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED); - if (name.equals("com.android.bluetooth.btservice.AdapterService")) { - msg.arg1 = SERVICE_IBLUETOOTH; - } else if (name.equals("com.android.bluetooth.gatt.GattService")) { - msg.arg1 = SERVICE_IBLUETOOTHGATT; - } else { - Slog.e(TAG, "Unknown service connected: " + name); - return; - } - msg.obj = service; - mHandler.sendMessage(msg); - } - - public void onServiceDisconnected(ComponentName componentName) { - // Called if we unexpectedly disconnect. - String name = componentName.getClassName(); - if (DBG) { - Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name); - } - Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED); - if (name.equals("com.android.bluetooth.btservice.AdapterService")) { - msg.arg1 = SERVICE_IBLUETOOTH; - } else if (name.equals("com.android.bluetooth.gatt.GattService")) { - msg.arg1 = SERVICE_IBLUETOOTHGATT; - } else { - Slog.e(TAG, "Unknown service disconnected: " + name); - return; - } - mHandler.sendMessage(msg); - } - } - - private BluetoothServiceConnection mConnection = new BluetoothServiceConnection(); - - private class BluetoothHandler extends Handler { - boolean mGetNameAddressOnly = false; - private int mWaitForEnableRetry; - private int mWaitForDisableRetry; - - BluetoothHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_GET_NAME_AND_ADDRESS: - if (DBG) { - Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS"); - } - try { - mBluetoothLock.writeLock().lock(); - if ((mBluetooth == null) && (!mBinding)) { - if (DBG) { - Slog.d(TAG, "Binding to service to get name and address"); - } - mGetNameAddressOnly = true; - Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); - mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS); - Intent i = new Intent(IBluetooth.class.getName()); - if (!doBind(i, mConnection, - Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, - UserHandle.CURRENT)) { - mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); - } else { - mBinding = true; - } - } else if (mBluetooth != null) { - try { - storeNameAndAddress( - mBluetooth.getName(mContext.getAttributionSource()), - mBluetooth.getAddressWithAttribution( - mContext.getAttributionSource())); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to grab names", re); - } - if (mGetNameAddressOnly && !mEnable) { - unbindAndFinish(); - } - mGetNameAddressOnly = false; - } - } finally { - mBluetoothLock.writeLock().unlock(); - } - break; - - case MESSAGE_ENABLE: - int quietEnable = msg.arg1; - int isBle = msg.arg2; - if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) - || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { - // We are handling enable or disable right now, wait for it. - mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE, - quietEnable, isBle), ENABLE_DISABLE_DELAY_MS); - break; - } - - if (DBG) { - Slog.d(TAG, "MESSAGE_ENABLE(" + quietEnable + "): mBluetooth = " - + mBluetooth); - } - mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mEnable = true; - - if (isBle == 0) { - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - } - - // Use service interface to get the exact state - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - boolean isHandled = true; - int state = mBluetooth.getState(); - switch (state) { - case BluetoothAdapter.STATE_BLE_ON: - if (isBle == 1) { - Slog.i(TAG, "Already at BLE_ON State"); - } else { - Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); - mBluetooth.onLeServiceUp(mContext.getAttributionSource()); - } - break; - case BluetoothAdapter.STATE_BLE_TURNING_ON: - case BluetoothAdapter.STATE_TURNING_ON: - case BluetoothAdapter.STATE_ON: - Slog.i(TAG, "MESSAGE_ENABLE: already enabled"); - break; - default: - isHandled = false; - break; - } - if (isHandled) break; - } - } catch (RemoteException e) { - Slog.e(TAG, "", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - - mQuietEnable = (quietEnable == 1); - if (mBluetooth == null) { - handleEnable(mQuietEnable); - } else { - // - // We need to wait until transitioned to STATE_OFF and - // the previous Bluetooth process has exited. The - // waiting period has three components: - // (a) Wait until the local state is STATE_OFF. This - // is accomplished by sending delay a message - // MESSAGE_HANDLE_ENABLE_DELAYED - // (b) Wait until the STATE_OFF state is updated to - // all components. - // (c) Wait until the Bluetooth process exits, and - // ActivityManager detects it. - // The waiting for (b) and (c) is accomplished by - // delaying the MESSAGE_RESTART_BLUETOOTH_SERVICE - // message. The delay time is backed off if Bluetooth - // continuously failed to turn on itself. - // - mWaitForEnableRetry = 0; - Message enableDelayedMsg = - mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); - mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); - } - break; - - case MESSAGE_DISABLE: - if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mBinding - || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { - // We are handling enable or disable right now, wait for it. - mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_DISABLE), - ENABLE_DISABLE_DELAY_MS); - break; - } - - if (DBG) { - Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth - + ", mBinding = " + mBinding); - } - mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); - - if (mEnable && mBluetooth != null) { - mWaitForDisableRetry = 0; - Message disableDelayedMsg = - mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); - mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); - } else { - mEnable = false; - handleDisable(); - } - break; - - case MESSAGE_HANDLE_ENABLE_DELAYED: { - // The Bluetooth is turning off, wait for STATE_OFF - if (mState != BluetoothAdapter.STATE_OFF) { - if (mWaitForEnableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { - mWaitForEnableRetry++; - Message enableDelayedMsg = - mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); - mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); - break; - } else { - Slog.e(TAG, "Wait for STATE_OFF timeout"); - } - } - // Either state is changed to STATE_OFF or reaches the maximum retry, we - // should move forward to the next step. - mWaitForEnableRetry = 0; - Message restartMsg = - mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); - Slog.d(TAG, "Handle enable is finished"); - break; - } - - case MESSAGE_HANDLE_DISABLE_DELAYED: { - boolean disabling = (msg.arg1 == 1); - Slog.d(TAG, "MESSAGE_HANDLE_DISABLE_DELAYED: disabling:" + disabling); - if (!disabling) { - // The Bluetooth is turning on, wait for STATE_ON - if (mState != BluetoothAdapter.STATE_ON) { - if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { - mWaitForDisableRetry++; - Message disableDelayedMsg = mHandler.obtainMessage( - MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); - mHandler.sendMessageDelayed(disableDelayedMsg, - ENABLE_DISABLE_DELAY_MS); - break; - } else { - Slog.e(TAG, "Wait for STATE_ON timeout"); - } - } - // Either state is changed to STATE_ON or reaches the maximum retry, we - // should move forward to the next step. - mWaitForDisableRetry = 0; - mEnable = false; - handleDisable(); - // Wait for state exiting STATE_ON - Message disableDelayedMsg = - mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); - mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); - } else { - // The Bluetooth is turning off, wait for exiting STATE_ON - if (mState == BluetoothAdapter.STATE_ON) { - if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { - mWaitForDisableRetry++; - Message disableDelayedMsg = mHandler.obtainMessage( - MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); - mHandler.sendMessageDelayed(disableDelayedMsg, - ENABLE_DISABLE_DELAY_MS); - break; - } else { - Slog.e(TAG, "Wait for exiting STATE_ON timeout"); - } - } - // Either state is exited from STATE_ON or reaches the maximum retry, we - // should move forward to the next step. - Slog.d(TAG, "Handle disable is finished"); - } - break; - } - - case MESSAGE_RESTORE_USER_SETTING: - if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { - if (DBG) { - Slog.d(TAG, "Restore Bluetooth state to disabled"); - } - persistBluetoothSetting(BLUETOOTH_OFF); - mEnableExternal = false; - sendDisableMsg( - BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING, - mContext.getPackageName()); - } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) { - if (DBG) { - Slog.d(TAG, "Restore Bluetooth state to enabled"); - } - mQuietEnableExternal = false; - mEnableExternal = true; - // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING, - mContext.getPackageName()); - } - break; - case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: { - IBluetoothStateChangeCallback callback = - (IBluetoothStateChangeCallback) msg.obj; - mStateChangeCallbacks.register(callback); - break; - } - case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK: { - IBluetoothStateChangeCallback callback = - (IBluetoothStateChangeCallback) msg.obj; - mStateChangeCallbacks.unregister(callback); - break; - } - case MESSAGE_ADD_PROXY_DELAYED: { - ProfileServiceConnections psc = mProfileServices.get(msg.arg1); - if (psc == null) { - break; - } - IBluetoothProfileServiceConnection proxy = - (IBluetoothProfileServiceConnection) msg.obj; - psc.addProxy(proxy); - break; - } - case MESSAGE_BIND_PROFILE_SERVICE: { - ProfileServiceConnections psc = (ProfileServiceConnections) msg.obj; - removeMessages(MESSAGE_BIND_PROFILE_SERVICE, msg.obj); - if (psc == null) { - break; - } - psc.bindService(); - break; - } - case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: { - if (DBG) { - Slog.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1); - } - - IBinder service = (IBinder) msg.obj; - try { - mBluetoothLock.writeLock().lock(); - if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { - mBluetoothGatt = - IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service)); - continueFromBleOnState(); - break; - } // else must be SERVICE_IBLUETOOTH - - //Remove timeout - mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); - - mBinding = false; - mBluetoothBinder = service; - mBluetooth = IBluetooth.Stub.asInterface(Binder.allowBlocking(service)); - - if (!isNameAndAddressSet()) { - Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); - mHandler.sendMessage(getMsg); - if (mGetNameAddressOnly) { - return; - } - } - - //Register callback object - try { - mBluetooth.registerCallback(mBluetoothCallback, - mContext.getAttributionSource()); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to register BluetoothCallback", re); - } - //Inform BluetoothAdapter instances that service is up - sendBluetoothServiceUpCallback(); - - //Do enable request - try { - if (!mBluetooth.enable(mQuietEnable, mContext.getAttributionSource())) { - Slog.e(TAG, "IBluetooth.enable() returned false"); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call enable()", e); - } - } finally { - mBluetoothLock.writeLock().unlock(); - } - - if (!mEnable) { - waitForState(Set.of(BluetoothAdapter.STATE_ON)); - handleDisable(); - waitForState(Set.of(BluetoothAdapter.STATE_OFF, - BluetoothAdapter.STATE_TURNING_ON, - BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_BLE_TURNING_ON, - BluetoothAdapter.STATE_BLE_ON, - BluetoothAdapter.STATE_BLE_TURNING_OFF)); - } - break; - } - case MESSAGE_BLUETOOTH_STATE_CHANGE: { - int prevState = msg.arg1; - int newState = msg.arg2; - if (DBG) { - Slog.d(TAG, - "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState( - prevState) + " > " + BluetoothAdapter.nameForState( - newState)); - } - mState = newState; - bluetoothStateChangeHandler(prevState, newState); - // handle error state transition case from TURNING_ON to OFF - // unbind and rebind bluetooth service and enable bluetooth - if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) && (newState - == BluetoothAdapter.STATE_OFF) && (mBluetooth != null) && mEnable) { - recoverBluetoothServiceFromError(false); - } - if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && (newState - == BluetoothAdapter.STATE_BLE_ON) && (mBluetooth != null) && mEnable) { - recoverBluetoothServiceFromError(true); - } - // If we tried to enable BT while BT was in the process of shutting down, - // wait for the BT process to fully tear down and then force a restart - // here. This is a bit of a hack (b/29363429). - if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) && (newState - == BluetoothAdapter.STATE_OFF)) { - if (mEnable) { - Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting."); - waitForState(Set.of(BluetoothAdapter.STATE_OFF)); - Message restartMsg = - mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); - } - } - if (newState == BluetoothAdapter.STATE_ON - || newState == BluetoothAdapter.STATE_BLE_ON) { - // bluetooth is working, reset the counter - if (mErrorRecoveryRetryCounter != 0) { - Slog.w(TAG, "bluetooth is recovered from error"); - mErrorRecoveryRetryCounter = 0; - } - } - break; - } - case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: { - Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED(" + msg.arg1 + ")"); - try { - mBluetoothLock.writeLock().lock(); - if (msg.arg1 == SERVICE_IBLUETOOTH) { - // if service is unbinded already, do nothing and return - if (mBluetooth == null) { - break; - } - mBluetooth = null; - } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { - mBluetoothGatt = null; - break; - } else { - Slog.e(TAG, "Unknown argument for service disconnect!"); - break; - } - } finally { - mBluetoothLock.writeLock().unlock(); - } - - // log the unexpected crash - addCrashLog(); - addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH, - mContext.getPackageName(), false); - if (mEnable) { - mEnable = false; - // Send a Bluetooth Restart message - Message restartMsg = - mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); - } - - sendBluetoothServiceDownCallback(); - - // Send BT state broadcast to update - // the BT icon correctly - if ((mState == BluetoothAdapter.STATE_TURNING_ON) || (mState - == BluetoothAdapter.STATE_ON)) { - bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, - BluetoothAdapter.STATE_TURNING_OFF); - mState = BluetoothAdapter.STATE_TURNING_OFF; - } - if (mState == BluetoothAdapter.STATE_TURNING_OFF) { - bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_OFF); - } - - mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); - mState = BluetoothAdapter.STATE_OFF; - break; - } - case MESSAGE_RESTART_BLUETOOTH_SERVICE: { - mErrorRecoveryRetryCounter++; - Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE: retry count=" - + mErrorRecoveryRetryCounter); - if (mErrorRecoveryRetryCounter < MAX_ERROR_RESTART_RETRIES) { - /* Enable without persisting the setting as - it doesnt change when IBluetooth - service restarts */ - mEnable = true; - addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED, - mContext.getPackageName(), true); - handleEnable(mQuietEnable); - } else { - Slog.e(TAG, "Reach maximum retry to restart Bluetooth!"); - } - break; - } - case MESSAGE_TIMEOUT_BIND: { - Slog.e(TAG, "MESSAGE_TIMEOUT_BIND"); - mBluetoothLock.writeLock().lock(); - mBinding = false; - mBluetoothLock.writeLock().unlock(); - break; - } - case MESSAGE_TIMEOUT_UNBIND: { - Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND"); - mBluetoothLock.writeLock().lock(); - mUnbinding = false; - mBluetoothLock.writeLock().unlock(); - break; - } - - case MESSAGE_USER_SWITCHED: { - if (DBG) { - Slog.d(TAG, "MESSAGE_USER_SWITCHED"); - } - mHandler.removeMessages(MESSAGE_USER_SWITCHED); - - /* disable and enable BT when detect a user switch */ - if (mBluetooth != null && isEnabled()) { - restartForReason(BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH); - } else if (mBinding || mBluetooth != null) { - Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED); - userMsg.arg2 = 1 + msg.arg2; - // if user is switched when service is binding retry after a delay - mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS); - if (DBG) { - Slog.d(TAG, "Retry MESSAGE_USER_SWITCHED " + userMsg.arg2); - } - } - break; - } - case MESSAGE_USER_UNLOCKED: { - if (DBG) { - Slog.d(TAG, "MESSAGE_USER_UNLOCKED"); - } - mHandler.removeMessages(MESSAGE_USER_SWITCHED); - - if (mEnable && !mBinding && (mBluetooth == null)) { - // We should be connected, but we gave up for some - // reason; maybe the Bluetooth service wasn't encryption - // aware, so try binding again. - if (DBG) { - Slog.d(TAG, "Enabled but not bound; retrying after unlock"); - } - handleEnable(mQuietEnable); - } - break; - } - case MESSAGE_INIT_FLAGS_CHANGED: { - if (DBG) { - Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED"); - } - mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); - if (mBluetoothModeChangeHelper.isMediaProfileConnected()) { - Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by " - + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS - + " ms due to existing connections"); - mHandler.sendEmptyMessageDelayed( - MESSAGE_INIT_FLAGS_CHANGED, - DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS); - break; - } - if (!isDeviceProvisioned()) { - Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by " - + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS - + "ms because device is not provisioned"); - mHandler.sendEmptyMessageDelayed( - MESSAGE_INIT_FLAGS_CHANGED, - DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS); - break; - } - if (mBluetooth != null && isEnabled()) { - Slog.i(TAG, "Restarting Bluetooth due to init flag change"); - restartForReason( - BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED); - } - break; - } - } - } - - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED - }) - private void restartForReason(int reason) { - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - mBluetooth.unregisterCallback(mBluetoothCallback, - mContext.getAttributionSource()); - } - } catch (RemoteException re) { - Slog.e(TAG, "Unable to unregister", re); - } finally { - mBluetoothLock.readLock().unlock(); - } - - if (mState == BluetoothAdapter.STATE_TURNING_OFF) { - // MESSAGE_USER_SWITCHED happened right after MESSAGE_ENABLE - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_OFF); - mState = BluetoothAdapter.STATE_OFF; - } - if (mState == BluetoothAdapter.STATE_OFF) { - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_TURNING_ON); - mState = BluetoothAdapter.STATE_TURNING_ON; - } - - waitForState(Set.of(BluetoothAdapter.STATE_ON)); - - if (mState == BluetoothAdapter.STATE_TURNING_ON) { - bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON); - } - - unbindAllBluetoothProfileServices(); - // disable - addActiveLog(reason, mContext.getPackageName(), false); - handleDisable(); - // Pbap service need receive STATE_TURNING_OFF intent to close - bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, - BluetoothAdapter.STATE_TURNING_OFF); - - boolean didDisableTimeout = - !waitForState(Set.of(BluetoothAdapter.STATE_OFF)); - - bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_OFF); - sendBluetoothServiceDownCallback(); - - try { - mBluetoothLock.writeLock().lock(); - if (mBluetooth != null) { - mBluetooth = null; - // Unbind - mContext.unbindService(mConnection); - } - mBluetoothGatt = null; - } finally { - mBluetoothLock.writeLock().unlock(); - } - - // - // If disabling Bluetooth times out, wait for an - // additional amount of time to ensure the process is - // shut down completely before attempting to restart. - // - if (didDisableTimeout) { - SystemClock.sleep(3000); - } else { - SystemClock.sleep(100); - } - - mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); - mState = BluetoothAdapter.STATE_OFF; - // enable - addActiveLog(reason, mContext.getPackageName(), true); - // mEnable flag could have been reset on disableBLE. Reenable it. - mEnable = true; - handleEnable(mQuietEnable); - } - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private void handleEnable(boolean quietMode) { - mQuietEnable = quietMode; - - try { - mBluetoothLock.writeLock().lock(); - if ((mBluetooth == null) && (!mBinding)) { - Slog.d(TAG, "binding Bluetooth service"); - //Start bind timeout and bind - Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); - mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS); - Intent i = new Intent(IBluetooth.class.getName()); - if (!doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, - UserHandle.CURRENT)) { - mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); - } else { - mBinding = true; - } - } else if (mBluetooth != null) { - //Enable bluetooth - try { - if (!mBluetooth.enable(mQuietEnable, mContext.getAttributionSource())) { - Slog.e(TAG, "IBluetooth.enable() returned false"); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call enable()", e); - } - } - } finally { - mBluetoothLock.writeLock().unlock(); - } - } - - boolean doBind(Intent intent, ServiceConnection conn, int flags, UserHandle user) { - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, conn, flags, user)) { - Slog.e(TAG, "Fail to bind to: " + intent); - return false; - } - return true; - } - - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - private void handleDisable() { - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - if (DBG) { - Slog.d(TAG, "Sending off request."); - } - if (!mBluetooth.disable(mContext.getAttributionSource())) { - Slog.e(TAG, "IBluetooth.disable() returned false"); - } - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call disable()", e); - } finally { - mBluetoothLock.readLock().unlock(); - } - } - - private boolean checkIfCallerIsForegroundUser() { - int foregroundUser; - int callingUser = UserHandle.getCallingUserId(); - int callingUid = Binder.getCallingUid(); - final long callingIdentity = Binder.clearCallingIdentity(); - UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - UserInfo ui = um.getProfileParent(callingUser); - int parentUser = (ui != null) ? ui.id : UserHandle.USER_NULL; - int callingAppId = UserHandle.getAppId(callingUid); - boolean valid = false; - try { - foregroundUser = ActivityManager.getCurrentUser(); - valid = (callingUser == foregroundUser) || parentUser == foregroundUser - || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid - || callingAppId == Process.SHELL_UID; - if (DBG && !valid) { - Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser=" - + callingUser + " parentUser=" + parentUser + " foregroundUser=" - + foregroundUser); - } - } finally { - Binder.restoreCallingIdentity(callingIdentity); - } - return valid; - } - - private void sendBleStateChanged(int prevState, int newState) { - if (DBG) { - Slog.d(TAG, - "Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) + " > " - + BluetoothAdapter.nameForState(newState)); - } - // Send broadcast message to everyone else - Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); - intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); - intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, getTempAllowlistBroadcastOptions()); - } - - private boolean isBleState(int state) { - switch (state) { - case BluetoothAdapter.STATE_BLE_ON: - case BluetoothAdapter.STATE_BLE_TURNING_ON: - case BluetoothAdapter.STATE_BLE_TURNING_OFF: - return true; - } - return false; - } - - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - private void bluetoothStateChangeHandler(int prevState, int newState) { - boolean isStandardBroadcast = true; - if (prevState == newState) { // No change. Nothing to do. - return; - } - // Notify all proxy objects first of adapter state change - if (newState == BluetoothAdapter.STATE_BLE_ON || newState == BluetoothAdapter.STATE_OFF) { - boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF - && newState == BluetoothAdapter.STATE_BLE_ON); - - if (newState == BluetoothAdapter.STATE_OFF) { - // If Bluetooth is off, send service down event to proxy objects, and unbind - if (DBG) { - Slog.d(TAG, "Bluetooth is complete send Service Down"); - } - sendBluetoothServiceDownCallback(); - unbindAndFinish(); - sendBleStateChanged(prevState, newState); - - /* Currently, the OFF intent is broadcasted externally only when we transition - * from TURNING_OFF to BLE_ON state. So if the previous state is a BLE state, - * we are guaranteed that the OFF intent has been broadcasted earlier and we - * can safely skip it. - * Conversely, if the previous state is not a BLE state, it indicates that some - * sort of crash has occurred, moving us directly to STATE_OFF without ever - * passing through BLE_ON. We should broadcast the OFF intent in this case. */ - isStandardBroadcast = !isBleState(prevState); - - } else if (!intermediate_off) { - // connect to GattService - if (DBG) { - Slog.d(TAG, "Bluetooth is in LE only mode"); - } - if (mBluetoothGatt != null || !mContext.getPackageManager() - .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { - continueFromBleOnState(); - } else { - if (DBG) { - Slog.d(TAG, "Binding Bluetooth GATT service"); - } - Intent i = new Intent(IBluetoothGatt.class.getName()); - doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, - UserHandle.CURRENT); - } - sendBleStateChanged(prevState, newState); - //Don't broadcase this as std intent - isStandardBroadcast = false; - - } else if (intermediate_off) { - if (DBG) { - Slog.d(TAG, "Intermediate off, back to LE only mode"); - } - // For LE only mode, broadcast as is - sendBleStateChanged(prevState, newState); - sendBluetoothStateCallback(false); // BT is OFF for general users - // Broadcast as STATE_OFF - newState = BluetoothAdapter.STATE_OFF; - sendBrEdrDownCallback(mContext.getAttributionSource()); - } - } else if (newState == BluetoothAdapter.STATE_ON) { - boolean isUp = (newState == BluetoothAdapter.STATE_ON); - sendBluetoothStateCallback(isUp); - sendBleStateChanged(prevState, newState); - - } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON - || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF) { - sendBleStateChanged(prevState, newState); - isStandardBroadcast = false; - - } else if (newState == BluetoothAdapter.STATE_TURNING_ON - || newState == BluetoothAdapter.STATE_TURNING_OFF) { - sendBleStateChanged(prevState, newState); - } - - if (isStandardBroadcast) { - if (prevState == BluetoothAdapter.STATE_BLE_ON) { - // Show prevState of BLE_ON as OFF to standard users - prevState = BluetoothAdapter.STATE_OFF; - } - if (DBG) { - Slog.d(TAG, - "Sending State Change: " + BluetoothAdapter.nameForState(prevState) + " > " - + BluetoothAdapter.nameForState(newState)); - } - Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED); - intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); - intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, - getTempAllowlistBroadcastOptions()); - } - } - - private boolean waitForState(Set<Integer> states) { - int i = 0; - while (i < 10) { - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth == null) { - break; - } - if (states.contains(mBluetooth.getState())) { - return true; - } - } catch (RemoteException e) { - Slog.e(TAG, "getState()", e); - break; - } finally { - mBluetoothLock.readLock().unlock(); - } - SystemClock.sleep(300); - i++; - } - Slog.e(TAG, "waitForState " + states + " time out"); - return false; - } - - private void sendDisableMsg(int reason, String packageName) { - mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); - addActiveLog(reason, packageName, false); - } - - private void sendEnableMsg(boolean quietMode, int reason, String packageName) { - sendEnableMsg(quietMode, reason, packageName, false); - } - - private void sendEnableMsg(boolean quietMode, int reason, String packageName, boolean isBle) { - mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, - isBle ? 1 : 0)); - addActiveLog(reason, packageName, true); - mLastEnabledTime = SystemClock.elapsedRealtime(); - } - - private void addActiveLog(int reason, String packageName, boolean enable) { - synchronized (mActiveLogs) { - if (mActiveLogs.size() > ACTIVE_LOG_MAX_SIZE) { - mActiveLogs.remove(); - } - mActiveLogs.add( - new ActiveLog(reason, packageName, enable, System.currentTimeMillis())); - } - - int state = enable ? FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED : - FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED; - FrameworkStatsLog.write_non_chained(FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED, - Binder.getCallingUid(), null, state, reason, packageName); - } - - private void addCrashLog() { - synchronized (mCrashTimestamps) { - if (mCrashTimestamps.size() == CRASH_LOG_MAX_SIZE) { - mCrashTimestamps.removeFirst(); - } - mCrashTimestamps.add(System.currentTimeMillis()); - mCrashes++; - } - } - - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - private void recoverBluetoothServiceFromError(boolean clearBle) { - Slog.e(TAG, "recoverBluetoothServiceFromError"); - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - //Unregister callback object - mBluetooth.unregisterCallback(mBluetoothCallback, mContext.getAttributionSource()); - } - } catch (RemoteException re) { - Slog.e(TAG, "Unable to unregister", re); - } finally { - mBluetoothLock.readLock().unlock(); - } - - SystemClock.sleep(500); - - // disable - addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR, - mContext.getPackageName(), false); - handleDisable(); - - waitForState(Set.of(BluetoothAdapter.STATE_OFF)); - - sendBluetoothServiceDownCallback(); - - try { - mBluetoothLock.writeLock().lock(); - if (mBluetooth != null) { - mBluetooth = null; - // Unbind - mContext.unbindService(mConnection); - } - mBluetoothGatt = null; - } finally { - mBluetoothLock.writeLock().unlock(); - } - - mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); - mState = BluetoothAdapter.STATE_OFF; - - if (clearBle) { - clearBleApps(); - } - - mEnable = false; - - // Send a Bluetooth Restart message to reenable bluetooth - Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, ERROR_RESTART_TIME_MS); - } - - private boolean isBluetoothDisallowed() { - final long callingIdentity = Binder.clearCallingIdentity(); - try { - return mContext.getSystemService(UserManager.class) - .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH, UserHandle.SYSTEM); - } finally { - Binder.restoreCallingIdentity(callingIdentity); - } - } - - /** - * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not - * offered to the user if Bluetooth or sharing is disallowed. Puts the component to its default - * state if Bluetooth is not disallowed. - * - * @param userId user to disable bluetooth sharing for. - * @param bluetoothSharingDisallowed whether bluetooth sharing is disallowed. - */ - private void updateOppLauncherComponentState(int userId, boolean bluetoothSharingDisallowed) { - final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth", - "com.android.bluetooth.opp.BluetoothOppLauncherActivity"); - final int newState = - bluetoothSharingDisallowed ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED - : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; - try { - final IPackageManager imp = AppGlobals.getPackageManager(); - imp.setComponentEnabledSetting(oppLauncherComponent, newState, - PackageManager.DONT_KILL_APP, userId); - } catch (Exception e) { - // The component was not found, do nothing. - } - } - - private int getServiceRestartMs() { - return (mErrorRecoveryRetryCounter + 1) * SERVICE_RESTART_TIME_MS; - } - - @Override - public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) { - return; - } - if ((args.length > 0) && args[0].startsWith("--proto")) { - dumpProto(fd); - return; - } - String errorMsg = null; - - writer.println("Bluetooth Status"); - writer.println(" enabled: " + isEnabled()); - writer.println(" state: " + BluetoothAdapter.nameForState(mState)); - writer.println(" address: " + mAddress); - writer.println(" name: " + mName); - if (mEnable) { - long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime; - String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d", - (int) (onDuration / (1000 * 60 * 60)), - (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60), - (int) (onDuration % 1000)); - writer.println(" time since enabled: " + onDurationString); - } - - if (mActiveLogs.size() == 0) { - writer.println("\nBluetooth never enabled!"); - } else { - writer.println("\nEnable log:"); - for (ActiveLog log : mActiveLogs) { - writer.println(" " + log); - } - } - - writer.println( - "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s")); - if (mCrashes == CRASH_LOG_MAX_SIZE) { - writer.println("(last " + CRASH_LOG_MAX_SIZE + ")"); - } - for (Long time : mCrashTimestamps) { - writer.println(" " + timeToLog(time)); - } - - writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s") - + " registered"); - for (ClientDeathRecipient app : mBleApps.values()) { - writer.println(" " + app.getPackageName()); - } - - writer.println("\nBluetoothManagerService:"); - writer.println(" mEnable:" + mEnable); - writer.println(" mQuietEnable:" + mQuietEnable); - writer.println(" mEnableExternal:" + mEnableExternal); - writer.println(" mQuietEnableExternal:" + mQuietEnableExternal); - - writer.println(""); - writer.flush(); - if (args.length == 0) { - // Add arg to produce output - args = new String[1]; - args[0] = "--print"; - } - - if (mBluetoothBinder == null) { - errorMsg = "Bluetooth Service not connected"; - } else { - try { - mBluetoothBinder.dump(fd, args); - } catch (RemoteException re) { - errorMsg = "RemoteException while dumping Bluetooth Service"; - } - } - if (errorMsg != null) { - writer.println(errorMsg); - } - } - - private void dumpProto(FileDescriptor fd) { - final ProtoOutputStream proto = new ProtoOutputStream(fd); - proto.write(BluetoothManagerServiceDumpProto.ENABLED, isEnabled()); - proto.write(BluetoothManagerServiceDumpProto.STATE, mState); - proto.write(BluetoothManagerServiceDumpProto.STATE_NAME, - BluetoothAdapter.nameForState(mState)); - proto.write(BluetoothManagerServiceDumpProto.ADDRESS, mAddress); - proto.write(BluetoothManagerServiceDumpProto.NAME, mName); - if (mEnable) { - proto.write(BluetoothManagerServiceDumpProto.LAST_ENABLED_TIME_MS, mLastEnabledTime); - } - proto.write(BluetoothManagerServiceDumpProto.CURR_TIMESTAMP_MS, - SystemClock.elapsedRealtime()); - for (ActiveLog log : mActiveLogs) { - long token = proto.start(BluetoothManagerServiceDumpProto.ACTIVE_LOGS); - log.dump(proto); - proto.end(token); - } - proto.write(BluetoothManagerServiceDumpProto.NUM_CRASHES, mCrashes); - proto.write(BluetoothManagerServiceDumpProto.CRASH_LOG_MAXED, - mCrashes == CRASH_LOG_MAX_SIZE); - for (Long time : mCrashTimestamps) { - proto.write(BluetoothManagerServiceDumpProto.CRASH_TIMESTAMPS_MS, time); - } - proto.write(BluetoothManagerServiceDumpProto.NUM_BLE_APPS, mBleApps.size()); - for (ClientDeathRecipient app : mBleApps.values()) { - proto.write(BluetoothManagerServiceDumpProto.BLE_APP_PACKAGE_NAMES, - app.getPackageName()); - } - proto.flush(); - } - - private static String getEnableDisableReasonString(int reason) { - switch (reason) { - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST: - return "APPLICATION_REQUEST"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE: - return "AIRPLANE_MODE"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED: - return "DISALLOWED"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTARTED: - return "RESTARTED"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_START_ERROR: - return "START_ERROR"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_SYSTEM_BOOT: - return "SYSTEM_BOOT"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_CRASH: - return "CRASH"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_USER_SWITCH: - return "USER_SWITCH"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_RESTORE_USER_SETTING: - return "RESTORE_USER_SETTING"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_FACTORY_RESET: - return "FACTORY_RESET"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED: - return "INIT_FLAGS_CHANGED"; - case BluetoothProtoEnums.ENABLE_DISABLE_REASON_UNSPECIFIED: - default: return "UNKNOWN[" + reason + "]"; - } - } - - @SuppressLint("AndroidFrameworkRequiresPermission") - private static boolean checkPermissionForDataDelivery(Context context, String permission, - AttributionSource attributionSource, String message) { - final int result = PermissionChecker.checkPermissionForDataDeliveryFromDataSource( - context, permission, PID_UNKNOWN, - new AttributionSource(context.getAttributionSource(), attributionSource), message); - if (result == PERMISSION_GRANTED) { - return true; - } - - final String msg = "Need " + permission + " permission for " + attributionSource + ": " - + message; - if (result == PERMISSION_HARD_DENIED) { - throw new SecurityException(msg); - } else { - Log.w(TAG, msg); - return false; - } - } - - /** - * Returns true if the BLUETOOTH_CONNECT permission is granted for the calling app. Returns - * false if the result is a soft denial. Throws SecurityException if the result is a hard - * denial. - * - * <p>Should be used in situations where the app op should not be noted. - */ - @SuppressLint("AndroidFrameworkRequiresPermission") - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public static boolean checkConnectPermissionForDataDelivery( - Context context, AttributionSource attributionSource, String message) { - return checkPermissionForDataDelivery(context, BLUETOOTH_CONNECT, - attributionSource, message); - } - - static @NonNull Bundle getTempAllowlistBroadcastOptions() { - final long duration = 10_000; - final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); - bOptions.setTemporaryAppAllowlist(duration, - TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, - PowerExemptionManager.REASON_BLUETOOTH_BROADCAST, ""); - return bOptions.toBundle(); - } -} diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java deleted file mode 100644 index e5854c968207..000000000000 --- a/services/core/java/com/android/server/BluetoothModeChangeHelper.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.annotation.RequiresPermission; -import android.bluetooth.BluetoothA2dp; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothHearingAid; -import android.bluetooth.BluetoothLeAudio; -import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothProfile.ServiceListener; -import android.content.Context; -import android.content.res.Resources; -import android.provider.Settings; -import android.widget.Toast; - -import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; - -/** - * Helper class that handles callout and callback methods without - * complex logic. - */ -public class BluetoothModeChangeHelper { - private volatile BluetoothA2dp mA2dp; - private volatile BluetoothHearingAid mHearingAid; - private volatile BluetoothLeAudio mLeAudio; - private final BluetoothAdapter mAdapter; - private final Context mContext; - - BluetoothModeChangeHelper(Context context) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); - mContext = context; - - mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); - mAdapter.getProfileProxy(mContext, mProfileServiceListener, - BluetoothProfile.HEARING_AID); - mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.LE_AUDIO); - } - - private final ServiceListener mProfileServiceListener = new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - // Setup Bluetooth profile proxies - switch (profile) { - case BluetoothProfile.A2DP: - mA2dp = (BluetoothA2dp) proxy; - break; - case BluetoothProfile.HEARING_AID: - mHearingAid = (BluetoothHearingAid) proxy; - break; - case BluetoothProfile.LE_AUDIO: - mLeAudio = (BluetoothLeAudio) proxy; - break; - default: - break; - } - } - - @Override - public void onServiceDisconnected(int profile) { - // Clear Bluetooth profile proxies - switch (profile) { - case BluetoothProfile.A2DP: - mA2dp = null; - break; - case BluetoothProfile.HEARING_AID: - mHearingAid = null; - break; - case BluetoothProfile.LE_AUDIO: - mLeAudio = null; - break; - default: - break; - } - } - }; - - @VisibleForTesting - public boolean isMediaProfileConnected() { - return isA2dpConnected() || isHearingAidConnected() || isLeAudioConnected(); - } - - @VisibleForTesting - public boolean isBluetoothOn() { - final BluetoothAdapter adapter = mAdapter; - if (adapter == null) { - return false; - } - return adapter.getLeState() == BluetoothAdapter.STATE_ON; - } - - @VisibleForTesting - public boolean isAirplaneModeOn() { - return Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 0) == 1; - } - - @VisibleForTesting - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public void onAirplaneModeChanged(BluetoothManagerService managerService) { - managerService.onAirplaneModeChanged(); - } - - @VisibleForTesting - public int getSettingsInt(String name) { - return Settings.Global.getInt(mContext.getContentResolver(), - name, 0); - } - - @VisibleForTesting - public void setSettingsInt(String name, int value) { - Settings.Global.putInt(mContext.getContentResolver(), - name, value); - } - - @VisibleForTesting - public void showToastMessage() { - Resources r = mContext.getResources(); - final CharSequence text = r.getString( - R.string.bluetooth_airplane_mode_toast, 0); - Toast.makeText(mContext, text, Toast.LENGTH_LONG).show(); - } - - private boolean isA2dpConnected() { - final BluetoothA2dp a2dp = mA2dp; - if (a2dp == null) { - return false; - } - return a2dp.getConnectedDevices().size() > 0; - } - - private boolean isHearingAidConnected() { - final BluetoothHearingAid hearingAid = mHearingAid; - if (hearingAid == null) { - return false; - } - return hearingAid.getConnectedDevices().size() > 0; - } - - private boolean isLeAudioConnected() { - final BluetoothLeAudio leAudio = mLeAudio; - if (leAudio == null) { - return false; - } - return leAudio.getConnectedDevices().size() > 0; - } -} diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java deleted file mode 100644 index 1a1eecd0f439..000000000000 --- a/services/core/java/com/android/server/BluetoothService.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2015 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.annotation.NonNull; -import android.annotation.Nullable; -import android.bluetooth.BluetoothAdapter; -import android.content.Context; -import android.os.UserManager; - -import com.android.server.SystemService.TargetUser; - -class BluetoothService extends SystemService { - private BluetoothManagerService mBluetoothManagerService; - private boolean mInitialized = false; - - public BluetoothService(Context context) { - super(context); - mBluetoothManagerService = new BluetoothManagerService(context); - } - - private void initialize() { - if (!mInitialized) { - mBluetoothManagerService.handleOnBootPhase(); - mInitialized = true; - } - } - - @Override - public void onStart() { - } - - @Override - public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { - publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, - mBluetoothManagerService); - } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY && - !UserManager.isHeadlessSystemUserMode()) { - initialize(); - } - } - - @Override - public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { - if (!mInitialized) { - initialize(); - } else { - mBluetoothManagerService.handleOnSwitchUser(to.getUserIdentifier()); - } - } - - @Override - public void onUserUnlocking(@NonNull TargetUser user) { - mBluetoothManagerService.handleOnUnlockUser(user.getUserIdentifier()); - } -} diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index a2c2dbd407a5..8551d887e80c 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -20,12 +20,12 @@ import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.OBSERVE_NETWORK_POLICY; import static android.Manifest.permission.SHUTDOWN; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY; import static android.net.INetd.FIREWALL_ALLOWLIST; -import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; import static android.net.INetd.FIREWALL_CHAIN_NONE; -import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; -import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED; -import static android.net.INetd.FIREWALL_CHAIN_STANDBY; import static android.net.INetd.FIREWALL_DENYLIST; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; @@ -39,9 +39,12 @@ 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; +import android.net.ConnectivityManager; import android.net.INetd; import android.net.INetdUnsolicitedEventListener; import android.net.INetworkManagementEventObserver; @@ -133,12 +136,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; @@ -1162,19 +1159,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "inetd bandwidth"); + final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); try { if (allowlist) { - if (enable) { - mNetdService.bandwidthAddNiceApp(uid); - } else { - mNetdService.bandwidthRemoveNiceApp(uid); - } + cm.updateMeteredNetworkAllowList(uid, enable); } else { - if (enable) { - mNetdService.bandwidthAddNaughtyApp(uid); - } else { - mNetdService.bandwidthRemoveNaughtyApp(uid); - } + cm.updateMeteredNetworkDenyList(uid, enable); } synchronized (mRulesLock) { if (enable) { @@ -1183,7 +1173,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { quotaList.delete(uid); } } - } catch (RemoteException | ServiceSpecificException e) { + } catch (RuntimeException e) { throw new IllegalStateException(e); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); @@ -1468,9 +1458,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub { throw new IllegalArgumentException("Bad child chain: " + chainName); } + final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); try { - mNetdService.firewallEnableChildChain(chain, enable); - } catch (RemoteException | ServiceSpecificException e) { + cm.setFirewallChainEnabled(chain, enable); + } catch (RuntimeException e) { throw new IllegalStateException(e); } @@ -1542,25 +1533,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub { updateFirewallUidRuleLocked(chain, uid, FIREWALL_RULE_DEFAULT); } } + final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); try { - switch (chain) { - case FIREWALL_CHAIN_DOZABLE: - mNetdService.firewallReplaceUidChain("fw_dozable", true, uids); - break; - case FIREWALL_CHAIN_STANDBY: - mNetdService.firewallReplaceUidChain("fw_standby", false, uids); - break; - case FIREWALL_CHAIN_POWERSAVE: - mNetdService.firewallReplaceUidChain("fw_powersave", true, uids); - break; - case FIREWALL_CHAIN_RESTRICTED: - mNetdService.firewallReplaceUidChain("fw_restricted", true, uids); - break; - case FIREWALL_CHAIN_NONE: - default: - Slog.d(TAG, "setFirewallUidRules() called on invalid chain: " + chain); - } - } catch (RemoteException e) { + cm.replaceFirewallChain(chain, uids); + } catch (RuntimeException e) { Slog.w(TAG, "Error flushing firewall chain " + chain, e); } } @@ -1576,10 +1552,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub { private void setFirewallUidRuleLocked(int chain, int uid, int rule) { if (updateFirewallUidRuleLocked(chain, uid, rule)) { - final int ruleType = getFirewallRuleType(chain, rule); + final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); try { - mNetdService.firewallSetUidRule(chain, uid, ruleType); - } catch (RemoteException | ServiceSpecificException e) { + cm.updateFirewallRule(chain, uid, isFirewallRuleAllow(chain, rule)); + } catch (RuntimeException e) { throw new IllegalStateException(e); } } @@ -1649,12 +1625,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } } - private int getFirewallRuleType(int chain, int rule) { + // There are only two type of firewall rule: FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY. + private boolean isFirewallRuleAllow(int chain, int rule) { if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) { - return getFirewallType(chain) == FIREWALL_ALLOWLIST - ? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW; + return getFirewallType(chain) == FIREWALL_DENYLIST; } - return rule; + return rule == INetd.FIREWALL_RULE_ALLOW; } private void enforceSystemUid() { diff --git a/services/core/java/com/android/server/SerialService.java b/services/core/java/com/android/server/SerialService.java index 1abe4588261a..e915fa1522a1 100644 --- a/services/core/java/com/android/server/SerialService.java +++ b/services/core/java/com/android/server/SerialService.java @@ -16,6 +16,7 @@ package com.android.server; +import android.annotation.EnforcePermission; import android.content.Context; import android.hardware.ISerialManager; import android.os.ParcelFileDescriptor; @@ -34,9 +35,8 @@ public class SerialService extends ISerialManager.Stub { com.android.internal.R.array.config_serialPorts); } + @EnforcePermission(android.Manifest.permission.SERIAL_PORT) public String[] getSerialPorts() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null); - ArrayList<String> ports = new ArrayList<String>(); for (int i = 0; i < mSerialPorts.length; i++) { String path = mSerialPorts[i]; @@ -49,8 +49,8 @@ public class SerialService extends ISerialManager.Stub { return result; } + @EnforcePermission(android.Manifest.permission.SERIAL_PORT) public ParcelFileDescriptor openSerialPort(String path) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SERIAL_PORT, null); for (int i = 0; i < mSerialPorts.length; i++) { if (mSerialPorts[i].equals(path)) { return native_open(path); 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..9546496fd8e8 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); @@ -3402,8 +3620,12 @@ class StorageManagerService extends IStorageManager.Stub mInstaller.tryMountDataMirror(volumeUuid); } } - } catch (Exception e) { + } catch (RemoteException | Installer.InstallerException e) { Slog.wtf(TAG, e); + // Make sure to re-throw this exception; we must not ignore failure + // to prepare the user storage as it could indicate that encryption + // wasn't successfully set up. + throw new RuntimeException(e); } } 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/clipboard/OWNERS b/services/core/java/com/android/server/clipboard/OWNERS index 5449df908051..0d5dbf9acac3 100644 --- a/services/core/java/com/android/server/clipboard/OWNERS +++ b/services/core/java/com/android/server/clipboard/OWNERS @@ -1 +1,3 @@ per-file EmulatorClipboardMonitor.java = bohu@google.com,lfy@google.com,rkir@google.com + +olilan@google.com diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java index cc9efbc64c02..603f20633cfb 100644 --- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java +++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java @@ -18,14 +18,12 @@ package com.android.server.connectivity; import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER; import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY; -import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.WARNING_DISABLED; -import static android.net.NetworkTemplate.OEM_MANAGED_ALL; import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -95,6 +93,7 @@ public class MultipathPolicyTracker { private static String TAG = MultipathPolicyTracker.class.getSimpleName(); private static final boolean DBG = false; + private static final long MIN_THRESHOLD_BYTES = 2 * 1_048_576L; // 2MiB // This context is for the current user. private final Context mContext; @@ -199,6 +198,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 +238,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 +269,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); @@ -272,15 +278,11 @@ public class MultipathPolicyTracker { } private NetworkIdentity getTemplateMatchingNetworkIdentity(NetworkCapabilities nc) { - return new NetworkIdentity( - ConnectivityManager.TYPE_MOBILE, - 0 /* subType, unused for template matching */, - subscriberId, - null /* networkId, unused for matching mobile networks */, - !nc.hasCapability(NET_CAPABILITY_NOT_ROAMING), - !nc.hasCapability(NET_CAPABILITY_NOT_METERED), - false /* defaultNetwork, templates should have DEFAULT_NETWORK_ALL */, - OEM_MANAGED_ALL); + return new NetworkIdentity.Builder().setType(ConnectivityManager.TYPE_MOBILE) + .setSubscriberId(subscriberId) + .setRoaming(!nc.hasCapability(NET_CAPABILITY_NOT_ROAMING)) + .setMetered(!nc.hasCapability(NET_CAPABILITY_NOT_METERED)) + .build(); } private long getRemainingDailyBudget(long limitBytes, @@ -369,7 +371,7 @@ public class MultipathPolicyTracker { // This will only be called if the total quota for the day changed, not if usage changed // since last time, so even if this is called very often the budget will not snap to 0 // as soon as there are less than 2MB left for today. - if (budget > NetworkStatsManager.MIN_THRESHOLD_BYTES) { + if (budget > MIN_THRESHOLD_BYTES) { if (DBG) { Log.d(TAG, "Setting callback for " + budget + " bytes on network " + network); } @@ -402,8 +404,8 @@ public class MultipathPolicyTracker { private void registerUsageCallback(long budget) { maybeUnregisterUsageCallback(); - mStatsManager.registerUsageCallback(mNetworkTemplate, TYPE_MOBILE, budget, - mUsageCallback, mHandler); + mStatsManager.registerUsageCallback(mNetworkTemplate, budget, + (command) -> mHandler.post(command), mUsageCallback); mMultipathBudget = budget; } 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/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index ffc1aed4c672..91de9e559e13 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -344,10 +344,19 @@ class BluetoothRouteProvider { } private void addActiveRoute(BluetoothRouteInfo btRoute) { + if (btRoute == null) { + if (DEBUG) { + Log.d(TAG, " btRoute is null"); + } + return; + } if (DEBUG) { Log.d(TAG, "Adding active route: " + btRoute.route); } - if (btRoute == null || mActiveRoutes.contains(btRoute)) { + if (mActiveRoutes.contains(btRoute)) { + if (DEBUG) { + Log.d(TAG, " btRoute is already added."); + } return; } setRouteConnectionState(btRoute, STATE_CONNECTED); 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..c05138f8eae1 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -24,7 +24,6 @@ import static android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_STACK; import static android.Manifest.permission.OBSERVE_NETWORK_POLICY; -import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.Manifest.permission.READ_PHONE_STATE; import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; import static android.app.PendingIntent.FLAG_IMMUTABLE; @@ -63,7 +62,6 @@ import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkIdentity.OEM_NONE; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; @@ -102,7 +100,6 @@ import static android.net.NetworkTemplate.MATCH_MOBILE; import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.buildTemplateCarrierMetered; import static android.net.NetworkTemplate.buildTemplateMobileAll; -import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; import static android.os.Trace.TRACE_TAG_NETWORK; import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED; @@ -130,8 +127,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.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED; +import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; @@ -151,6 +147,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,10 +179,8 @@ 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; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.BestClock; @@ -443,7 +439,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 +451,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 +704,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 +716,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 +789,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 +894,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) { @@ -959,10 +1008,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { userFilter.addAction(ACTION_USER_REMOVED); mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler); - // listen for stats update events - final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED); - mContext.registerReceiver( - mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler); + // listen for stats updated callbacks for interested network types. + mNetworkStats.registerUsageCallback(new NetworkTemplate.Builder(MATCH_MOBILE).build(), + 0 /* thresholdBytes */, new HandlerExecutor(mHandler), mStatsCallback); + mNetworkStats.registerUsageCallback(new NetworkTemplate.Builder(MATCH_WIFI).build(), + 0 /* thresholdBytes */, new HandlerExecutor(mHandler), mStatsCallback); // listen for restrict background changes from notifications final IntentFilter allowFilter = new IntentFilter(ACTION_ALLOW_BACKGROUND); @@ -1167,14 +1217,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { }; /** - * Receiver that watches for {@link INetworkStatsService} updates, which we - * use to check against {@link NetworkPolicy#warningBytes}. + * Listener that watches for {@link NetworkStatsManager} updates, which + * NetworkPolicyManagerService uses to check against {@link NetworkPolicy#warningBytes}. */ - final private BroadcastReceiver mStatsReceiver = new BroadcastReceiver() { + private final StatsCallback mStatsCallback = new StatsCallback(); + private class StatsCallback extends NetworkStatsManager.UsageCallback { + private boolean mIsAnyCallbackReceived = false; + @Override - public void onReceive(Context context, Intent intent) { - // on background handler thread, and verified - // READ_NETWORK_USAGE_HISTORY permission above. + public void onThresholdReached(int networkType, String subscriberId) { + mIsAnyCallbackReceived = true; synchronized (mNetworkPoliciesSecondLock) { updateNetworkRulesNL(); @@ -1182,6 +1234,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateNotificationsNL(); } } + + /** + * Return whether any callback is received. + * Used to determine if NetworkStatsService is ready. + */ + public boolean isAnyCallbackReceived() { + return mIsAnyCallbackReceived; + } }; /** @@ -1405,15 +1465,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 (!mStatsCallback.isAnyCallbackReceived()) 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(); } } @@ -1449,13 +1511,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { for (int i = 0; i < mSubIdToSubscriberId.size(); i++) { final int subId = mSubIdToSubscriberId.keyAt(i); final String subscriberId = mSubIdToSubscriberId.valueAt(i); - final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, - true, OEM_NONE); - /* While OEM_NONE indicates "any non OEM managed network", OEM_NONE is meant to be a - * placeholder value here. The probeIdent is matched against a NetworkTemplate which - * should have its OEM managed value set to OEM_MANAGED_ALL, which will cause the - * template to match probeIdent without regard to OEM managed status. */ + final NetworkIdentity probeIdent = new NetworkIdentity.Builder() + .setType(TYPE_MOBILE) + .setSubscriberId(subscriberId) + .setMetered(true) + .setDefaultNetwork(true).build(); if (template.matches(probeIdent)) { return subId; } @@ -1688,9 +1748,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // find and update the carrier NetworkPolicy for this subscriber id boolean policyUpdated = false; - final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true, - OEM_NONE); + final NetworkIdentity probeIdent = new NetworkIdentity.Builder() + .setType(TYPE_MOBILE) + .setSubscriberId(subscriberId) + .setMetered(true) + .setDefaultNetwork(true).build(); for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) { final NetworkTemplate template = mNetworkPolicy.keyAt(i); if (template.matches(probeIdent)) { @@ -1918,10 +1980,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { for (int i = 0; i < mSubIdToSubscriberId.size(); i++) { final int subId = mSubIdToSubscriberId.keyAt(i); final String subscriberId = mSubIdToSubscriberId.valueAt(i); - - final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, - true, OEM_NONE); + final NetworkIdentity probeIdent = new NetworkIdentity.Builder() + .setType(TYPE_MOBILE) + .setSubscriberId(subscriberId) + .setMetered(true) + .setDefaultNetwork(true).build(); // Template is matched when subscriber id matches. if (template.matches(probeIdent)) { matchingSubIds.add(subId); @@ -2025,11 +2088,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { for (final NetworkStateSnapshot snapshot : snapshots) { mNetIdToSubId.put(snapshot.getNetwork().getNetId(), parseSubId(snapshot)); - // Policies matched by NPMS only match by subscriber ID or by network ID. Thus subtype - // in the object created here is never used and its value doesn't matter, so use - // NETWORK_TYPE_UNKNOWN. - final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot, - true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */); + // Policies matched by NPMS only match by subscriber ID or by network ID. + final NetworkIdentity ident = new NetworkIdentity.Builder() + .setNetworkStateSnapshot(snapshot).setDefaultNetwork(true).build(); identified.put(snapshot, ident); } @@ -2226,9 +2287,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mNetworkPoliciesSecondLock") private boolean ensureActiveCarrierPolicyAL(int subId, String subscriberId) { // Poke around to see if we already have a policy - final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true, - OEM_NONE); + final NetworkIdentity probeIdent = new NetworkIdentity.Builder() + .setType(TYPE_MOBILE) + .setSubscriberId(subscriberId) + .setMetered(true) + .setDefaultNetwork(true).build(); for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) { final NetworkTemplate template = mNetworkPolicy.keyAt(i); if (template.matches(probeIdent)) { @@ -2254,7 +2317,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (dataWarningConfig == WARNING_DISABLED) { return WARNING_DISABLED; } else { - return dataWarningConfig * MB_IN_BYTES; + return DataUnit.MEBIBYTES.toBytes(dataWarningConfig); } } @@ -2638,7 +2701,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final List<WifiConfiguration> configs = wm.getConfiguredNetworks(); for (int i = 0; i < configs.size(); ++i) { final WifiConfiguration config = configs.get(i); - for (String key : config.getAllPersistableNetworkKeys()) { + for (String key : config.getAllNetworkKeys()) { final Boolean metered = wifiNetworkKeys.get(key); if (metered != null) { Slog.d(TAG, "Found network " + key + "; upgrading metered hint"); @@ -3383,6 +3446,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 notifyStatsProviderWarningOrLimitReached() { + 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); @@ -3394,9 +3486,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { plans.add(SubscriptionPlan.Builder .createRecurringMonthly(ZonedDateTime.parse("2007-03-14T00:00:00.000Z")) .setTitle("G-Mobile") - .setDataLimit(5 * TrafficStats.GB_IN_BYTES, + .setDataLimit(DataUnit.GIBIBYTES.toBytes(5), SubscriptionPlan.LIMIT_BEHAVIOR_BILLED) - .setDataUsage(1 * TrafficStats.GB_IN_BYTES, + .setDataUsage(DataUnit.GIBIBYTES.toBytes(1), ZonedDateTime.now().minusHours(36).toInstant().toEpochMilli()) .build()); plans.add(SubscriptionPlan.Builder @@ -3404,15 +3496,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .setTitle("G-Mobile Happy") .setDataLimit(SubscriptionPlan.BYTES_UNLIMITED, SubscriptionPlan.LIMIT_BEHAVIOR_BILLED) - .setDataUsage(5 * TrafficStats.GB_IN_BYTES, + .setDataUsage(DataUnit.GIBIBYTES.toBytes(5), ZonedDateTime.now().minusHours(36).toInstant().toEpochMilli()) .build()); plans.add(SubscriptionPlan.Builder .createRecurringMonthly(ZonedDateTime.parse("2017-03-14T00:00:00.000Z")) .setTitle("G-Mobile, Charged after limit") - .setDataLimit(5 * TrafficStats.GB_IN_BYTES, + .setDataLimit(DataUnit.GIBIBYTES.toBytes(5), SubscriptionPlan.LIMIT_BEHAVIOR_BILLED) - .setDataUsage(5 * TrafficStats.GB_IN_BYTES, + .setDataUsage(DataUnit.GIBIBYTES.toBytes(5), ZonedDateTime.now().minusHours(36).toInstant().toEpochMilli()) .build()); } else if ("month_soft".equals(fake)) { @@ -3421,25 +3513,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .setTitle("G-Mobile is the carriers name who this plan belongs to") .setSummary("Crazy unlimited bandwidth plan with incredibly long title " + "that should be cut off to prevent UI from looking terrible") - .setDataLimit(5 * TrafficStats.GB_IN_BYTES, + .setDataLimit(DataUnit.GIBIBYTES.toBytes(5), SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED) - .setDataUsage(1 * TrafficStats.GB_IN_BYTES, + .setDataUsage(DataUnit.GIBIBYTES.toBytes(1), ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli()) .build()); plans.add(SubscriptionPlan.Builder .createRecurringMonthly(ZonedDateTime.parse("2017-03-14T00:00:00.000Z")) .setTitle("G-Mobile, Throttled after limit") - .setDataLimit(5 * TrafficStats.GB_IN_BYTES, + .setDataLimit(DataUnit.GIBIBYTES.toBytes(5), SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED) - .setDataUsage(5 * TrafficStats.GB_IN_BYTES, + .setDataUsage(DataUnit.GIBIBYTES.toBytes(5), ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli()) .build()); plans.add(SubscriptionPlan.Builder .createRecurringMonthly(ZonedDateTime.parse("2017-03-14T00:00:00.000Z")) .setTitle("G-Mobile, No data connection after limit") - .setDataLimit(5 * TrafficStats.GB_IN_BYTES, + .setDataLimit(DataUnit.GIBIBYTES.toBytes(5), SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED) - .setDataUsage(5 * TrafficStats.GB_IN_BYTES, + .setDataUsage(DataUnit.GIBIBYTES.toBytes(5), ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli()) .build()); @@ -3447,25 +3539,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { plans.add(SubscriptionPlan.Builder .createRecurringMonthly(ZonedDateTime.parse("2007-03-14T00:00:00.000Z")) .setTitle("G-Mobile is the carriers name who this plan belongs to") - .setDataLimit(5 * TrafficStats.GB_IN_BYTES, + .setDataLimit(DataUnit.GIBIBYTES.toBytes(5), SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED) - .setDataUsage(6 * TrafficStats.GB_IN_BYTES, + .setDataUsage(DataUnit.GIBIBYTES.toBytes(6), ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli()) .build()); plans.add(SubscriptionPlan.Builder .createRecurringMonthly(ZonedDateTime.parse("2017-03-14T00:00:00.000Z")) .setTitle("G-Mobile, Throttled after limit") - .setDataLimit(5 * TrafficStats.GB_IN_BYTES, + .setDataLimit(DataUnit.GIBIBYTES.toBytes(5), SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED) - .setDataUsage(5 * TrafficStats.GB_IN_BYTES, + .setDataUsage(DataUnit.GIBIBYTES.toBytes(5), ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli()) .build()); plans.add(SubscriptionPlan.Builder .createRecurringMonthly(ZonedDateTime.parse("2017-03-14T00:00:00.000Z")) .setTitle("G-Mobile, No data connection after limit") - .setDataLimit(5 * TrafficStats.GB_IN_BYTES, + .setDataLimit(DataUnit.GIBIBYTES.toBytes(5), SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED) - .setDataUsage(5 * TrafficStats.GB_IN_BYTES, + .setDataUsage(DataUnit.GIBIBYTES.toBytes(5), ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli()) .build()); @@ -3479,9 +3571,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .createNonrecurring(ZonedDateTime.now().minusDays(20), ZonedDateTime.now().plusDays(10)) .setTitle("G-Mobile") - .setDataLimit(512 * TrafficStats.MB_IN_BYTES, + .setDataLimit(DataUnit.MEBIBYTES.toBytes(512), SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED) - .setDataUsage(100 * TrafficStats.MB_IN_BYTES, + .setDataUsage(DataUnit.MEBIBYTES.toBytes(100), ZonedDateTime.now().minusHours(3).toInstant().toEpochMilli()) .build()); } else if ("prepaid_crazy".equals(fake)) { @@ -3489,9 +3581,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .createNonrecurring(ZonedDateTime.now().minusDays(20), ZonedDateTime.now().plusDays(10)) .setTitle("G-Mobile Anytime") - .setDataLimit(512 * TrafficStats.MB_IN_BYTES, + .setDataLimit(DataUnit.MEBIBYTES.toBytes(512), SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED) - .setDataUsage(100 * TrafficStats.MB_IN_BYTES, + .setDataUsage(DataUnit.MEBIBYTES.toBytes(100), ZonedDateTime.now().minusHours(3).toInstant().toEpochMilli()) .build()); plans.add(SubscriptionPlan.Builder @@ -3499,9 +3591,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { ZonedDateTime.now().plusDays(20)) .setTitle("G-Mobile Nickel Nights") .setSummary("5¢/GB between 1-5AM") - .setDataLimit(5 * TrafficStats.GB_IN_BYTES, + .setDataLimit(DataUnit.GIBIBYTES.toBytes(5), SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED) - .setDataUsage(15 * TrafficStats.MB_IN_BYTES, + .setDataUsage(DataUnit.MEBIBYTES.toBytes(15), ZonedDateTime.now().minusHours(30).toInstant().toEpochMilli()) .build()); plans.add(SubscriptionPlan.Builder @@ -3509,9 +3601,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { ZonedDateTime.now().plusDays(20)) .setTitle("G-Mobile Bonus 3G") .setSummary("Unlimited 3G data") - .setDataLimit(1 * TrafficStats.GB_IN_BYTES, + .setDataLimit(DataUnit.GIBIBYTES.toBytes(1), SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED) - .setDataUsage(300 * TrafficStats.MB_IN_BYTES, + .setDataUsage(DataUnit.MEBIBYTES.toBytes(300), ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli()) .build()); } else if ("unlimited".equals(fake)) { @@ -3521,7 +3613,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .setTitle("G-Mobile Awesome") .setDataLimit(SubscriptionPlan.BYTES_UNLIMITED, SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED) - .setDataUsage(50 * TrafficStats.MB_IN_BYTES, + .setDataUsage(DataUnit.MEBIBYTES.toBytes(50), ZonedDateTime.now().minusHours(3).toInstant().toEpochMilli()) .build()); } @@ -5000,7 +5092,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // make sure stats are recorded frequently enough; we aim // for 2MB threshold for 2GB/month rules. final long persistThreshold = lowestRule / 1000; - mNetworkStats.advisePersistThreshold(persistThreshold); + // TODO: Sync internal naming with the API surface. + mNetworkStats.setDefaultGlobalAlert(persistThreshold); return true; } case MSG_UPDATE_INTERFACE_QUOTAS: { @@ -5369,25 +5462,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 (!mStatsCallback.isAnyCallbackReceived()) return 0; + return mDeps.getNetworkTotalBytes(template, start, end); } private boolean isBandwidthControlEnabled() { @@ -5607,14 +5685,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 +5726,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/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 1ef202511452..758877b3f266 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -18,27 +18,17 @@ package com.android.server.stats.pull; import static android.app.AppOpsManager.OP_FLAG_SELF; import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED; -import static android.app.usage.NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN; -import static android.app.usage.NetworkStatsManager.FLAG_POLL_FORCE; -import static android.app.usage.NetworkStatsManager.FLAG_POLL_ON_OPEN; import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static android.net.NetworkIdentity.OEM_PAID; -import static android.net.NetworkIdentity.OEM_PRIVATE; -import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; -import static android.net.NetworkStats.METERED_ALL; import static android.net.NetworkStats.METERED_YES; -import static android.net.NetworkStats.ROAMING_ALL; import static android.net.NetworkTemplate.MATCH_ETHERNET; -import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD; -import static android.net.NetworkTemplate.MATCH_WIFI_WILDCARD; -import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; +import static android.net.NetworkTemplate.MATCH_MOBILE; +import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.OEM_MANAGED_ALL; -import static android.net.NetworkTemplate.buildTemplateMobileWildcard; -import static android.net.NetworkTemplate.buildTemplateMobileWithRatType; -import static android.net.NetworkTemplate.buildTemplateWifiWildcard; +import static android.net.NetworkTemplate.OEM_MANAGED_PAID; +import static android.net.NetworkTemplate.OEM_MANAGED_PRIVATE; import static android.net.NetworkTemplate.getAllCollapsedRatTypes; import static android.os.Debug.getIonHeapsSizeKb; import static android.os.Process.LAST_SHARED_APPLICATION_GID; @@ -80,6 +70,7 @@ import android.app.ProcessMemoryState; import android.app.RuntimeAppOpAccessMessage; import android.app.StatsManager; import android.app.StatsManager.PullAtomMetadata; +import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.BluetoothAdapter; import android.bluetooth.UidTraffic; @@ -93,8 +84,6 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.net.ConnectivityManager; -import android.net.INetworkStatsService; -import android.net.INetworkStatsSession; import android.net.Network; import android.net.NetworkRequest; import android.net.NetworkStats; @@ -188,6 +177,7 @@ import com.android.internal.os.StoragedUidIoStatsReader; import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import com.android.internal.util.CollectionUtils; import com.android.internal.util.FrameworkStatsLog; +import com.android.net.module.util.NetworkStatsUtils; import com.android.role.RoleManagerLocal; import com.android.server.BinderCallsStatsService; import com.android.server.LocalManagerRegistry; @@ -235,7 +225,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.function.BiConsumer; +import java.util.function.Function; /** * SystemService containing PullAtomCallbacks that are registered with statsd. @@ -331,6 +321,7 @@ public class StatsPullAtomService extends SystemService { private WifiManager mWifiManager; private TelephonyManager mTelephony; private SubscriptionManager mSubscriptionManager; + private NetworkStatsManager mNetworkStatsManager; @GuardedBy("mKernelWakelockLock") private KernelWakelockReader mKernelWakelockReader; @@ -776,7 +767,7 @@ public class StatsPullAtomService extends SystemService { mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); mStatsSubscriptionsListener = new StatsSubscriptionsListener(mSubscriptionManager); mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class); - + mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class); // Initialize DiskIO mStoragedUidIoStatsReader = new StoragedUidIoStatsReader(); @@ -966,32 +957,6 @@ public class StatsPullAtomService extends SystemService { registerOemManagedBytesTransfer(); } - /** - * Return the {@code INetworkStatsSession} object that holds the necessary properties needed - * for the subsequent queries to {@link com.android.server.net.NetworkStatsService}. Or - * null if the service or binder cannot be obtained. Calling this method will trigger poll - * in NetworkStatsService with once per 15 seconds rate-limit, unless {@code bypassRateLimit} - * is set to true. This is needed in {@link #getUidNetworkStatsSnapshotForTemplate}, where - * bypassing the limit is necessary for perfd to supply realtime stats to developers looking at - * the network usage of their app. - */ - @Nullable - private INetworkStatsSession getNetworkStatsSession(boolean bypassRateLimit) { - final INetworkStatsService networkStatsService = - INetworkStatsService.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); - if (networkStatsService == null) return null; - - try { - return networkStatsService.openSessionForUsageStats( - FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN | (bypassRateLimit ? FLAG_POLL_FORCE - : FLAG_POLL_ON_OPEN), mContext.getOpPackageName()); - } catch (RemoteException e) { - Slog.e(TAG, "Cannot get NetworkStats session", e); - return null; - } - } - private IThermalService getIThermalService() { synchronized (mThermalLock) { if (mThermalService == null) { @@ -1122,8 +1087,8 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.WIFI_BYTES_TRANSFER: { final NetworkStats stats = getUidNetworkStatsSnapshotForTransport(TRANSPORT_WIFI); if (stats != null) { - ret.add(new NetworkStatsExt(stats.groupedByUid(), new int[] {TRANSPORT_WIFI}, - /*slicedByFgbg=*/false)); + ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats), + new int[] {TRANSPORT_WIFI}, /*slicedByFgbg=*/false)); } break; } @@ -1139,7 +1104,7 @@ public class StatsPullAtomService extends SystemService { final NetworkStats stats = getUidNetworkStatsSnapshotForTransport(TRANSPORT_CELLULAR); if (stats != null) { - ret.add(new NetworkStatsExt(stats.groupedByUid(), + ret.add(new NetworkStatsExt(sliceNetworkStatsByUid(stats), new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false)); } break; @@ -1155,9 +1120,10 @@ public class StatsPullAtomService extends SystemService { } case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: { final NetworkStats wifiStats = getUidNetworkStatsSnapshotForTemplate( - buildTemplateWifiWildcard(), /*includeTags=*/true); + new NetworkTemplate.Builder(MATCH_WIFI).build(), /*includeTags=*/true); final NetworkStats cellularStats = getUidNetworkStatsSnapshotForTemplate( - buildTemplateMobileWildcard(), /*includeTags=*/true); + new NetworkTemplate.Builder(MATCH_MOBILE) + .setMeteredness(METERED_YES).build(), /*includeTags=*/true); if (wifiStats != null && cellularStats != null) { final NetworkStats stats = wifiStats.add(cellularStats); ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats), @@ -1229,23 +1195,19 @@ public class StatsPullAtomService extends SystemService { private void addNetworkStats(int atomTag, @NonNull List<StatsEvent> ret, @NonNull NetworkStatsExt statsExt) { - int size = statsExt.stats.size(); - final NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling - for (int j = 0; j < size; j++) { - statsExt.stats.getValues(j, entry); + for (NetworkStats.Entry entry : statsExt.stats) { StatsEvent statsEvent; - if (statsExt.slicedByFgbg) { // MobileBytesTransferByFgBg atom or WifiBytesTransferByFgBg atom. statsEvent = FrameworkStatsLog.buildStatsEvent( - atomTag, entry.uid, - (entry.set > 0), entry.rxBytes, entry.rxPackets, entry.txBytes, - entry.txPackets); + atomTag, entry.getUid(), + (entry.getSet() > 0), entry.getRxBytes(), entry.getRxPackets(), + entry.getTxBytes(), entry.getTxPackets()); } else { // MobileBytesTransfer atom or WifiBytesTransfer atom. statsEvent = FrameworkStatsLog.buildStatsEvent( - atomTag, entry.uid, entry.rxBytes, - entry.rxPackets, entry.txBytes, entry.txPackets); + atomTag, entry.getUid(), entry.getRxBytes(), + entry.getRxPackets(), entry.getTxBytes(), entry.getTxPackets()); } ret.add(statsEvent); } @@ -1253,13 +1215,12 @@ public class StatsPullAtomService extends SystemService { private void addBytesTransferByTagAndMeteredAtoms(@NonNull NetworkStatsExt statsExt, @NonNull List<StatsEvent> pulledData) { - final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling - for (int i = 0; i < statsExt.stats.size(); i++) { - statsExt.stats.getValues(i, entry); + for (NetworkStats.Entry entry : statsExt.stats) { pulledData.add(FrameworkStatsLog.buildStatsEvent( - FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED, entry.uid, - entry.metered == NetworkStats.METERED_YES, entry.tag, entry.rxBytes, - entry.rxPackets, entry.txBytes, entry.txPackets)); + FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED, entry.getUid(), + entry.getMetered() == NetworkStats.METERED_YES, entry.getTag(), + entry.getRxBytes(), entry.getRxPackets(), entry.getTxBytes(), + entry.getTxPackets())); } } @@ -1274,12 +1235,11 @@ public class StatsPullAtomService extends SystemService { // Report NR connected in 5G non-standalone mode, or if the RAT type is NR to begin with. final boolean isNR = is5GNsa || statsExt.ratType == TelephonyManager.NETWORK_TYPE_NR; - final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling - for (int i = 0; i < statsExt.stats.size(); i++) { - statsExt.stats.getValues(i, entry); + for (NetworkStats.Entry entry : statsExt.stats) { pulledData.add(FrameworkStatsLog.buildStatsEvent( - FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER, entry.set, entry.rxBytes, - entry.rxPackets, entry.txBytes, entry.txPackets, + FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER, + entry.getSet(), entry.getRxBytes(), entry.getRxPackets(), + entry.getTxBytes(), entry.getTxPackets(), is5GNsa ? TelephonyManager.NETWORK_TYPE_LTE : statsExt.ratType, // Fill information about subscription, these cannot be null since invalid data // would be filtered when adding into subInfo list. @@ -1293,38 +1253,35 @@ public class StatsPullAtomService extends SystemService { private void addOemDataUsageBytesTransferAtoms(@NonNull NetworkStatsExt statsExt, @NonNull List<StatsEvent> pulledData) { - final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling final int oemManaged = statsExt.oemManaged; for (final int transport : statsExt.transports) { - for (int i = 0; i < statsExt.stats.size(); i++) { - statsExt.stats.getValues(i, entry); + for (NetworkStats.Entry entry : statsExt.stats) { pulledData.add(FrameworkStatsLog.buildStatsEvent( - FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER, entry.uid, (entry.set > 0), - oemManaged, transport, entry.rxBytes, entry.rxPackets, entry.txBytes, - entry.txPackets)); + FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER, entry.getUid(), + (entry.getSet() > 0), oemManaged, transport, entry.getRxBytes(), + entry.getRxPackets(), entry.getTxBytes(), entry.getTxPackets())); } } } @NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() { - final int[] transports = new int[] {MATCH_ETHERNET, MATCH_MOBILE_WILDCARD, - MATCH_WIFI_WILDCARD}; - final int[] oemManagedTypes = new int[] {OEM_PAID | OEM_PRIVATE, OEM_PAID, OEM_PRIVATE}; + final int[] matchRules = new int[] {MATCH_ETHERNET, MATCH_MOBILE, MATCH_WIFI}; + final int[] oemManagedTypes = new int[] {OEM_MANAGED_PAID | OEM_MANAGED_PRIVATE, + OEM_MANAGED_PAID, OEM_MANAGED_PRIVATE}; final List<NetworkStatsExt> ret = new ArrayList<>(); - for (final int transport : transports) { + for (final int matchRule : matchRules) { for (final int oemManaged : oemManagedTypes) { - /* A null subscriberId will set wildcard=true, since we aren't trying to select a - specific ssid or subscriber. */ - final NetworkTemplate template = new NetworkTemplate(transport, - /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, - METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - oemManaged); + // Subscriber Ids and Wifi Network Keys will not be set since the purpose is to + // slice statistics of different OEM managed networks among all network types. + // Thus, specifying networks through their identifiers are not needed. + final NetworkTemplate template = new NetworkTemplate.Builder(matchRule) + .setOemManaged(oemManaged).build(); final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, true); if (stats != null) { ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats), - new int[] {transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/true, + new int[] {matchRule}, /*slicedByFgbg=*/true, /*slicedByTag=*/true, /*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN, /*subInfo=*/null, oemManaged)); } @@ -1338,10 +1295,18 @@ public class StatsPullAtomService extends SystemService { * Create a snapshot of NetworkStats for a given transport. */ @Nullable private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) { - final NetworkTemplate template = (transport == TRANSPORT_CELLULAR) - ? NetworkTemplate.buildTemplateMobileWithRatType( - /*subscriptionId=*/null, NETWORK_TYPE_ALL, METERED_YES) - : NetworkTemplate.buildTemplateWifiWildcard(); + NetworkTemplate template = null; + switch (transport) { + case TRANSPORT_CELLULAR: + template = new NetworkTemplate.Builder(MATCH_MOBILE) + .setMeteredness(METERED_YES).build(); + break; + case TRANSPORT_WIFI: + template = new NetworkTemplate.Builder(MATCH_WIFI).build(); + break; + default: + Log.wtf(TAG, "Unexpected transport."); + } return getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false); } @@ -1357,22 +1322,32 @@ public class StatsPullAtomService extends SystemService { final long currentTimeInMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro()); final long bucketDuration = Settings.Global.getLong(mContext.getContentResolver(), NETSTATS_UID_BUCKET_DURATION, NETSTATS_UID_DEFAULT_BUCKET_DURATION_MS); - try { - // TODO (b/156313635): This is short-term hack to allow perfd gets updated networkStats - // history when query in every second in order to show realtime statistics. However, - // this is not a good long-term solution since NetworkStatsService will make frequent - // I/O and also block main thread when polling. - // Consider making perfd queries NetworkStatsService directly. - final NetworkStats stats = getNetworkStatsSession(template.getMatchRule() - == NetworkTemplate.MATCH_WIFI_WILDCARD).getSummaryForAllUid(template, - currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration, - currentTimeInMillis, includeTags); - return stats; - } catch (RemoteException | NullPointerException e) { - Slog.e(TAG, "Pulling netstats for template=" + template + " and includeTags=" - + includeTags + " causes error", e); + + // TODO (b/156313635): This is short-term hack to allow perfd gets updated networkStats + // history when query in every second in order to show realtime statistics. However, + // this is not a good long-term solution since NetworkStatsService will make frequent + // I/O and also block main thread when polling. + // Consider making perfd queries NetworkStatsService directly. + if (template.getMatchRule() == MATCH_WIFI && template.getSubscriberIds().isEmpty()) { + mNetworkStatsManager.forceUpdate(); } - return null; + + final android.app.usage.NetworkStats queryNonTaggedStats = + mNetworkStatsManager.querySummary( + template, currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration, + currentTimeInMillis); + + final NetworkStats nonTaggedStats = + NetworkStatsUtils.fromPublicNetworkStats(queryNonTaggedStats); + if (!includeTags) return nonTaggedStats; + + final android.app.usage.NetworkStats queryTaggedStats = + mNetworkStatsManager.queryTaggedSummary(template, + currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration, + currentTimeInMillis); + final NetworkStats taggedStats = + NetworkStatsUtils.fromPublicNetworkStats(queryTaggedStats); + return nonTaggedStats.add(taggedStats); } @NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForSub( @@ -1380,8 +1355,10 @@ public class StatsPullAtomService extends SystemService { final List<NetworkStatsExt> ret = new ArrayList<>(); for (final int ratType : getAllCollapsedRatTypes()) { final NetworkTemplate template = - buildTemplateMobileWithRatType(subInfo.subscriberId, ratType, - METERED_YES); + new NetworkTemplate.Builder(MATCH_MOBILE) + .setSubscriberIds(Set.of(subInfo.subscriberId)) + .setRatType(ratType) + .setMeteredness(METERED_YES).build(); final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false); if (stats != null) { @@ -1394,27 +1371,51 @@ public class StatsPullAtomService extends SystemService { return ret; } + @NonNull private NetworkStats sliceNetworkStatsByUid(@NonNull NetworkStats stats) { + return sliceNetworkStats(stats, + (entry) -> { + return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(), + NetworkStats.SET_ALL, NetworkStats.TAG_NONE, + NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL, + NetworkStats.DEFAULT_NETWORK_ALL, + entry.getRxBytes(), entry.getRxPackets(), + entry.getTxBytes(), entry.getTxPackets(), 0); + }); + } + @NonNull private NetworkStats sliceNetworkStatsByFgbg(@NonNull NetworkStats stats) { return sliceNetworkStats(stats, - (newEntry, oldEntry) -> { - newEntry.set = oldEntry.set; + (entry) -> { + return new NetworkStats.Entry(null /* IFACE_ALL */, NetworkStats.UID_ALL, + entry.getSet(), NetworkStats.TAG_NONE, + NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL, + NetworkStats.DEFAULT_NETWORK_ALL, + entry.getRxBytes(), entry.getRxPackets(), + entry.getTxBytes(), entry.getTxPackets(), 0); }); } @NonNull private NetworkStats sliceNetworkStatsByUidAndFgbg(@NonNull NetworkStats stats) { return sliceNetworkStats(stats, - (newEntry, oldEntry) -> { - newEntry.uid = oldEntry.uid; - newEntry.set = oldEntry.set; + (entry) -> { + return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(), + entry.getSet(), NetworkStats.TAG_NONE, + NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL, + NetworkStats.DEFAULT_NETWORK_ALL, + entry.getRxBytes(), entry.getRxPackets(), + entry.getTxBytes(), entry.getTxPackets(), 0); }); } @NonNull private NetworkStats sliceNetworkStatsByUidTagAndMetered(@NonNull NetworkStats stats) { return sliceNetworkStats(stats, - (newEntry, oldEntry) -> { - newEntry.uid = oldEntry.uid; - newEntry.tag = oldEntry.tag; - newEntry.metered = oldEntry.metered; + (entry) -> { + return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(), + NetworkStats.SET_ALL, entry.getTag(), + entry.getMetered(), NetworkStats.ROAMING_ALL, + NetworkStats.DEFAULT_NETWORK_ALL, + entry.getRxBytes(), entry.getRxPackets(), + entry.getTxBytes(), entry.getTxPackets(), 0); }); } @@ -1424,46 +1425,31 @@ public class StatsPullAtomService extends SystemService { * * This function iterates through each NetworkStats.Entry, sets its dimensions equal to the * default state (with the presumption that we don't want to slice on anything), and then - * applies the slicer lambda to allow users to control which dimensions to slice on. This is - * adapted from groupedByUid within NetworkStats.java + * applies the slicer lambda to allow users to control which dimensions to slice on. * - * @param slicer An operation taking into two parameters, new NetworkStats.Entry and old - * NetworkStats.Entry, that should be used to copy state from the old to the new. + * @param slicer An operation taking one parameter, NetworkStats.Entry, that should be used to + * get the state from entry to replace the default value. * This is useful for slicing by particular dimensions. For example, if we wished * to slice by uid and tag, we could write the following lambda: - * (new, old) -> { - * new.uid = old.uid; - * new.tag = old.tag; + * (entry) -> { + * return new NetworkStats.Entry(null, entry.getUid(), + * NetworkStats.SET_ALL, entry.getTag(), + * NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL, + * NetworkStats.DEFAULT_NETWORK_ALL, + * entry.getRxBytes(), entry.getRxPackets(), + * entry.getTxBytes(), entry.getTxPackets(), 0); * } - * If no slicer is provided, the data is not sliced by any dimensions. * @return new NeworkStats object appropriately sliced */ @NonNull private NetworkStats sliceNetworkStats(@NonNull NetworkStats stats, - @Nullable BiConsumer<NetworkStats.Entry, NetworkStats.Entry> slicer) { - final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1); - - final NetworkStats.Entry entry = new NetworkStats.Entry(); - entry.uid = NetworkStats.UID_ALL; - entry.iface = NetworkStats.IFACE_ALL; - entry.set = NetworkStats.SET_ALL; - entry.tag = NetworkStats.TAG_NONE; - entry.metered = NetworkStats.METERED_ALL; - entry.roaming = NetworkStats.ROAMING_ALL; - entry.defaultNetwork = NetworkStats.DEFAULT_NETWORK_ALL; - - final NetworkStats.Entry recycle = new NetworkStats.Entry(); // used for retrieving values - for (int i = 0; i < stats.size(); i++) { - stats.getValues(i, recycle); + @NonNull Function<NetworkStats.Entry, NetworkStats.Entry> slicer) { + NetworkStats ret = new NetworkStats(0, 1); + NetworkStats.Entry entry = new NetworkStats.Entry(); + for (NetworkStats.Entry e : stats) { if (slicer != null) { - slicer.accept(entry, recycle); + entry = slicer.apply(e); } - - entry.rxBytes = recycle.rxBytes; - entry.rxPackets = recycle.rxPackets; - entry.txBytes = recycle.txBytes; - entry.txPackets = recycle.txPackets; - // Operations purposefully omitted since we don't use them for statsd. - ret.combineValues(entry); + ret = ret.addEntry(entry); } return ret; } 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/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index e0cc8e182079..f29c40f74353 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -39,10 +39,13 @@ import android.net.vcn.VcnConfig; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnManager.VcnErrorCode; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Message; import android.os.ParcelUuid; import android.provider.Settings; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -57,6 +60,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -148,6 +152,10 @@ public class Vcn extends Handler { @NonNull private final VcnContentResolver mContentResolver; @NonNull private final ContentObserver mMobileDataSettingsObserver; + @NonNull + private final Map<Integer, VcnUserMobileDataStateListener> mMobileDataStateListeners = + new ArrayMap<>(); + /** * Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs. * @@ -221,6 +229,9 @@ public class Vcn extends Handler { // Update mIsMobileDataEnabled before starting handling of NetworkRequests. mIsMobileDataEnabled = getMobileDataStatus(); + // Register mobile data state listeners. + updateMobileDataStateListeners(); + // Register to receive cached and future NetworkRequests mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener); } @@ -348,6 +359,12 @@ public class Vcn extends Handler { gatewayConnection.teardownAsynchronously(); } + // Unregister MobileDataStateListeners + for (VcnUserMobileDataStateListener listener : mMobileDataStateListeners.values()) { + getTelephonyManager().unregisterTelephonyCallback(listener); + } + mMobileDataStateListeners.clear(); + mCurrentStatus = VCN_STATUS_CODE_INACTIVE; } @@ -454,11 +471,40 @@ public class Vcn extends Handler { gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot); } + updateMobileDataStateListeners(); + // Update the mobile data state after updating the subscription snapshot as a change in // subIds for a subGroup may affect the mobile data state. handleMobileDataToggled(); } + private void updateMobileDataStateListeners() { + final Set<Integer> subIdsInGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup); + final HandlerExecutor executor = new HandlerExecutor(this); + + // Register new callbacks + for (int subId : subIdsInGroup) { + if (!mMobileDataStateListeners.containsKey(subId)) { + final VcnUserMobileDataStateListener listener = + new VcnUserMobileDataStateListener(); + + getTelephonyManagerForSubid(subId).registerTelephonyCallback(executor, listener); + mMobileDataStateListeners.put(subId, listener); + } + } + + // Unregister old callbacks + Iterator<Entry<Integer, VcnUserMobileDataStateListener>> iterator = + mMobileDataStateListeners.entrySet().iterator(); + while (iterator.hasNext()) { + final Entry<Integer, VcnUserMobileDataStateListener> entry = iterator.next(); + if (!subIdsInGroup.contains(entry.getKey())) { + getTelephonyManager().unregisterTelephonyCallback(entry.getValue()); + iterator.remove(); + } + } + } + private void handleMobileDataToggled() { final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled; mIsMobileDataEnabled = getMobileDataStatus(); @@ -493,11 +539,8 @@ public class Vcn extends Handler { } private boolean getMobileDataStatus() { - final TelephonyManager genericTelMan = - mVcnContext.getContext().getSystemService(TelephonyManager.class); - for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) { - if (genericTelMan.createForSubscriptionId(subId).isDataEnabled()) { + if (getTelephonyManagerForSubid(subId).isDataEnabled()) { return true; } } @@ -517,6 +560,14 @@ public class Vcn extends Handler { return request.canBeSatisfiedBy(builder.build()); } + private TelephonyManager getTelephonyManager() { + return mVcnContext.getContext().getSystemService(TelephonyManager.class); + } + + private TelephonyManager getTelephonyManagerForSubid(int subid) { + return getTelephonyManager().createForSubscriptionId(subid); + } + private String getLogPrefix() { return "[" + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) @@ -670,6 +721,16 @@ public class Vcn extends Handler { } } + @VisibleForTesting(visibility = Visibility.PRIVATE) + class VcnUserMobileDataStateListener extends TelephonyCallback + implements TelephonyCallback.UserMobileDataStateListener { + + @Override + public void onUserMobileDataStateChanged(boolean enabled) { + sendMessage(obtainMessage(MSG_EVENT_MOBILE_DATA_TOGGLED)); + } + } + /** External dependencies used by Vcn, for injection in tests */ @VisibleForTesting(visibility = Visibility.PRIVATE) public static class Dependencies { 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/core/jni/Android.bp b/services/core/jni/Android.bp index e7005daf5626..e48b5e17739b 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -141,8 +141,8 @@ cc_defaults { "libutils", "libhwui", "libbpf_android", - "libnetdbpf", "libnetdutils", + "libnetworkstats", "libpsi", "libdataloader", "libincfs", diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index 37a84f3698c1..1c9d58458629 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -32,6 +32,7 @@ import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManager; import android.app.admin.FactoryResetProtectionPolicy; import android.app.admin.PasswordPolicy; +import android.app.admin.PreferentialNetworkServiceConfig; import android.graphics.Color; import android.os.Bundle; import android.os.PersistableBundle; @@ -294,6 +295,8 @@ class ActiveAdmin { public boolean mAdminCanGrantSensorsPermissions; public boolean mPreferentialNetworkServiceEnabled = DevicePolicyManager.PREFERENTIAL_NETWORK_SERVICE_ENABLED_DEFAULT; + public PreferentialNetworkServiceConfig mPreferentialNetworkServiceConfig = + PreferentialNetworkServiceConfig.DEFAULT; private static final boolean USB_DATA_SIGNALING_ENABLED_DEFAULT = true; boolean mUsbDataSignalingEnabled = USB_DATA_SIGNALING_ENABLED_DEFAULT; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 0792d9bde4b4..564b60825ab0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -109,6 +109,8 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT; import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE; +import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK; +import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1; import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; @@ -179,6 +181,7 @@ import android.app.admin.NetworkEvent; import android.app.admin.ParcelableGranteeMap; import android.app.admin.PasswordMetrics; import android.app.admin.PasswordPolicy; +import android.app.admin.PreferentialNetworkServiceConfig; import android.app.admin.SecurityLog; import android.app.admin.SecurityLog.SecurityEvent; import android.app.admin.StartInstallingUpdateCallback; @@ -231,6 +234,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; @@ -3276,14 +3280,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { updatePermissionPolicyCache(userId); updateAdminCanGrantSensorsPermissionCache(userId); - final boolean preferentialNetworkServiceEnabled; + final PreferentialNetworkServiceConfig preferentialNetworkServiceConfig; synchronized (getLockObject()) { ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId); - preferentialNetworkServiceEnabled = owner != null - ? owner.mPreferentialNetworkServiceEnabled - : DevicePolicyManager.PREFERENTIAL_NETWORK_SERVICE_ENABLED_DEFAULT; + preferentialNetworkServiceConfig = owner != null + ? owner.mPreferentialNetworkServiceConfig + : PreferentialNetworkServiceConfig.DEFAULT; } - updateNetworkPreferenceForUser(userId, preferentialNetworkServiceEnabled); + updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfig); startOwnerService(userId, "start-user"); } @@ -3300,7 +3304,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override void handleStopUser(int userId) { - updateNetworkPreferenceForUser(userId, false); + updateNetworkPreferenceForUser(userId, PreferentialNetworkServiceConfig.DEFAULT); stopOwnerService(userId, "stop-user"); } @@ -11844,7 +11848,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(isProfileOwner(caller), "Caller is not profile owner;" - + " only profile owner may control the preferntial network service"); + + " only profile owner may control the preferential network service"); synchronized (getLockObject()) { final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked( caller.getUserId()); @@ -11881,6 +11885,47 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public void setPreferentialNetworkServiceConfig( + PreferentialNetworkServiceConfig preferentialNetworkServiceConfig) { + if (!mHasFeature) { + return; + } + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(isProfileOwner(caller), + "Caller is not profile owner;" + + " only profile owner may control the preferential network service"); + synchronized (getLockObject()) { + final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked( + caller.getUserId()); + if (!requiredAdmin.mPreferentialNetworkServiceConfig.equals( + preferentialNetworkServiceConfig)) { + requiredAdmin.mPreferentialNetworkServiceConfig = preferentialNetworkServiceConfig; + saveSettingsLocked(caller.getUserId()); + } + } + updateNetworkPreferenceForUser(caller.getUserId(), preferentialNetworkServiceConfig); + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.SET_PREFERENTIAL_NETWORK_SERVICE_ENABLED) + .setBoolean(preferentialNetworkServiceConfig.isEnabled()) + .write(); + } + + @Override + public PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig() { + if (!mHasFeature) { + return PreferentialNetworkServiceConfig.DEFAULT; + } + + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(isProfileOwner(caller), + "Caller is not profile owner"); + synchronized (getLockObject()) { + final ActiveAdmin requiredAdmin = getProfileOwnerAdminLocked(caller.getUserId()); + return requiredAdmin.mPreferentialNetworkServiceConfig; + } + } + + @Override public void setLockTaskPackages(ComponentName who, String[] packages) throws SecurityException { Objects.requireNonNull(who, "ComponentName is null"); @@ -17536,12 +17581,53 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!isManagedProfile(userId)) { return; } - int networkPreference = preferentialNetworkServiceEnabled - ? PROFILE_NETWORK_PREFERENCE_ENTERPRISE : PROFILE_NETWORK_PREFERENCE_DEFAULT; + ProfileNetworkPreference.Builder preferenceBuilder = + new ProfileNetworkPreference.Builder(); + if (preferentialNetworkServiceEnabled) { + preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE); + preferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1); + } else { + preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT); + } + List<ProfileNetworkPreference> preferences = new ArrayList<>(); + preferences.add(preferenceBuilder.build()); + mInjector.binderWithCleanCallingIdentity(() -> + mInjector.getConnectivityManager().setProfileNetworkPreferences( + UserHandle.of(userId), preferences, + null /* executor */, null /* listener */)); + } + + private void updateNetworkPreferenceForUser(int userId, + PreferentialNetworkServiceConfig preferentialNetworkServiceConfig) { + if (!isManagedProfile(userId)) { + return; + } + ProfileNetworkPreference.Builder preferenceBuilder = + new ProfileNetworkPreference.Builder(); + if (preferentialNetworkServiceConfig.isEnabled()) { + if (preferentialNetworkServiceConfig.isFallbackToDefaultConnectionAllowed()) { + preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE); + } else { + preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK); + } + } else { + preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT); + } + List<Integer> allowedUids = Arrays.stream( + preferentialNetworkServiceConfig.getIncludedUids()).boxed().collect( + Collectors.toList()); + List<Integer> excludedUids = Arrays.stream( + preferentialNetworkServiceConfig.getExcludedUids()).boxed().collect( + Collectors.toList()); + preferenceBuilder.setIncludedUids(allowedUids); + preferenceBuilder.setExcludedUids(excludedUids); + preferenceBuilder.setPreferenceEnterpriseId( + preferentialNetworkServiceConfig.getNetworkId()); + 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..710a9cf74112 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -54,6 +54,7 @@ import android.hardware.display.DisplayManagerInternal; import android.net.ConnectivityManager; import android.net.ConnectivityModuleConnector; import android.net.NetworkStackClient; +import android.net.TrafficStats; import android.os.BaseBundle; import android.os.Binder; import android.os.Build; @@ -1338,7 +1339,6 @@ public final class SystemServer implements Dumpable { VcnManagementService vcnManagement = null; NetworkStatsService networkStats = null; NetworkPolicyManagerService networkPolicy = null; - NsdService serviceDiscovery = null; WindowManagerService wm = null; SerialService serial = null; NetworkTimeUpdateService networkTimeUpdater = null; @@ -1837,8 +1837,9 @@ 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); + TrafficStats.init(context); } catch (Throwable e) { reportWtf("starting NetworkStats Service", e); } @@ -1941,16 +1942,6 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); - t.traceBegin("StartNsdService"); - try { - serviceDiscovery = NsdService.create(context); - ServiceManager.addService( - Context.NSD_SERVICE, serviceDiscovery); - } catch (Throwable e) { - reportWtf("starting Service Discovery Service", e); - } - t.traceEnd(); - t.traceBegin("StartSystemUpdateManagerService"); try { ServiceManager.addService(Context.SYSTEM_UPDATE_SERVICE, 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/Android.bp b/services/tests/servicestests/Android.bp index f182b66b910f..428327c00728 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -25,6 +25,7 @@ android_test { "test-apps/JobTestApp/src/**/*.java", "test-apps/SuspendTestApp/src/**/*.java", + ":service-bluetooth-tests-sources", // TODO(b/214988855) : Remove once framework-bluetooth jar is ready ], static_libs: [ "frameworks-base-testutils", 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/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java deleted file mode 100644 index a1d4c203de18..000000000000 --- a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import static org.mockito.Mockito.*; - -import android.bluetooth.BluetoothAdapter; -import android.content.Context; -import android.os.Looper; -import android.provider.Settings; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.MediumTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; - -@MediumTest -@RunWith(AndroidJUnit4.class) -public class BluetoothAirplaneModeListenerTest { - private Context mContext; - private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; - private BluetoothAdapter mBluetoothAdapter; - private BluetoothModeChangeHelper mHelper; - - @Mock BluetoothManagerService mBluetoothManagerService; - - @Before - public void setUp() throws Exception { - mContext = InstrumentationRegistry.getTargetContext(); - - mHelper = mock(BluetoothModeChangeHelper.class); - when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT)) - .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT); - doNothing().when(mHelper).setSettingsInt(anyString(), anyInt()); - doNothing().when(mHelper).showToastMessage(); - doNothing().when(mHelper).onAirplaneModeChanged(any(BluetoothManagerService.class)); - - mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener( - mBluetoothManagerService, Looper.getMainLooper(), mContext); - mBluetoothAirplaneModeListener.start(mHelper); - } - - @Test - public void testIgnoreOnAirplanModeChange() { - Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); - - when(mHelper.isBluetoothOn()).thenReturn(true); - Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); - - when(mHelper.isMediaProfileConnected()).thenReturn(true); - Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); - - when(mHelper.isAirplaneModeOn()).thenReturn(true); - Assert.assertTrue(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); - } - - @Test - public void testHandleAirplaneModeChange_InvokeAirplaneModeChanged() { - mBluetoothAirplaneModeListener.handleAirplaneModeChange(); - verify(mHelper).onAirplaneModeChanged(mBluetoothManagerService); - } - - @Test - public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() { - mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; - when(mHelper.isBluetoothOn()).thenReturn(true); - when(mHelper.isMediaProfileConnected()).thenReturn(true); - when(mHelper.isAirplaneModeOn()).thenReturn(true); - mBluetoothAirplaneModeListener.handleAirplaneModeChange(); - - verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON, - BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); - verify(mHelper, times(0)).showToastMessage(); - verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService); - } - - @Test - public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() { - mBluetoothAirplaneModeListener.mToastCount = 0; - when(mHelper.isBluetoothOn()).thenReturn(true); - when(mHelper.isMediaProfileConnected()).thenReturn(true); - when(mHelper.isAirplaneModeOn()).thenReturn(true); - mBluetoothAirplaneModeListener.handleAirplaneModeChange(); - - verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON, - BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); - verify(mHelper).showToastMessage(); - verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService); - } - - @Test - public void testIsPopToast_PopToast() { - mBluetoothAirplaneModeListener.mToastCount = 0; - Assert.assertTrue(mBluetoothAirplaneModeListener.shouldPopToast()); - verify(mHelper).setSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT, 1); - } - - @Test - public void testIsPopToast_NotPopToast() { - mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; - Assert.assertFalse(mBluetoothAirplaneModeListener.shouldPopToast()); - verify(mHelper, times(0)).setSettingsInt(anyString(), anyInt()); - } -} 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..3511fc1c36d5 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -37,7 +37,11 @@ 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.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK; import static android.net.InetAddresses.parseNumericAddress; +import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback; @@ -87,6 +91,7 @@ import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.DevicePolicyManagerLiteInternal; import android.app.admin.FactoryResetProtectionPolicy; import android.app.admin.PasswordMetrics; +import android.app.admin.PreferentialNetworkServiceConfig; import android.app.admin.SystemUpdatePolicy; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -99,7 +104,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 +4049,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 +4069,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 +4095,188 @@ 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) + .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1) + .build(); + List<ProfileNetworkPreference> preferences2 = new ArrayList<>(); + preferences2.add(preferenceDetails); + verify(getServices().connectivityManager, times(1)) + .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences2, + null, null); + } + + @Test + public void testSetPreferentialNetworkServiceConfig_noProfileOwner() throws Exception { + assertExpectException(SecurityException.class, null, + () -> dpm.setPreferentialNetworkServiceConfig( + PreferentialNetworkServiceConfig.DEFAULT)); + } + + @Test + public void testIsPreferentialNetworkServiceEnabled_noProfileOwner() throws Exception { + assertExpectException(SecurityException.class, null, + () -> dpm.isPreferentialNetworkServiceEnabled()); + } + + @Test + public void testSetPreferentialNetworkServiceConfig_invalidConfig() throws Exception { + final int managedProfileUserId = 15; + final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436); + addManagedProfile(admin1, managedProfileAdminUid, admin1); + mContext.binder.callingUid = managedProfileAdminUid; + + PreferentialNetworkServiceConfig.Builder preferentialNetworkServiceConfigBuilder = + new PreferentialNetworkServiceConfig.Builder(); + assertExpectException(NullPointerException.class, null, + () -> preferentialNetworkServiceConfigBuilder.setIncludedUids(null)); + assertExpectException(NullPointerException.class, null, + () -> preferentialNetworkServiceConfigBuilder.setExcludedUids(null)); + assertExpectException(IllegalArgumentException.class, null, + () -> preferentialNetworkServiceConfigBuilder.setNetworkId(6)); + int[] includedUids = new int[]{1, 2}; + int[] excludedUids = new int[]{3, 4}; + preferentialNetworkServiceConfigBuilder.setIncludedUids(includedUids); + preferentialNetworkServiceConfigBuilder.setExcludedUids(excludedUids); + + assertExpectException(IllegalStateException.class, null, + () -> preferentialNetworkServiceConfigBuilder.build()); + } + + @Test + public void testSetPreferentialNetworkServiceConfig_defaultPreference() throws Exception { + final int managedProfileUserId = 15; + final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436); + addManagedProfile(admin1, managedProfileAdminUid, admin1); + mContext.binder.callingUid = managedProfileAdminUid; + + dpm.setPreferentialNetworkServiceConfig(PreferentialNetworkServiceConfig.DEFAULT); + assertThat(dpm.isPreferentialNetworkServiceEnabled()).isFalse(); + assertThat(dpm.getPreferentialNetworkServiceConfig() + .isEnabled()).isFalse(); + + 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 + public void testSetPreferentialNetworkServiceConfig_enterprisePreference() throws Exception { + PreferentialNetworkServiceConfig preferentialNetworkServiceConfigEnabled = + (new PreferentialNetworkServiceConfig.Builder()) + .setEnabled(true) + .setNetworkId(NET_ENTERPRISE_ID_1) + .build(); + + final int managedProfileUserId = 15; + final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436); + addManagedProfile(admin1, managedProfileAdminUid, admin1); + mContext.binder.callingUid = managedProfileAdminUid; + + dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled); + assertThat(dpm.getPreferentialNetworkServiceConfig() + .isEnabled()).isTrue(); + ProfileNetworkPreference preferenceDetails = + new ProfileNetworkPreference.Builder() + .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE) + .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1) + .build(); + List<ProfileNetworkPreference> preferences = new ArrayList<>(); + preferences.add(preferenceDetails); + verify(getServices().connectivityManager, times(1)) + .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences, + null, null); + } + + @Test + public void testSetPreferentialNetworkServiceConfig_enterprisePreferenceIncludedUids() + throws Exception { + final int managedProfileUserId = 15; + final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436); + addManagedProfile(admin1, managedProfileAdminUid, admin1); + mContext.binder.callingUid = managedProfileAdminUid; + + PreferentialNetworkServiceConfig preferentialNetworkServiceConfigEnabled = + (new PreferentialNetworkServiceConfig.Builder()) + .setEnabled(true) + .setNetworkId(NET_ENTERPRISE_ID_1) + .setFallbackToDefaultConnectionAllowed(false) + .setIncludedUids(new int[]{1, 2}) + .build(); + dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled); + assertThat(dpm.getPreferentialNetworkServiceConfig() + .isEnabled()).isTrue(); + List<Integer> includedList = new ArrayList<>(); + includedList.add(1); + includedList.add(2); + ProfileNetworkPreference preferenceDetails = + new ProfileNetworkPreference.Builder() + .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK) + .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1) + .setIncludedUids(includedList) + .build(); + List<ProfileNetworkPreference> preferences = new ArrayList<>(); + preferences.add(preferenceDetails); + verify(getServices().connectivityManager, times(1)) + .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences, + null, null); + } + + @Test + public void testSetPreferentialNetworkServiceConfig_enterprisePreferenceExcludedUids() + throws Exception { + final int managedProfileUserId = 15; + final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436); + addManagedProfile(admin1, managedProfileAdminUid, admin1); + mContext.binder.callingUid = managedProfileAdminUid; + + PreferentialNetworkServiceConfig preferentialNetworkServiceConfigEnabled = + (new PreferentialNetworkServiceConfig.Builder()) + .setEnabled(true) + .setNetworkId(NET_ENTERPRISE_ID_1) + .setFallbackToDefaultConnectionAllowed(false) + .setExcludedUids(new int[]{1, 2}) + .build(); + + dpm.setPreferentialNetworkServiceConfig(preferentialNetworkServiceConfigEnabled); + assertThat(dpm.getPreferentialNetworkServiceConfig() + .isEnabled()).isTrue(); + List<Integer> excludedUids = new ArrayList<>(); + excludedUids.add(1); + excludedUids.add(2); + ProfileNetworkPreference preferenceDetails = + new ProfileNetworkPreference.Builder() + .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK) + .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1) + .setExcludedUids(excludedUids) + .build(); + List<ProfileNetworkPreference> preferences = new ArrayList<>(); + preferences.clear(); + preferences.add(preferenceDetails); + verify(getServices().connectivityManager, times(1)) + .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences, + 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..e80721a10e90 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,9 @@ 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.MATCH_MOBILE; import static android.net.NetworkTemplate.buildTemplateCarrierMetered; import static android.net.NetworkTemplate.buildTemplateWifi; import static android.net.TrafficStats.MB_IN_BYTES; @@ -98,6 +95,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -108,6 +106,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 +125,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 +136,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 +260,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 +333,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 +413,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 +439,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 +496,15 @@ public class NetworkPolicyManagerServiceTest { verify(mNetworkManager).registerObserver(networkObserver.capture()); mNetworkObserver = networkObserver.getValue(); + // Catch UsageCallback during systemReady(). Simulate NetworkStatsService triggered + // stats updated callback to signal its readiness. + final ArgumentCaptor<NetworkStatsManager.UsageCallback> usageObserver = + ArgumentCaptor.forClass(NetworkStatsManager.UsageCallback.class); + verify(mStatsManager, times(2)) + .registerUsageCallback(any(), anyLong(), any(), usageObserver.capture()); + usageObserver.getValue().onThresholdReached( + new NetworkTemplate.Builder(MATCH_MOBILE).build()); + NetworkPolicy defaultPolicy = mService.buildDefaultCarrierPolicy(0, ""); mDefaultWarningBytes = defaultPolicy.warningBytes; mDefaultLimitBytes = defaultPolicy.limitBytes; @@ -479,7 +528,6 @@ public class NetworkPolicyManagerServiceTest { LocalServices.removeServiceForTest(DeviceIdleInternal.class); LocalServices.removeServiceForTest(AppStandbyInternal.class); LocalServices.removeServiceForTest(UsageStatsManagerInternal.class); - LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class); } @After @@ -1108,10 +1156,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 +1169,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 +1186,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 +1201,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 +1217,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 +1234,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 +1265,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 +1282,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 +1292,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 +1313,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 +1345,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 +1627,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 +1637,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 +1752,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.notifyStatsProviderWarningOrLimitReached(); // 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 +1769,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 +1784,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 { @@ -2099,7 +2053,7 @@ public class NetworkPolicyManagerServiceTest { private static NetworkStateSnapshot buildWifi() { WifiInfo mockWifiInfo = mock(WifiInfo.class); when(mockWifiInfo.makeCopy(anyLong())).thenReturn(mockWifiInfo); - when(mockWifiInfo.getCurrentNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY); + when(mockWifiInfo.getNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY); final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(TEST_IFACE); final NetworkCapabilities networkCapabilities = new NetworkCapabilities.Builder() @@ -2145,7 +2099,7 @@ public class NetworkPolicyManagerServiceTest { } private void verifyAdvisePersistThreshold() throws Exception { - verify(mStatsService).advisePersistThreshold(anyLong()); + verify(mStatsManager).setDefaultGlobalAlert(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/services/wallpapereffectsgeneration/OWNERS b/services/wallpapereffectsgeneration/OWNERS new file mode 100644 index 000000000000..d2d3e2c0a7b6 --- /dev/null +++ b/services/wallpapereffectsgeneration/OWNERS @@ -0,0 +1,4 @@ +susharon@google.com +shanh@google.com +huiwu@google.com +srazdan@google.com diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index b109c4664a7d..9a764a0a0a3a 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"; @@ -4879,6 +4883,10 @@ public class CarrierConfigManager { /** Specifies the PCO id for IPv4 Epdg server address */ public static final String KEY_EPDG_PCO_ID_IPV4_INT = KEY_PREFIX + "epdg_pco_id_ipv4_int"; + /** Controls if the IKE tunnel setup supports EAP-AKA fast reauth */ + public static final String KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL = + KEY_PREFIX + "supports_eap_aka_fast_reauth_bool"; + /** @hide */ @IntDef({AUTHENTICATION_METHOD_EAP_ONLY, AUTHENTICATION_METHOD_CERT}) public @interface AuthenticationMethodType {} @@ -4899,7 +4907,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 +4922,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}) @@ -5022,6 +5033,7 @@ public class CarrierConfigManager { defaults.putBoolean(KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL, false); defaults.putInt(KEY_EPDG_PCO_ID_IPV6_INT, 0); defaults.putInt(KEY_EPDG_PCO_ID_IPV4_INT, 0); + defaults.putBoolean(KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL, false); return defaults; } @@ -5846,10 +5858,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/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index f72832438204..4e32a55b2e6e 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -3820,6 +3820,11 @@ public class SubscriptionManager { * {@link #PHONE_NUMBER_SOURCE_UICC UICC} and decide if the previously set phone number * of source {@link #PHONE_NUMBER_SOURCE_CARRIER carrier} should be updated. * + * <p>The API provides no guarantees of what format the number is in: the format can vary + * depending on the {@code source} and the network etc. Programmatic parsing should be done + * cautiously, for example, after formatting the number to a consistent format with + * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}. + * * <p>Note the assumption is that one subscription (which usually means one SIM) has * only one phone number. The multiple sources backup each other so hopefully at least one * is availavle. For example, for a carrier that doesn't typically set phone numbers @@ -3878,6 +3883,11 @@ public class SubscriptionManager { * from available sources in the following order: {@link #PHONE_NUMBER_SOURCE_CARRIER} * > {@link #PHONE_NUMBER_SOURCE_UICC} > {@link #PHONE_NUMBER_SOURCE_IMS}. * + * <p>The API provides no guarantees of what format the number is in: the format can vary + * depending on the underlying source and the network etc. Programmatic parsing should be done + * cautiously, for example, after formatting the number to a consistent format with + * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}. + * * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID} * for the default one. * @return the phone number, or an empty string if not available. @@ -3916,6 +3926,9 @@ public class SubscriptionManager { * <p>The API is suitable for carrier apps to provide a phone number, for example when * it's not possible to update {@link #PHONE_NUMBER_SOURCE_UICC UICC} directly. * + * <p>It's recommended that the phone number is formatted to well-known formats, + * for example, by {@link PhoneNumberUtils} {@code formatNumber*} methods. + * * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID} * for the default one. * @param number the phone number, or an empty string to remove the previously set number. diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 61de3ac2b25e..154bb11db5e3 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -268,6 +268,13 @@ public class RcsUceAdapter { @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; + /** + * A capability update has been requested due to IMS being registered over INTERNET PDN. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_INTERNET_PDN = 12; + /**@hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "ERROR_", value = { @@ -282,7 +289,8 @@ public class RcsUceAdapter { CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED, - CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_INTERNET_PDN }) public @interface StackPublishTriggerType {} 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/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java index 4ec86b186285..56848b89be6f 100644 --- a/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java +++ b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java @@ -15,20 +15,19 @@ */ package com.android.tests.dataidle; +import static android.net.NetworkStats.METERED_YES; + +import android.app.usage.NetworkStats; +import android.app.usage.NetworkStatsManager; import android.content.Context; -import android.net.INetworkStatsService; -import android.net.INetworkStatsSession; -import android.net.NetworkStats; -import android.net.NetworkStats.Entry; import android.net.NetworkTemplate; -import android.net.TrafficStats; import android.os.Bundle; -import android.os.RemoteException; -import android.os.ServiceManager; import android.telephony.TelephonyManager; import android.test.InstrumentationTestCase; import android.util.Log; +import java.util.Set; + /** * A test that dumps data usage to instrumentation out, used for measuring data usage for idle * devices. @@ -36,7 +35,7 @@ import android.util.Log; public class DataIdleTest extends InstrumentationTestCase { private TelephonyManager mTelephonyManager; - private INetworkStatsService mStatsService; + private NetworkStatsManager mStatsManager; private static final String LOG_TAG = "DataIdleTest"; private final static int INSTRUMENTATION_IN_PROGRESS = 2; @@ -44,8 +43,7 @@ public class DataIdleTest extends InstrumentationTestCase { protected void setUp() throws Exception { super.setUp(); Context c = getInstrumentation().getTargetContext(); - mStatsService = INetworkStatsService.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + mStatsManager = c.getSystemService(NetworkStatsManager.class); mTelephonyManager = (TelephonyManager) c.getSystemService(Context.TELEPHONY_SERVICE); } @@ -53,7 +51,9 @@ public class DataIdleTest extends InstrumentationTestCase { * Test that dumps all the data usage metrics for wifi to instrumentation out. */ public void testWifiIdle() { - NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard(); + final NetworkTemplate template = new NetworkTemplate + .Builder(NetworkTemplate.MATCH_WIFI) + .build(); fetchStats(template); } @@ -61,8 +61,11 @@ public class DataIdleTest extends InstrumentationTestCase { * Test that dumps all the data usage metrics for all mobile to instrumentation out. */ public void testMobile() { - String subscriberId = mTelephonyManager.getSubscriberId(); - NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId); + final String subscriberId = mTelephonyManager.getSubscriberId(); + NetworkTemplate template = new NetworkTemplate + .Builder(NetworkTemplate.MATCH_MOBILE) + .setMeteredness(METERED_YES) + .setSubscriberIds(Set.of(subscriberId)).build(); fetchStats(template); } @@ -72,49 +75,26 @@ public class DataIdleTest extends InstrumentationTestCase { * @param template {@link NetworkTemplate} to match. */ private void fetchStats(NetworkTemplate template) { - INetworkStatsSession session = null; try { - mStatsService.forceUpdate(); - session = mStatsService.openSession(); - final NetworkStats stats = session.getSummaryForAllUid( - template, Long.MIN_VALUE, Long.MAX_VALUE, false); - reportStats(stats); - } catch (RemoteException e) { + mStatsManager.forceUpdate(); + final NetworkStats.Bucket bucket = + mStatsManager.querySummaryForDevice(template, Long.MIN_VALUE, Long.MAX_VALUE); + reportStats(bucket); + } catch (RuntimeException e) { Log.w(LOG_TAG, "Failed to fetch network stats."); - } finally { - TrafficStats.closeQuietly(session); } } /** * Print network data usage stats to instrumentation out - * @param stats {@link NetworkorStats} to print + * @param bucket {@link NetworkStats} to print */ - void reportStats(NetworkStats stats) { + void reportStats(NetworkStats.Bucket bucket) { Bundle result = new Bundle(); - long rxBytes = 0; - long txBytes = 0; - long rxPackets = 0; - long txPackets = 0; - for (int i = 0; i < stats.size(); ++i) { - // Label will be iface_uid_tag_set - Entry statsEntry = stats.getValues(i, null); - // Debugging use. - /* - String labelTemplate = String.format("%s_%d_%d_%d", statsEntry.iface, statsEntry.uid, - statsEntry.tag, statsEntry.set) + "_%s"; - result.putLong(String.format(labelTemplate, "rxBytes"), statsEntry.rxBytes); - result.putLong(String.format(labelTemplate, "txBytes"), statsEntry.txBytes); - */ - rxPackets += statsEntry.rxPackets; - rxBytes += statsEntry.rxBytes; - txPackets += statsEntry.txPackets; - txBytes += statsEntry.txBytes; - } - result.putLong("Total rx Bytes", rxBytes); - result.putLong("Total tx Bytes", txBytes); - result.putLong("Total rx Packets", rxPackets); - result.putLong("Total tx Packets", txPackets); + result.putLong("Total rx Bytes", bucket.getRxBytes()); + result.putLong("Total tx Bytes", bucket.getTxBytes()); + result.putLong("Total rx Packets", bucket.getRxPackets()); + result.putLong("Total tx Packets", bucket.getTxPackets()); getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, result); } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS new file mode 100644 index 000000000000..f7c0a87f5dac --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS @@ -0,0 +1,2 @@ +# window manager > animations/transitions +# Bug component: 316275 diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS new file mode 100644 index 000000000000..301fafa5309e --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS @@ -0,0 +1,2 @@ +# ime +# Bug component: 34867 diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS new file mode 100644 index 000000000000..2c414a27cacb --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS @@ -0,0 +1,4 @@ +# System UI > ... > Overview (recent apps) > UI +# Bug template url: https://b.corp.google.com/issues/new?component=807991&template=1390280 = per-file *Overview* +# window manager > animations/transitions +# Bug template url: https://b.corp.google.com/issues/new?component=316275&template=1018192 diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS new file mode 100644 index 000000000000..897fe5dee7fb --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS @@ -0,0 +1,2 @@ +# System UI > ... > Launcher > Gesture nav +# Bug component: 565144 diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS new file mode 100644 index 000000000000..f7c0a87f5dac --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS @@ -0,0 +1,2 @@ +# window manager > animations/transitions +# Bug component: 316275 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/core/java/android/bluetooth/BluetoothUtils.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java index 867469241f84..399e13600442 100644 --- a/core/java/android/bluetooth/BluetoothUtils.java +++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java @@ -13,29 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package android.net.vcn; -package android.bluetooth; - -import java.time.Duration; - -/** - * {@hide} - */ -public final class BluetoothUtils { - /** - * This utility class cannot be instantiated - */ - private BluetoothUtils() {} - - /** - * Timeout value for synchronous binder call - */ - private static final Duration SYNC_CALLS_TIMEOUT = Duration.ofSeconds(5); - - /** - * @return timeout value for synchronous binder call - */ - static Duration getSyncTimeout() { - return SYNC_CALLS_TIMEOUT; - } +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/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java index 5d2f9d748581..6d269686e42f 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java @@ -58,6 +58,7 @@ import android.util.ArraySet; import com.android.server.VcnManagementService.VcnCallback; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; +import com.android.server.vcn.Vcn.VcnUserMobileDataStateListener; import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener; import org.junit.Before; @@ -208,6 +209,13 @@ public class VcnTest { } @Test + public void testMobileDataStateListenersRegistered() { + // Validate state from setUp() + verify(mTelephonyManager, times(3)) + .registerTelephonyCallback(any(), any(VcnUserMobileDataStateListener.class)); + } + + @Test public void testMobileDataStateCheckedOnInitialization_enabled() { // Validate state from setUp() assertTrue(mVcn.isMobileDataEnabled()); @@ -263,6 +271,24 @@ public class VcnTest { assertFalse(mVcn.isMobileDataEnabled()); } + @Test + public void testSubscriptionSnapshotUpdatesMobileDataStateListeners() { + final TelephonySubscriptionSnapshot updatedSnapshot = + mock(TelephonySubscriptionSnapshot.class); + + doReturn(new ArraySet<>(Arrays.asList(2, 4))) + .when(updatedSnapshot) + .getAllSubIdsInGroup(any()); + + mVcn.updateSubscriptionSnapshot(updatedSnapshot); + mTestLooper.dispatchAll(); + + verify(mTelephonyManager, times(4)) + .registerTelephonyCallback(any(), any(VcnUserMobileDataStateListener.class)); + verify(mTelephonyManager, times(2)) + .unregisterTelephonyCallback(any(VcnUserMobileDataStateListener.class)); + } + private void triggerVcnRequestListeners(NetworkRequestListener requestListener) { for (final int[] caps : TEST_CAPS) { startVcnGatewayWithCapabilities(requestListener, caps); @@ -402,24 +428,17 @@ public class VcnTest { verify(mVcnNetworkProvider).resendAllRequests(requestListener); } - private void verifyMobileDataToggled(boolean startingToggleState, boolean endingToggleState) { - final ArgumentCaptor<ContentObserver> captor = - ArgumentCaptor.forClass(ContentObserver.class); - verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture()); - final ContentObserver contentObserver = captor.getValue(); - + private void setupForMobileDataTest(boolean startingToggleState) { // Start VcnGatewayConnections final NetworkRequestListener requestListener = verifyAndGetRequestListener(); mVcn.setMobileDataEnabled(startingToggleState); triggerVcnRequestListeners(requestListener); - final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways = - mVcn.getVcnGatewayConnectionConfigMap(); - - // Trigger data toggle change. - doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled(); - contentObserver.onChange(false /* selfChange, ignored */); - mTestLooper.dispatchAll(); + } + private void verifyMobileDataToggledUpdatesGatewayConnections( + boolean startingToggleState, + boolean endingToggleState, + Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways) { // Verify that data toggle changes restart ONLY INTERNET or DUN networks, and only if the // toggle state changed. for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : gateways.entrySet()) { @@ -433,29 +452,98 @@ public class VcnTest { } } + final NetworkRequestListener requestListener = verifyAndGetRequestListener(); if (startingToggleState != endingToggleState) { verify(mVcnNetworkProvider).resendAllRequests(requestListener); } assertEquals(endingToggleState, mVcn.isMobileDataEnabled()); } + private void verifyGlobalMobileDataToggled( + boolean startingToggleState, boolean endingToggleState) { + setupForMobileDataTest(startingToggleState); + final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways = + mVcn.getVcnGatewayConnectionConfigMap(); + + // Trigger data toggle change + final ArgumentCaptor<ContentObserver> captor = + ArgumentCaptor.forClass(ContentObserver.class); + verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture()); + final ContentObserver contentObserver = captor.getValue(); + + doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled(); + contentObserver.onChange(false /* selfChange, ignored */); + mTestLooper.dispatchAll(); + + // Verify resultant behavior + verifyMobileDataToggledUpdatesGatewayConnections( + startingToggleState, endingToggleState, gateways); + } + + @Test + public void testGlobalMobileDataEnabled() { + verifyGlobalMobileDataToggled( + false /* startingToggleState */, true /* endingToggleState */); + } + + @Test + public void testGlobalMobileDataDisabled() { + verifyGlobalMobileDataToggled( + true /* startingToggleState */, false /* endingToggleState */); + } + + @Test + public void testGlobalMobileDataObserverFiredWithoutChanges_dataEnabled() { + verifyGlobalMobileDataToggled( + false /* startingToggleState */, false /* endingToggleState */); + } + + @Test + public void testGlobalMobileDataObserverFiredWithoutChanges_dataDisabled() { + verifyGlobalMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */); + } + + private void verifySubscriptionMobileDataToggled( + boolean startingToggleState, boolean endingToggleState) { + setupForMobileDataTest(startingToggleState); + final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways = + mVcn.getVcnGatewayConnectionConfigMap(); + + // Trigger data toggle change. + final ArgumentCaptor<VcnUserMobileDataStateListener> captor = + ArgumentCaptor.forClass(VcnUserMobileDataStateListener.class); + verify(mTelephonyManager, times(3)).registerTelephonyCallback(any(), captor.capture()); + final VcnUserMobileDataStateListener listener = captor.getValue(); + + doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled(); + listener.onUserMobileDataStateChanged(false /* enabled, ignored */); + mTestLooper.dispatchAll(); + + // Verify resultant behavior + verifyMobileDataToggledUpdatesGatewayConnections( + startingToggleState, endingToggleState, gateways); + } + @Test - public void testMobileDataEnabled() { - verifyMobileDataToggled(false /* startingToggleState */, true /* endingToggleState */); + public void testSubscriptionMobileDataEnabled() { + verifyGlobalMobileDataToggled( + false /* startingToggleState */, true /* endingToggleState */); } @Test - public void testMobileDataDisabled() { - verifyMobileDataToggled(true /* startingToggleState */, false /* endingToggleState */); + public void testSubscriptionMobileDataDisabled() { + verifyGlobalMobileDataToggled( + true /* startingToggleState */, false /* endingToggleState */); } @Test - public void testMobileDataObserverFiredWithoutChanges_dataEnabled() { - verifyMobileDataToggled(false /* startingToggleState */, false /* endingToggleState */); + public void testSubscriptionMobileDataListenerFiredWithoutChanges_dataEnabled() { + verifyGlobalMobileDataToggled( + false /* startingToggleState */, false /* endingToggleState */); } @Test - public void testMobileDataObserverFiredWithoutChanges_dataDisabled() { - verifyMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */); + public void testSubscriptionMobileDataListenerFiredWithoutChanges_dataDisabled() { + verifyGlobalMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */); } } 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/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")) { |