summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/tests/coretests/AndroidManifest.xml1
-rw-r--r--core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java30
-rw-r--r--core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java11
-rw-r--r--core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java136
4 files changed, 178 insertions, 0 deletions
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 01734f2f755a..4be6995f46f1 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -44,6 +44,7 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
index 672f252388fd..5dedd4a92b56 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
@@ -316,4 +316,34 @@ public class BluetoothStressTest extends InstrumentationTestCase {
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.sHeadsetAddress);
+ mTestUtils.enable(adapter);
+ mTestUtils.pair(adapter, device, BluetoothTestRunner.sPairPasskey,
+ BluetoothTestRunner.sPairPin);
+ mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET);
+
+ 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);
+ mTestUtils.unpair(adapter, device);
+ mTestUtils.disable(adapter);
+ }
}
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
index cede05a8a174..1febc5cc7beb 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
@@ -39,6 +39,7 @@ import android.util.Log;
* [-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>] \
@@ -62,6 +63,7 @@ public class BluetoothTestRunner extends InstrumentationTestRunner {
public static int sConnectA2dpIterations = 100;
public static int sConnectInputIterations = 100;
public static int sConnectPanIterations = 100;
+ public static int sStartStopScoIterations = 100;
public static String sPairAddress = "";
public static String sHeadsetAddress = "";
@@ -167,6 +169,14 @@ public class BluetoothTestRunner extends InstrumentationTestRunner {
}
}
+ 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("pair_address");
if (val != null) {
sPairAddress = val;
@@ -214,6 +224,7 @@ public class BluetoothTestRunner extends InstrumentationTestRunner {
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("pair_address=%s", sPairAddress));
Log.i(TAG, String.format("a2dp_address=%s", sA2dpAddress));
Log.i(TAG, String.format("headset_address=%s", sHeadsetAddress));
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
index 85c5eaa85b9b..1741119cad9b 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
@@ -22,6 +22,7 @@ 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;
@@ -67,6 +68,11 @@ public class BluetoothTestUtils extends Assert {
private static final int CONNECT_PROXY_TIMEOUT = 5000;
/**
+ * Timeout to start or stop a SCO channel in ms.
+ */
+ private static final int START_STOP_SCO_TIMEOUT = 10000;
+
+ /**
* Time between polls in ms.
*/
private static final int POLL_TIME = 100;
@@ -319,6 +325,32 @@ public class BluetoothTestUtils extends Assert {
}
}
+ 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() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
@@ -1269,6 +1301,103 @@ public class BluetoothTestUtils extends Assert {
}
/**
+ * 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 = "startSco()";
+ mask = StartStopScoReceiver.STATE_CONNECTED_FLAG;
+ } else {
+ methodName = "stopSco()";
+ mask = StartStopScoReceiver.STATE_DISCONNECTED_FLAG;
+ }
+
+ if (!adapter.isEnabled()) {
+ fail(String.format("%s bluetooth not enabled: device=%s, start=%b", methodName, device,
+ isStart));
+ }
+
+ if (!adapter.getBondedDevices().contains(device)) {
+ fail(String.format("%s device not paired: device=%s, start=%b", methodName, device,
+ isStart));
+ }
+
+ AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull(manager);
+
+ if (!manager.isBluetoothScoAvailableOffCall()) {
+ fail(String.format("%s device does not support SCO: device=%s, start=%b", methodName,
+ device, isStart));
+ }
+
+ 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: start=%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.
@@ -1336,6 +1465,13 @@ public class BluetoothTestUtils extends Assert {
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);