summaryrefslogtreecommitdiffstats
path: root/core/tests/bluetoothtests
diff options
context:
space:
mode:
authorBrett Chabot <brettchabot@android.com>2011-05-27 17:09:06 -0700
committerBrett Chabot <brettchabot@android.com>2011-06-01 16:04:30 -0700
commitb525f089cf0f580ccb26e80bbb0db1376ba554cc (patch)
tree7751d22496b77f9e59b5ed09be939de3c85db4dc /core/tests/bluetoothtests
parent73e0bc805a143d8cc2202fccb73230459edc6869 (diff)
downloadframeworks_base-b525f089cf0f580ccb26e80bbb0db1376ba554cc.zip
frameworks_base-b525f089cf0f580ccb26e80bbb0db1376ba554cc.tar.gz
frameworks_base-b525f089cf0f580ccb26e80bbb0db1376ba554cc.tar.bz2
Move bluetooth tests to their own package.
Bug 4501764 Change-Id: Id65de5d96516720e75dfbb5d522f465187e73913
Diffstat (limited to 'core/tests/bluetoothtests')
-rw-r--r--core/tests/bluetoothtests/Android.mk17
-rw-r--r--core/tests/bluetoothtests/AndroidManifest.xml32
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java94
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java390
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java214
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java1487
6 files changed, 2234 insertions, 0 deletions
diff --git a/core/tests/bluetoothtests/Android.mk b/core/tests/bluetoothtests/Android.mk
new file mode 100644
index 0000000..4a1d18c
--- /dev/null
+++ b/core/tests/bluetoothtests/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files.
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_PACKAGE_NAME := BluetoothTests
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
new file mode 100644
index 0000000..96db035
--- /dev/null
+++ b/core/tests/bluetoothtests/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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" >
+
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <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" />
+
+</manifest>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java
new file mode 100644
index 0000000..33e9dd7
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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
new file mode 100644
index 0000000..abd7d9a
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
@@ -0,0 +1,390 @@
+/*
+ * 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";
+
+ 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();
+ }
+
+ /**
+ * Stress test for enabling and disabling Bluetooth.
+ */
+ public void testEnable() {
+ int iterations = BluetoothTestRunner.sEnableIterations;
+ if (iterations == 0) {
+ return;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ mTestUtils.disable(adapter);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("enable iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.enable(adapter);
+ mTestUtils.disable(adapter);
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ mTestUtils.undiscoverable(adapter);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("discoverable iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.discoverable(adapter);
+ mTestUtils.undiscoverable(adapter);
+ }
+
+ mTestUtils.disable(adapter);
+ }
+
+ /**
+ * Stress test for starting and stopping Bluetooth scans.
+ */
+ public void testScan() {
+ int iterations = BluetoothTestRunner.sScanIterations;
+ if (iterations == 0) {
+ return;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ mTestUtils.stopScan(adapter);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("scan iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.startScan(adapter);
+ mTestUtils.stopScan(adapter);
+ }
+
+ mTestUtils.disable(adapter);
+ }
+
+ /**
+ * Stress test for enabling and disabling the PAN NAP profile.
+ */
+ public void testEnablePan() {
+ int iterations = BluetoothTestRunner.sEnablePanIterations;
+ if (iterations == 0) {
+ return;
+ }
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ mTestUtils.disablePan(adapter);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("testEnablePan iteration " + (i + 1) + " of "
+ + iterations);
+ mTestUtils.enablePan(adapter);
+ mTestUtils.disablePan(adapter);
+ }
+
+ mTestUtils.disable(adapter);
+ }
+
+ /**
+ * 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;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ mTestUtils.unpair(adapter, device);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("pair iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothTestRunner.sDevicePairPin);
+ mTestUtils.unpair(adapter, device);
+ }
+ mTestUtils.disable(adapter);
+ }
+
+ /**
+ * 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;
+ }
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ mTestUtils.unpair(adapter, device);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("acceptPair iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.acceptPair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothTestRunner.sDevicePairPin);
+ mTestUtils.unpair(adapter, device);
+ }
+ mTestUtils.disable(adapter);
+ }
+
+ /**
+ * 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;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothTestRunner.sDevicePairPin);
+ mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.A2DP, null);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("connectA2dp iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.connectProfile(adapter, device, BluetoothProfile.A2DP,
+ String.format("connectA2dp(device=%s)", device));
+ mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.A2DP,
+ String.format("disconnectA2dp(device=%s)", device));
+ }
+
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.disable(adapter);
+ }
+
+ /**
+ * 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;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothTestRunner.sDevicePairPin);
+ mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("connectHeadset iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET,
+ String.format("connectHeadset(device=%s)", device));
+ mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET,
+ String.format("disconnectHeadset(device=%s)", device));
+ }
+
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.disable(adapter);
+ }
+
+ /**
+ * 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;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothTestRunner.sDevicePairPin);
+ mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE, null);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("connectInput iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.connectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE,
+ String.format("connectInput(device=%s)", device));
+ mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.INPUT_DEVICE,
+ String.format("disconnectInput(device=%s)", device));
+ }
+
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.disable(adapter);
+ }
+
+ /**
+ * 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;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothTestRunner.sDevicePairPin);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("connectPan iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.connectPan(adapter, device);
+ mTestUtils.disconnectPan(adapter, device);
+ }
+
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.disable(adapter);
+ }
+
+ /**
+ * 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;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ mTestUtils.disablePan(adapter);
+ mTestUtils.enablePan(adapter);
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.acceptPair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothTestRunner.sDevicePairPin);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("incomingPanConnection iteration " + (i + 1) + " of "
+ + iterations);
+ mTestUtils.incomingPanConnection(adapter, device);
+ mTestUtils.incomingPanDisconnection(adapter, device);
+ }
+
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.disablePan(adapter);
+ mTestUtils.disable(adapter);
+ }
+
+ /**
+ * 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;
+ }
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.pair(adapter, device, BluetoothTestRunner.sDevicePairPasskey,
+ BluetoothTestRunner.sDevicePairPin);
+ mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null);
+ mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET, null);
+ mTestUtils.stopSco(adapter, device);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("startStopSco iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.startSco(adapter, device);
+ mTestUtils.stopSco(adapter, device);
+ }
+
+ mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null);
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.disable(adapter);
+ }
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
new file mode 100644
index 0000000..64d2c12
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
@@ -0,0 +1,214 @@
+/*
+ * 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 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.frameworks.coretests/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 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("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
new file mode 100644
index 0000000..f1dd8fe
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -0,0 +1,1487 @@
+/*
+ * 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;
+
+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;
+
+ 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;
+
+ 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:
+ mDevice.setPin(mPin);
+ break;
+ case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+ mDevice.setPasskey(mPasskey);
+ break;
+ case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+ case BluetoothDevice.PAIRING_VARIANT_CONSENT:
+ mDevice.setPairingConfirmation(true);
+ break;
+ case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
+ mDevice.setRemoteOutOfBandData();
+ 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.INPUT_DEVICE:
+ mConnectionAction = BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED;
+ break;
+ case BluetoothProfile.PAN:
+ mConnectionAction = BluetoothPan.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_CHANGED.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 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.INPUT_DEVICE:
+ mInput = (BluetoothInputDevice) proxy;
+ break;
+ case BluetoothProfile.PAN:
+ mPan = (BluetoothPan) 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.INPUT_DEVICE:
+ mInput = null;
+ break;
+ case BluetoothProfile.PAN:
+ mPan = 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 BluetoothInputDevice mInput = null;
+ private BluetoothPan mPan = 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) {
+ int mask = (BluetoothReceiver.STATE_TURNING_ON_FLAG | BluetoothReceiver.STATE_ON_FLAG
+ | BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG);
+ long start = -1;
+ BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+ int state = adapter.getState();
+ switch (state) {
+ case BluetoothAdapter.STATE_ON:
+ assertTrue(adapter.isEnabled());
+ removeReceiver(receiver);
+ return;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ assertFalse(adapter.isEnabled());
+ mask = 0; // Don't check for received intents since we might have missed them.
+ break;
+ case BluetoothAdapter.STATE_OFF:
+ assertFalse(adapter.isEnabled());
+ start = System.currentTimeMillis();
+ assertTrue(adapter.enable());
+ break;
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ start = System.currentTimeMillis();
+ assertTrue(adapter.enable());
+ break;
+ default:
+ removeReceiver(receiver);
+ fail(String.format("enable() invalid state: state=%d", state));
+ }
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < ENABLE_DISABLE_TIMEOUT) {
+ state = adapter.getState();
+ if (state == BluetoothAdapter.STATE_ON
+ && (receiver.getFiredFlags() & mask) == mask) {
+ assertTrue(adapter.isEnabled());
+ long finish = receiver.getCompletedTime();
+ if (start != -1 && finish != -1) {
+ writeOutput(String.format("enable() completed in %d ms", (finish - start)));
+ } else {
+ writeOutput("enable() completed");
+ }
+ removeReceiver(receiver);
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
+ state, BluetoothAdapter.STATE_ON, firedFlags, mask));
+ }
+
+ /**
+ * 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) {
+ int mask = (BluetoothReceiver.STATE_TURNING_OFF_FLAG | BluetoothReceiver.STATE_OFF_FLAG
+ | BluetoothReceiver.SCAN_MODE_NONE_FLAG);
+ long start = -1;
+ BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+ int state = adapter.getState();
+ switch (state) {
+ case BluetoothAdapter.STATE_OFF:
+ assertFalse(adapter.isEnabled());
+ removeReceiver(receiver);
+ return;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ assertFalse(adapter.isEnabled());
+ start = System.currentTimeMillis();
+ break;
+ case BluetoothAdapter.STATE_ON:
+ assertTrue(adapter.isEnabled());
+ start = System.currentTimeMillis();
+ assertTrue(adapter.disable());
+ break;
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ assertFalse(adapter.isEnabled());
+ mask = 0; // Don't check for received intents since we might have missed them.
+ break;
+ default:
+ removeReceiver(receiver);
+ fail(String.format("disable() invalid state: state=%d", state));
+ }
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < ENABLE_DISABLE_TIMEOUT) {
+ state = adapter.getState();
+ if (state == BluetoothAdapter.STATE_OFF
+ && (receiver.getFiredFlags() & mask) == mask) {
+ assertFalse(adapter.isEnabled());
+ long finish = receiver.getCompletedTime();
+ if (start != -1 && finish != -1) {
+ writeOutput(String.format("disable() completed in %d ms", (finish - start)));
+ } else {
+ writeOutput("disable() completed");
+ }
+ removeReceiver(receiver);
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
+ state, BluetoothAdapter.STATE_OFF, firedFlags, mask));
+ }
+
+ /**
+ * 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) {
+ int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
+
+ if (!adapter.isEnabled()) {
+ fail("discoverable() bluetooth not enabled");
+ }
+
+ int scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+ return;
+ }
+
+ BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+ assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE, scanMode);
+ long start = System.currentTimeMillis();
+ assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
+
+ while (System.currentTimeMillis() - start < DISCOVERABLE_UNDISCOVERABLE_TIMEOUT) {
+ scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ && (receiver.getFiredFlags() & mask) == mask) {
+ writeOutput(String.format("discoverable() completed in %d ms",
+ (receiver.getCompletedTime() - start)));
+ removeReceiver(receiver);
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
+ + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,
+ firedFlags, mask));
+ }
+
+ /**
+ * 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) {
+ int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG;
+
+ if (!adapter.isEnabled()) {
+ fail("undiscoverable() bluetooth not enabled");
+ }
+
+ int scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
+ return;
+ }
+
+ BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+ assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, scanMode);
+ long start = System.currentTimeMillis();
+ assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
+
+ while (System.currentTimeMillis() - start < DISCOVERABLE_UNDISCOVERABLE_TIMEOUT) {
+ scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE
+ && (receiver.getFiredFlags() & mask) == mask) {
+ writeOutput(String.format("undiscoverable() completed in %d ms",
+ (receiver.getCompletedTime() - start)));
+ removeReceiver(receiver);
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
+ + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags,
+ mask));
+ }
+
+ /**
+ * 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();
+ // TODO: put assertTrue() around cancelDiscovery() once it starts returning true.
+ 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));
+ }
+
+ /**
+ * 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}, or {@link BluetoothProfile#INPUT_DEVICE}.
+ * @param methodName The method name to printed in the logs. If null, will be
+ * "connectProfile(profile=&lt;profile&gt;, device=&lt;device&gt;)"
+ */
+ 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();
+ assertTrue(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#INPUT_DEVICE}.
+ * @param methodName The method name to printed in the logs. If null, will be
+ * "connectProfile(profile=&lt;profile&gt;, device=&lt;device&gt;)"
+ */
+ 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();
+ assertTrue(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 == BluetoothInputDevice.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, BluetoothInputDevice.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);
+ }
+ }
+
+ 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,
+ BluetoothInputDevice.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_CHANGED};
+ StartStopScoReceiver receiver = new StartStopScoReceiver(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.INPUT_DEVICE:
+ if (mInput != null) {
+ return mInput;
+ }
+ break;
+ case BluetoothProfile.PAN:
+ if (mPan != null) {
+ return mPan;
+ }
+ 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.INPUT_DEVICE:
+ 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;
+ default:
+ return null;
+ }
+ }
+
+ private void sleep(long time) {
+ try {
+ Thread.sleep(time);
+ } catch (InterruptedException e) {
+ }
+ }
+}