Add BluetoothCddRootTest to verify CDD requirements in CTS am: a597b2ceb4 am: 206f1c3867 am: e4819194e4
Original change: https://android-review.googlesource.com/c/platform/test/cts-root/+/2507152
Change-Id: I5858f9f998e18cd5f5a692ca24182a157fdbaf17
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/tests/bluetooth/Android.bp b/tests/bluetooth/Android.bp
new file mode 100644
index 0000000..6e117f1
--- /dev/null
+++ b/tests/bluetooth/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsRootBluetoothTestCases",
+ defaults: ["cts_defaults"],
+ static_libs: [
+ "ctstestrunner-axt",
+ "bluetooth-test-util-lib",
+ "compatibility-device-util-axt",
+ "PlatformProperties",
+ "statsd-helper",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "statsdprotonano",
+ ],
+ srcs: ["src/**/*.java"],
+ // Allows access to system apis
+ platform_apis: true,
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts_root",
+ "general-tests",
+ "mts-bluetooth",
+ ],
+ min_sdk_version: "UpsideDownCake",
+}
diff --git a/tests/bluetooth/AndroidManifest.xml b/tests/bluetooth/AndroidManifest.xml
new file mode 100644
index 0000000..58f8d77
--- /dev/null
+++ b/tests/bluetooth/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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="android.bluetooth.cts_root">
+
+ <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.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.bluetooth.cts_root"
+ android:label="CTS root tests of Bluetooth component"/>
+
+</manifest>
+
diff --git a/tests/bluetooth/AndroidTest.xml b/tests/bluetooth/AndroidTest.xml
new file mode 100644
index 0000000..1850b6b
--- /dev/null
+++ b/tests/bluetooth/AndroidTest.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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="Runs Bluetooth CTS root tests">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="bluetooth" />
+ <!-- Instant apps cannot hold android.permission.BLUETOOTH which makes BT tests irrelevant -->
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="CtsRootBluetoothTestCases.apk"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="setenforce 0" />
+ <option name="run-command" value="cmd bluetooth_manager enable" />
+ <option name="run-command" value="cmd bluetooth_manager wait-for-state:STATE_ON" />
+ <option name="teardown-command" value="cmd bluetooth_manager disable" />
+ <option name="teardown-command" value="cmd bluetooth_manager wait-for-state:STATE_OFF" />
+ <option name="teardown-command" value="setenforce 1" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.bluetooth.cts_root"/>
+ </test>
+
+ <!-- Only run Cts Tests in MTS if the Bluetooth Mainline module is installed. -->
+ <object type="module_controller"
+ class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.android.btservices" />
+ <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ </object>
+</configuration>
diff --git a/tests/bluetooth/OWNERS b/tests/bluetooth/OWNERS
new file mode 100644
index 0000000..f5f3106
--- /dev/null
+++ b/tests/bluetooth/OWNERS
@@ -0,0 +1,5 @@
+siyuanh@google.com
+muhammadfalam@google.com
+sattiraju@google.com
+girardier@google.com
+sungsoo@google.com
diff --git a/tests/bluetooth/TEST_MAPPING b/tests/bluetooth/TEST_MAPPING
new file mode 100644
index 0000000..324c0b1
--- /dev/null
+++ b/tests/bluetooth/TEST_MAPPING
@@ -0,0 +1,29 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsRootBluetoothTestCases",
+ "options": [
+ {
+ // TODO(b/275847929)
+ "exclude-filter": "android.bluetooth.cts_root.BluetoothCddRootTest#test_C_12_1_Bluetooth5Requirements"
+ }
+ ]
+ }
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "CtsRootBluetoothTestCases"
+ }
+ ],
+ "kernel-presubmit": [
+ {
+ "name": "CtsRootBluetoothTestCases",
+ "options": [
+ {
+ // TODO(b/275847929)
+ "exclude-filter": "android.bluetooth.cts_root.BluetoothCddRootTest#test_C_12_1_Bluetooth5Requirements"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/bluetooth/src/android/bluetooth/cts_root/BluetoothCddRootTest.java b/tests/bluetooth/src/android/bluetooth/cts_root/BluetoothCddRootTest.java
new file mode 100644
index 0000000..949fe36
--- /dev/null
+++ b/tests/bluetooth/src/android/bluetooth/cts_root/BluetoothCddRootTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2023 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.cts_root;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.Manifest.permission.BLUETOOTH_SCAN;
+import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.cts.BTAdapterUtils;
+import android.bluetooth.cts.TestUtils;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.helpers.StatsdHelper;
+import com.android.os.nano.AtomsProto;
+import com.android.os.nano.StatsLog;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Test cases that can only run in rooted environments
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BluetoothCddRootTest {
+ private static final int BLUETOOTH_CORE_SPECIFICATION_4_2 = 0x08;
+ private static final int BLUETOOTH_CORE_SPECIFICATION_5_0 = 0x09;
+ private static final int BLUETOOTH_LOCAL_VERSION_REPORTED_ATOM_ID = 530;
+ // Some devices need some extra time after entering STATE_OFF
+ private static final int BLUETOOTH_TOGGLE_DELAY_MS = 2000;
+
+ private Context mContext;
+ private boolean mHasBluetooth;
+ private BluetoothAdapter mAdapter;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mHasBluetooth = TestUtils.hasBluetooth();
+ Assume.assumeTrue(mHasBluetooth);
+ TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT,
+ BLUETOOTH_PRIVILEGED, BLUETOOTH_SCAN, DUMP, PACKAGE_USAGE_STATS);
+ mAdapter = TestUtils.getBluetoothAdapterOrDie();
+ if (mAdapter.isEnabled()) {
+ assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue();
+ try {
+ Thread.sleep(BLUETOOTH_TOGGLE_DELAY_MS);
+ } catch (InterruptedException ignored) { }
+ }
+ }
+
+ @After
+ public void tearDown() {
+ if (!mHasBluetooth) {
+ return;
+ }
+ if (mAdapter != null && mAdapter.getState() != BluetoothAdapter.STATE_OFF) {
+ if (mAdapter.getState() == BluetoothAdapter.STATE_ON) {
+ assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue();
+ }
+ try {
+ Thread.sleep(BLUETOOTH_TOGGLE_DELAY_MS);
+ } catch (InterruptedException ignored) { }
+ }
+ mAdapter = null;
+ TestUtils.dropPermissionAsShellUid();
+ }
+
+ @Test
+ public void test_C_1_1_VrHighPerformance() {
+ Assume.assumeTrue(mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE));
+ assertThat(mHasBluetooth).isTrue();
+ AtomsProto.BluetoothLocalVersionsReported version = getBluetoothVersion();
+ assertThat(version.hciVersion).isAtLeast(BLUETOOTH_CORE_SPECIFICATION_4_2);
+ assertThat(TestUtils.isBleSupported(mContext)).isTrue();
+ // TODO: Enforce LE data length extension
+ }
+
+ @Test
+ public void test_C_12_1_Bluetooth5Requirements() {
+ Assume.assumeTrue(mHasBluetooth);
+ AtomsProto.BluetoothLocalVersionsReported version = getBluetoothVersion();
+ if (version.hciVersion >= BLUETOOTH_CORE_SPECIFICATION_5_0) {
+ // Assert LMP Version is larger than or equal to HCI version
+ assertThat(version.lmpVersion).isAtLeast(version.hciVersion);
+ assertThat(mAdapter.isLe2MPhySupported()).isTrue();
+ assertThat(mAdapter.isLeCodedPhySupported()).isTrue();
+ assertThat(mAdapter.isLeExtendedAdvertisingSupported()).isTrue();
+ assertThat(mAdapter.isLePeriodicAdvertisingSupported()).isTrue();
+ assertThat(mAdapter.isMultipleAdvertisementSupported()).isTrue();
+ // TODO: Enforce number of advertisement supported
+ // TODO: Enforce number of concurrent LE-ACL connections supported
+ }
+ }
+
+ /**
+ * Get Bluetooth version information. Bluetooth is enabled after this method call.
+ *
+ * Requires ROOT access on the running Android device
+ *
+ * @return Bluetooth version proto
+ */
+ private AtomsProto.BluetoothLocalVersionsReported getBluetoothVersion() {
+ if (mAdapter.isEnabled()) {
+ assertThat(BTAdapterUtils.disableAdapter(mAdapter, mContext)).isTrue();
+ try {
+ Thread.sleep(BLUETOOTH_TOGGLE_DELAY_MS);
+ } catch (InterruptedException ignored) { }
+ }
+ StatsdHelper statsdHelper = new StatsdHelper();
+ // Requires root to enable metrics
+ assertThat(statsdHelper.addEventConfig(
+ List.of(BLUETOOTH_LOCAL_VERSION_REPORTED_ATOM_ID))).isTrue();
+ assertThat(BTAdapterUtils.enableAdapter(mAdapter, mContext)).isTrue();
+ List<StatsLog.EventMetricData> metrics = statsdHelper.getEventMetrics();
+ AtomsProto.BluetoothLocalVersionsReported summaryAtom =
+ new AtomsProto.BluetoothLocalVersionsReported();
+ // When multiple atoms are reported use the maximum value of HCI version
+ // They should really all be the same
+ int i = 0;
+ for (StatsLog.EventMetricData data : metrics) {
+ AtomsProto.BluetoothLocalVersionsReported atom =
+ data.atom.getBluetoothLocalVersionsReported();
+ if (atom == null) {
+ continue;
+ }
+ Log.i("BluetoothCddTest", "[" + i + "] HCI version is " + atom.hciVersion
+ + ", LMP version is " + atom.lmpVersion);
+ assertThat(atom.lmpManufacturerName).isGreaterThan(0);
+ assertThat(atom.lmpVersion).isGreaterThan(0);
+ assertThat(atom.hciVersion).isGreaterThan(0);
+ if (atom.hciVersion > summaryAtom.hciVersion) {
+ summaryAtom.lmpManufacturerName = atom.lmpManufacturerName;
+ summaryAtom.lmpVersion = atom.lmpVersion;
+ summaryAtom.lmpSubversion = atom.lmpSubversion;
+ summaryAtom.hciVersion = atom.hciVersion;
+ summaryAtom.hciRevision = atom.hciRevision;
+ }
+ i++;
+ }
+ return summaryAtom;
+ }
+}