1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.bluetooth; 18 19 import android.bluetooth.BluetoothHeadset.ServiceListener; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.os.Environment; 25 import android.util.Log; 26 27 import junit.framework.Assert; 28 29 import java.io.BufferedWriter; 30 import java.io.File; 31 import java.io.FileWriter; 32 import java.io.IOException; 33 import java.util.ArrayList; 34 import java.util.List; 35 36 public class BluetoothTestUtils extends Assert { 37 38 /** 39 * Timeout for {@link BluetoothAdapter#disable()} in ms. 40 */ 41 private static final int DISABLE_TIMEOUT = 20000; 42 43 /** 44 * Timeout for {@link BluetoothAdapter#enable()} in ms. 45 */ 46 private static final int ENABLE_TIMEOUT = 20000; 47 48 /** 49 * Timeout for {@link BluetoothAdapter#setScanMode(int)} in ms. 50 */ 51 private static final int SET_SCAN_MODE_TIMEOUT = 5000; 52 53 /** 54 * Timeout for {@link BluetoothAdapter#startDiscovery()} in ms. 55 */ 56 private static final int START_DISCOVERY_TIMEOUT = 5000; 57 58 /** 59 * Timeout for {@link BluetoothAdapter#cancelDiscovery()} in ms. 60 */ 61 private static final int CANCEL_DISCOVERY_TIMEOUT = 5000; 62 63 /** 64 * Timeout for {@link BluetoothDevice#createBond()} in ms. 65 */ 66 private static final int PAIR_TIMEOUT = 20000; 67 68 /** 69 * Timeout for {@link BluetoothDevice#removeBond()} in ms. 70 */ 71 private static final int UNPAIR_TIMEOUT = 20000; 72 73 /** 74 * Timeout for {@link BluetoothA2dp#connectSink(BluetoothDevice)} in ms. 75 */ 76 private static final int CONNECT_A2DP_TIMEOUT = 20000; 77 78 /** 79 * Timeout for {@link BluetoothA2dp#disconnectSink(BluetoothDevice)} in ms. 80 */ 81 private static final int DISCONNECT_A2DP_TIMEOUT = 20000; 82 83 /** 84 * Timeout for {@link BluetoothHeadset#connectHeadset(BluetoothDevice)} in ms. 85 */ 86 private static final int CONNECT_HEADSET_TIMEOUT = 20000; 87 88 /** 89 * Timeout for {@link BluetoothHeadset#disconnectHeadset(BluetoothDevice)} in ms. 90 */ 91 private static final int DISCONNECT_HEADSET_TIMEOUT = 20000; 92 93 /** 94 * Time between polls in ms. 95 */ 96 private static final int POLL_TIME = 100; 97 98 private Context mContext; 99 100 private BufferedWriter mOutputWriter; 101 102 private BluetoothA2dp mA2dp; 103 104 private BluetoothHeadset mHeadset; 105 106 private String mOutputFile; 107 private String mTag; 108 private class HeadsetServiceListener implements ServiceListener { 109 private boolean mConnected = false; 110 111 public void onServiceConnected() { 112 synchronized (this) { 113 mConnected = true; 114 } 115 } 116 117 public void onServiceDisconnected() { 118 synchronized (this) { 119 mConnected = false; 120 } 121 } 122 123 public boolean isConnected() { 124 synchronized (this) { 125 return mConnected; 126 } 127 } 128 } 129 130 private HeadsetServiceListener mHeadsetServiceListener = new HeadsetServiceListener(); 131 132 private class BluetoothReceiver extends BroadcastReceiver { 133 private static final int DISCOVERY_STARTED_FLAG = 1; 134 private static final int DISCOVERY_FINISHED_FLAG = 1 << 1; 135 private static final int SCAN_MODE_NONE_FLAG = 1 << 2; 136 private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3; 137 private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4; 138 private static final int STATE_OFF_FLAG = 1 << 5; 139 private static final int STATE_TURNING_ON_FLAG = 1 << 6; 140 private static final int STATE_ON_FLAG = 1 << 7; 141 private static final int STATE_TURNING_OFF_FLAG = 1 << 8; 142 private static final int PROFILE_A2DP_FLAG = 1 << 9; 143 private static final int PROFILE_HEADSET_FLAG = 1 << 10; 144 145 private static final int A2DP_STATE_DISCONNECTED = 1; 146 private static final int A2DP_STATE_CONNECTING = 1 << 1; 147 private static final int A2DP_STATE_CONNECTED = 1 << 2; 148 private static final int A2DP_STATE_DISCONNECTING = 1 << 3; 149 private static final int A2DP_STATE_PLAYING = 1 << 4; 150 151 private static final int HEADSET_STATE_DISCONNECTED = 1; 152 private static final int HEADSET_STATE_CONNECTING = 1 << 1; 153 private static final int HEADSET_STATE_CONNECTED = 1 << 2; 154 155 private int mFiredFlags = 0; 156 private int mA2dpFiredFlags = 0; 157 private int mHeadsetFiredFlags = 0; 158 159 @Override 160 public void onReceive(Context context, Intent intent) { 161 synchronized (this) { 162 if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) { 163 mFiredFlags |= DISCOVERY_STARTED_FLAG; 164 } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) { 165 mFiredFlags |= DISCOVERY_FINISHED_FLAG; 166 } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) { 167 int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, 168 BluetoothAdapter.ERROR); 169 assertNotSame(mode, BluetoothAdapter.ERROR); 170 switch (mode) { 171 case BluetoothAdapter.SCAN_MODE_NONE: 172 mFiredFlags |= SCAN_MODE_NONE_FLAG; 173 break; 174 case BluetoothAdapter.SCAN_MODE_CONNECTABLE: 175 mFiredFlags |= SCAN_MODE_CONNECTABLE_FLAG; 176 break; 177 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: 178 mFiredFlags |= SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG; 179 break; 180 } 181 } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) { 182 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 183 BluetoothAdapter.ERROR); 184 assertNotSame(state, BluetoothAdapter.ERROR); 185 switch (state) { 186 case BluetoothAdapter.STATE_OFF: 187 mFiredFlags |= STATE_OFF_FLAG; 188 break; 189 case BluetoothAdapter.STATE_TURNING_ON: 190 mFiredFlags |= STATE_TURNING_ON_FLAG; 191 break; 192 case BluetoothAdapter.STATE_ON: 193 mFiredFlags |= STATE_ON_FLAG; 194 break; 195 case BluetoothAdapter.STATE_TURNING_OFF: 196 mFiredFlags |= STATE_TURNING_OFF_FLAG; 197 break; 198 } 199 } else if (BluetoothA2dp.ACTION_SINK_STATE_CHANGED.equals(intent.getAction())) { 200 mFiredFlags |= PROFILE_A2DP_FLAG; 201 int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, -1); 202 assertNotSame(state, -1); 203 switch (state) { 204 case BluetoothA2dp.STATE_DISCONNECTED: 205 mA2dpFiredFlags |= A2DP_STATE_DISCONNECTED; 206 break; 207 case BluetoothA2dp.STATE_CONNECTING: 208 mA2dpFiredFlags |= A2DP_STATE_CONNECTING; 209 break; 210 case BluetoothA2dp.STATE_CONNECTED: 211 mA2dpFiredFlags |= A2DP_STATE_CONNECTED; 212 break; 213 case BluetoothA2dp.STATE_DISCONNECTING: 214 mA2dpFiredFlags |= A2DP_STATE_DISCONNECTING; 215 break; 216 case BluetoothA2dp.STATE_PLAYING: 217 mA2dpFiredFlags |= A2DP_STATE_PLAYING; 218 break; 219 } 220 } else if (BluetoothHeadset.ACTION_STATE_CHANGED.equals(intent.getAction())) { 221 mFiredFlags |= PROFILE_HEADSET_FLAG; 222 int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 223 BluetoothHeadset.STATE_ERROR); 224 assertNotSame(state, BluetoothHeadset.STATE_ERROR); 225 switch (state) { 226 case BluetoothHeadset.STATE_DISCONNECTED: 227 mHeadsetFiredFlags |= HEADSET_STATE_DISCONNECTED; 228 break; 229 case BluetoothHeadset.STATE_CONNECTING: 230 mHeadsetFiredFlags |= HEADSET_STATE_CONNECTING; 231 break; 232 case BluetoothHeadset.STATE_CONNECTED: 233 mHeadsetFiredFlags |= HEADSET_STATE_CONNECTED; 234 break; 235 } 236 } 237 } 238 } 239 240 public int getFiredFlags() { 241 synchronized (this) { 242 return mFiredFlags; 243 } 244 } 245 246 public int getA2dpFiredFlags() { 247 synchronized (this) { 248 return mA2dpFiredFlags; 249 } 250 } 251 252 public int getHeadsetFiredFlags() { 253 synchronized (this) { 254 return mHeadsetFiredFlags; 255 } 256 } 257 258 public void resetFiredFlags() { 259 synchronized (this) { 260 mFiredFlags = 0; 261 mA2dpFiredFlags = 0; 262 mHeadsetFiredFlags = 0; 263 } 264 } 265 } 266 267 private BluetoothReceiver mBluetoothReceiver = new BluetoothReceiver(); 268 269 private class PairReceiver extends BroadcastReceiver { 270 private final static int PAIR_FLAG = 1; 271 private static final int PAIR_STATE_BONDED = 1; 272 private static final int PAIR_STATE_BONDING = 1 << 1; 273 private static final int PAIR_STATE_NONE = 1 << 2; 274 275 private int mFiredFlags = 0; 276 private int mPairFiredFlags = 0; 277 278 private BluetoothDevice mDevice; 279 private int mPasskey; 280 private byte[] mPin; 281 282 public PairReceiver(BluetoothDevice device, int passkey, byte[] pin) { 283 super(); 284 mDevice = device; 285 mPasskey = passkey; 286 mPin = pin; 287 } 288 289 @Override 290 public void onReceive(Context context, Intent intent) { 291 synchronized (this) { 292 if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction()) 293 && mDevice.equals(intent.getParcelableExtra( 294 BluetoothDevice.EXTRA_DEVICE))) { 295 int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, 296 BluetoothDevice.ERROR); 297 assertNotSame(type, BluetoothDevice.ERROR); 298 switch (type) { 299 case BluetoothDevice.PAIRING_VARIANT_PIN: 300 mDevice.setPin(mPin); 301 break; 302 case BluetoothDevice.PAIRING_VARIANT_PASSKEY: 303 mDevice.setPasskey(mPasskey); 304 break; 305 case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION: 306 case BluetoothDevice.PAIRING_VARIANT_CONSENT: 307 mDevice.setPairingConfirmation(true); 308 break; 309 case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT: 310 mDevice.setRemoteOutOfBandData(); 311 break; 312 } 313 } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction()) 314 && mDevice.equals(intent.getParcelableExtra( 315 BluetoothDevice.EXTRA_DEVICE))) { 316 mFiredFlags |= PAIR_FLAG; 317 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 318 BluetoothDevice.ERROR); 319 assertNotSame(state, BluetoothDevice.ERROR); 320 switch (state) { 321 case BluetoothDevice.BOND_BONDED: 322 mPairFiredFlags |= PAIR_STATE_BONDED; 323 break; 324 case BluetoothDevice.BOND_BONDING: 325 mPairFiredFlags |= PAIR_STATE_BONDING; 326 break; 327 case BluetoothDevice.BOND_NONE: 328 mPairFiredFlags |= PAIR_STATE_NONE; 329 break; 330 } 331 } 332 } 333 } 334 335 public int getFiredFlags() { 336 synchronized (this) { 337 return mFiredFlags; 338 } 339 } 340 341 public int getPairFiredFlags() { 342 synchronized (this) { 343 return mPairFiredFlags; 344 } 345 } 346 347 public void resetFiredFlags() { 348 synchronized (this) { 349 mFiredFlags = 0; 350 mPairFiredFlags = 0; 351 } 352 } 353 } 354 355 private List<BroadcastReceiver> mReceivers = new ArrayList<BroadcastReceiver>(); 356 357 public BluetoothTestUtils(Context context, String tag) { 358 this(context, tag, null); 359 } 360 361 public BluetoothTestUtils(Context context, String tag, String outputFile) { 362 mContext = context; 363 mTag = tag; 364 mOutputFile = outputFile; 365 366 if (mOutputFile == null) { 367 mOutputWriter = null; 368 } else { 369 try { 370 mOutputWriter = new BufferedWriter(new FileWriter(new File( 371 Environment.getExternalStorageDirectory(), mOutputFile), true)); 372 } catch (IOException e) { 373 Log.w(mTag, "Test output file could not be opened", e); 374 mOutputWriter = null; 375 } 376 } 377 378 mA2dp = new BluetoothA2dp(mContext); 379 mHeadset = new BluetoothHeadset(mContext, mHeadsetServiceListener); 380 mBluetoothReceiver = getBluetoothReceiver(mContext); 381 mReceivers.add(mBluetoothReceiver); 382 } 383 384 public void close() { 385 while (!mReceivers.isEmpty()) { 386 mContext.unregisterReceiver(mReceivers.remove(0)); 387 } 388 389 if (mOutputWriter != null) { 390 try { 391 mOutputWriter.close(); 392 } catch (IOException e) { 393 Log.w(mTag, "Test output file could not be closed", e); 394 } 395 } 396 } 397 398 public void enable(BluetoothAdapter adapter) { 399 int mask = (BluetoothReceiver.STATE_TURNING_ON_FLAG | BluetoothReceiver.STATE_ON_FLAG 400 | BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG); 401 mBluetoothReceiver.resetFiredFlags(); 402 403 int state = adapter.getState(); 404 switch (state) { 405 case BluetoothAdapter.STATE_ON: 406 assertTrue(adapter.isEnabled()); 407 return; 408 case BluetoothAdapter.STATE_OFF: 409 case BluetoothAdapter.STATE_TURNING_OFF: 410 assertFalse(adapter.isEnabled()); 411 assertTrue(adapter.enable()); 412 break; 413 case BluetoothAdapter.STATE_TURNING_ON: 414 assertFalse(adapter.isEnabled()); 415 mask = 0; // Don't check for received intents since we might have missed them. 416 break; 417 default: 418 fail("enable() invalid state: state=" + state); 419 } 420 421 long s = System.currentTimeMillis(); 422 while (System.currentTimeMillis() - s < ENABLE_TIMEOUT) { 423 state = adapter.getState(); 424 if (state == BluetoothAdapter.STATE_ON) { 425 assertTrue(adapter.isEnabled()); 426 if ((mBluetoothReceiver.getFiredFlags() & mask) == mask) { 427 mBluetoothReceiver.resetFiredFlags(); 428 writeOutput(String.format("enable() completed in %d ms", 429 (System.currentTimeMillis() - s))); 430 return; 431 } 432 } else { 433 assertFalse(adapter.isEnabled()); 434 assertEquals(BluetoothAdapter.STATE_TURNING_ON, state); 435 } 436 sleep(POLL_TIME); 437 } 438 439 int firedFlags = mBluetoothReceiver.getFiredFlags(); 440 mBluetoothReceiver.resetFiredFlags(); 441 fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", 442 state, BluetoothAdapter.STATE_ON, firedFlags, mask)); 443 } 444 445 public void disable(BluetoothAdapter adapter) { 446 int mask = (BluetoothReceiver.STATE_TURNING_OFF_FLAG | BluetoothReceiver.STATE_OFF_FLAG 447 | BluetoothReceiver.SCAN_MODE_NONE_FLAG); 448 mBluetoothReceiver.resetFiredFlags(); 449 450 int state = adapter.getState(); 451 switch (state) { 452 case BluetoothAdapter.STATE_OFF: 453 assertFalse(adapter.isEnabled()); 454 return; 455 case BluetoothAdapter.STATE_ON: 456 assertTrue(adapter.isEnabled()); 457 assertTrue(adapter.disable()); 458 break; 459 case BluetoothAdapter.STATE_TURNING_ON: 460 assertFalse(adapter.isEnabled()); 461 assertTrue(adapter.disable()); 462 break; 463 case BluetoothAdapter.STATE_TURNING_OFF: 464 assertFalse(adapter.isEnabled()); 465 mask = 0; // Don't check for received intents since we might have missed them. 466 break; 467 default: 468 fail("disable() invalid state: state=" + state); 469 } 470 471 long s = System.currentTimeMillis(); 472 while (System.currentTimeMillis() - s < DISABLE_TIMEOUT) { 473 state = adapter.getState(); 474 if (state == BluetoothAdapter.STATE_OFF) { 475 assertFalse(adapter.isEnabled()); 476 if ((mBluetoothReceiver.getFiredFlags() & mask) == mask) { 477 mBluetoothReceiver.resetFiredFlags(); 478 writeOutput(String.format("disable() completed in %d ms", 479 (System.currentTimeMillis() - s))); 480 return; 481 } 482 } else { 483 assertFalse(adapter.isEnabled()); 484 assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state); 485 } 486 sleep(POLL_TIME); 487 } 488 489 int firedFlags = mBluetoothReceiver.getFiredFlags(); 490 mBluetoothReceiver.resetFiredFlags(); 491 fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)", 492 state, BluetoothAdapter.STATE_OFF, firedFlags, mask)); 493 } 494 495 public void discoverable(BluetoothAdapter adapter) { 496 int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG; 497 mBluetoothReceiver.resetFiredFlags(); 498 499 if (!adapter.isEnabled()) { 500 fail("discoverable() bluetooth not enabled"); 501 } 502 503 int scanMode = adapter.getScanMode(); 504 if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { 505 return; 506 } 507 508 assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE); 509 assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)); 510 511 long s = System.currentTimeMillis(); 512 while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) { 513 scanMode = adapter.getScanMode(); 514 if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { 515 if ((mBluetoothReceiver.getFiredFlags() & mask) == mask) { 516 mBluetoothReceiver.resetFiredFlags(); 517 writeOutput(String.format("discoverable() completed in %d ms", 518 (System.currentTimeMillis() - s))); 519 return; 520 } 521 } else { 522 assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE); 523 } 524 sleep(POLL_TIME); 525 } 526 527 int firedFlags = mBluetoothReceiver.getFiredFlags(); 528 mBluetoothReceiver.resetFiredFlags(); 529 fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x " 530 + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, 531 firedFlags, mask)); 532 } 533 534 public void undiscoverable(BluetoothAdapter adapter) { 535 int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG; 536 mBluetoothReceiver.resetFiredFlags(); 537 538 if (!adapter.isEnabled()) { 539 fail("undiscoverable() bluetooth not enabled"); 540 } 541 542 int scanMode = adapter.getScanMode(); 543 if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) { 544 return; 545 } 546 547 assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); 548 assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE)); 549 550 long s = System.currentTimeMillis(); 551 while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) { 552 scanMode = adapter.getScanMode(); 553 if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) { 554 if ((mBluetoothReceiver.getFiredFlags() & mask) == mask) { 555 mBluetoothReceiver.resetFiredFlags(); 556 writeOutput(String.format("undiscoverable() completed in %d ms", 557 (System.currentTimeMillis() - s))); 558 return; 559 } 560 } else { 561 assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); 562 } 563 sleep(POLL_TIME); 564 } 565 566 int firedFlags = mBluetoothReceiver.getFiredFlags(); 567 mBluetoothReceiver.resetFiredFlags(); 568 fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x " 569 + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags, 570 mask)); 571 } 572 573 public void startScan(BluetoothAdapter adapter) { 574 int mask = BluetoothReceiver.DISCOVERY_STARTED_FLAG; 575 mBluetoothReceiver.resetFiredFlags(); 576 577 if (!adapter.isEnabled()) { 578 fail("startScan() bluetooth not enabled"); 579 } 580 581 if (adapter.isDiscovering()) { 582 return; 583 } 584 585 assertTrue(adapter.startDiscovery()); 586 587 long s = System.currentTimeMillis(); 588 while (System.currentTimeMillis() - s < START_DISCOVERY_TIMEOUT) { 589 if (adapter.isDiscovering() && ((mBluetoothReceiver.getFiredFlags() & mask) == mask)) { 590 mBluetoothReceiver.resetFiredFlags(); 591 writeOutput(String.format("startScan() completed in %d ms", 592 (System.currentTimeMillis() - s))); 593 return; 594 } 595 sleep(POLL_TIME); 596 } 597 598 int firedFlags = mBluetoothReceiver.getFiredFlags(); 599 mBluetoothReceiver.resetFiredFlags(); 600 fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)", 601 adapter.isDiscovering(), firedFlags, mask)); 602 } 603 604 public void stopScan(BluetoothAdapter adapter) { 605 int mask = BluetoothReceiver.DISCOVERY_FINISHED_FLAG; 606 mBluetoothReceiver.resetFiredFlags(); 607 608 if (!adapter.isEnabled()) { 609 fail("stopScan() bluetooth not enabled"); 610 } 611 612 if (!adapter.isDiscovering()) { 613 return; 614 } 615 616 // TODO: put assertTrue() around cancelDiscovery() once it starts returning true. 617 adapter.cancelDiscovery(); 618 619 long s = System.currentTimeMillis(); 620 while (System.currentTimeMillis() - s < CANCEL_DISCOVERY_TIMEOUT) { 621 if (!adapter.isDiscovering() && ((mBluetoothReceiver.getFiredFlags() & mask) == mask)) { 622 mBluetoothReceiver.resetFiredFlags(); 623 writeOutput(String.format("stopScan() completed in %d ms", 624 (System.currentTimeMillis() - s))); 625 return; 626 } 627 sleep(POLL_TIME); 628 } 629 630 int firedFlags = mBluetoothReceiver.getFiredFlags(); 631 mBluetoothReceiver.resetFiredFlags(); 632 fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)", 633 adapter.isDiscovering(), firedFlags, mask)); 634 635 } 636 637 public void pair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, byte[] pin) { 638 pairOrAcceptPair(adapter, device, passkey, pin, true); 639 } 640 641 public void acceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, 642 byte[] pin) { 643 pairOrAcceptPair(adapter, device, passkey, pin, false); 644 } 645 646 private void pairOrAcceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, 647 byte[] pin, boolean pair) { 648 String methodName = pair ? "pair()" : "acceptPair()"; 649 int mask = PairReceiver.PAIR_FLAG; 650 int pairMask = PairReceiver.PAIR_STATE_BONDING | PairReceiver.PAIR_STATE_BONDED; 651 652 PairReceiver pairReceiver = getPairReceiver(mContext, device, passkey, pin); 653 mReceivers.add(pairReceiver); 654 655 if (!adapter.isEnabled()) { 656 fail(methodName + " bluetooth not enabled"); 657 } 658 659 int state = device.getBondState(); 660 switch (state) { 661 case BluetoothDevice.BOND_BONDED: 662 assertTrue(adapter.getBondedDevices().contains(device)); 663 return; 664 case BluetoothDevice.BOND_BONDING: 665 // Don't check for received intents since we might have missed them. 666 mask = pairMask = 0; 667 break; 668 case BluetoothDevice.BOND_NONE: 669 assertFalse(adapter.getBondedDevices().contains(device)); 670 if (pair) { 671 assertTrue(device.createBond()); 672 } 673 break; 674 default: 675 fail(methodName + " invalide state: state=" + state); 676 } 677 678 long s = System.currentTimeMillis(); 679 while (System.currentTimeMillis() - s < PAIR_TIMEOUT) { 680 state = device.getBondState(); 681 if (state == BluetoothDevice.BOND_BONDED) { 682 assertTrue(adapter.getBondedDevices().contains(device)); 683 if ((pairReceiver.getFiredFlags() & mask) == mask 684 && (pairReceiver.getPairFiredFlags() & pairMask) == pairMask) { 685 writeOutput(String.format("%s completed in %d ms: device=%s", 686 methodName, (System.currentTimeMillis() - s), device)); 687 mReceivers.remove(pairReceiver); 688 mContext.unregisterReceiver(pairReceiver); 689 return; 690 } 691 } 692 sleep(POLL_TIME); 693 } 694 695 int firedFlags = pairReceiver.getFiredFlags(); 696 int pairFiredFlags = pairReceiver.getPairFiredFlags(); 697 pairReceiver.resetFiredFlags(); 698 fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x), " 699 + "pairFlags=0x%x (expected 0x%x)", methodName, state, BluetoothDevice.BOND_BONDED, 700 firedFlags, mask, pairFiredFlags, pairMask)); 701 } 702 703 public void unpair(BluetoothAdapter adapter, BluetoothDevice device) { 704 int mask = PairReceiver.PAIR_FLAG; 705 int pairMask = PairReceiver.PAIR_STATE_NONE; 706 707 PairReceiver pairReceiver = getPairReceiver(mContext, device, 0, null); 708 mReceivers.add(pairReceiver); 709 710 if (!adapter.isEnabled()) { 711 fail("unpair() bluetooth not enabled"); 712 } 713 714 int state = device.getBondState(); 715 switch (state) { 716 case BluetoothDevice.BOND_BONDED: 717 assertTrue(adapter.getBondedDevices().contains(device)); 718 assertTrue(device.removeBond()); 719 break; 720 case BluetoothDevice.BOND_BONDING: 721 assertTrue(device.removeBond()); 722 break; 723 case BluetoothDevice.BOND_NONE: 724 assertFalse(adapter.getBondedDevices().contains(device)); 725 return; 726 default: 727 fail("unpair() invalid state: state=" + state); 728 } 729 730 assertTrue(device.removeBond()); 731 732 long s = System.currentTimeMillis(); 733 while (System.currentTimeMillis() - s < UNPAIR_TIMEOUT) { 734 if (device.getBondState() == BluetoothDevice.BOND_NONE) { 735 assertFalse(adapter.getBondedDevices().contains(device)); 736 if ((pairReceiver.getFiredFlags() & mask) == mask 737 && (pairReceiver.getPairFiredFlags() & pairMask) == pairMask) { 738 writeOutput(String.format("unpair() completed in %d ms: device=%s", 739 (System.currentTimeMillis() - s), device)); 740 mReceivers.remove(pairReceiver); 741 mContext.unregisterReceiver(pairReceiver); 742 return; 743 } 744 } 745 } 746 747 int firedFlags = pairReceiver.getFiredFlags(); 748 int pairFiredFlags = pairReceiver.getPairFiredFlags(); 749 pairReceiver.resetFiredFlags(); 750 fail(String.format("unpair() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x), " 751 + "pairFlags=0x%x (expected 0x%x)", state, BluetoothDevice.BOND_BONDED, firedFlags, 752 mask, pairFiredFlags, pairMask)); 753 } 754 755 public void connectA2dp(BluetoothAdapter adapter, BluetoothDevice device) { 756 int mask = BluetoothReceiver.PROFILE_A2DP_FLAG; 757 int a2dpMask1 = (BluetoothReceiver.A2DP_STATE_CONNECTING 758 | BluetoothReceiver.A2DP_STATE_CONNECTED | BluetoothReceiver.A2DP_STATE_PLAYING); 759 int a2dpMask2 = a2dpMask1 ^ BluetoothReceiver.A2DP_STATE_CONNECTED; 760 int a2dpMask3 = a2dpMask1 ^ BluetoothReceiver.A2DP_STATE_PLAYING; 761 mBluetoothReceiver.resetFiredFlags(); 762 763 if (!adapter.isEnabled()) { 764 fail("connectA2dp() bluetooth not enabled"); 765 } 766 767 if (!adapter.getBondedDevices().contains(device)) { 768 fail("connectA2dp() device not paired: device=" + device); 769 } 770 771 int state = mA2dp.getSinkState(device); 772 switch (state) { 773 case BluetoothA2dp.STATE_CONNECTED: 774 case BluetoothA2dp.STATE_PLAYING: 775 assertTrue(mA2dp.isSinkConnected(device)); 776 return; 777 case BluetoothA2dp.STATE_DISCONNECTING: 778 case BluetoothA2dp.STATE_DISCONNECTED: 779 assertFalse(mA2dp.isSinkConnected(device)); 780 assertTrue(mA2dp.connectSink(device)); 781 break; 782 case BluetoothA2dp.STATE_CONNECTING: 783 assertFalse(mA2dp.isSinkConnected(device)); 784 // Don't check for received intents since we might have missed them. 785 mask = a2dpMask1 = a2dpMask2 = a2dpMask3 = 0; 786 break; 787 default: 788 fail("connectA2dp() invalid state: state=" + state); 789 } 790 791 long s = System.currentTimeMillis(); 792 while (System.currentTimeMillis() - s < CONNECT_A2DP_TIMEOUT) { 793 state = mA2dp.getSinkState(device); 794 if (state == BluetoothA2dp.STATE_CONNECTED || state == BluetoothA2dp.STATE_PLAYING) { 795 assertTrue(mA2dp.isSinkConnected(device)); 796 // Check whether STATE_CONNECTING and (STATE_CONNECTED or STATE_PLAYING) intents 797 // have fired if we are checking if intents should be fired. 798 int firedFlags = mBluetoothReceiver.getFiredFlags(); 799 int a2dpFiredFlags = mBluetoothReceiver.getA2dpFiredFlags(); 800 if ((mBluetoothReceiver.getFiredFlags() & mask) == mask 801 && ((a2dpFiredFlags & a2dpMask1) == a2dpMask1 802 || (a2dpFiredFlags & a2dpMask2) == a2dpMask2 803 || (a2dpFiredFlags & a2dpMask3) == a2dpMask3)) { 804 mBluetoothReceiver.resetFiredFlags(); 805 writeOutput(String.format("connectA2dp() completed in %d ms: device=%s", 806 (System.currentTimeMillis() - s), device)); 807 return; 808 } 809 } 810 sleep(POLL_TIME); 811 } 812 813 int firedFlags = mBluetoothReceiver.getFiredFlags(); 814 int a2dpFiredFlags = mBluetoothReceiver.getA2dpFiredFlags(); 815 mBluetoothReceiver.resetFiredFlags(); 816 fail(String.format("connectA2dp() timeout: state=%d (expected %d or %d), " 817 + "flags=0x%x (expected 0x%x), a2dpFlags=0x%x (expected 0x%x or 0x%x or 0x%x)", 818 state, BluetoothHeadset.STATE_CONNECTED, BluetoothA2dp.STATE_PLAYING, firedFlags, 819 mask, a2dpFiredFlags, a2dpMask1, a2dpMask2, a2dpMask3)); 820 } 821 822 public void disconnectA2dp(BluetoothAdapter adapter, BluetoothDevice device) { 823 int mask = BluetoothReceiver.PROFILE_A2DP_FLAG; 824 int a2dpMask = (BluetoothReceiver.A2DP_STATE_DISCONNECTING 825 | BluetoothReceiver.A2DP_STATE_DISCONNECTED); 826 mBluetoothReceiver.resetFiredFlags(); 827 828 if (!adapter.isEnabled()) { 829 fail("disconnectA2dp() bluetooth not enabled"); 830 } 831 832 if (!adapter.getBondedDevices().contains(device)) { 833 fail("disconnectA2dp() device not paired: device=" + device); 834 } 835 836 int state = mA2dp.getSinkState(device); 837 switch (state) { 838 case BluetoothA2dp.STATE_DISCONNECTED: 839 assertFalse(mA2dp.isSinkConnected(device)); 840 return; 841 case BluetoothA2dp.STATE_CONNECTED: 842 case BluetoothA2dp.STATE_PLAYING: 843 assertTrue(mA2dp.isSinkConnected(device)); 844 assertTrue(mA2dp.disconnectSink(device)); 845 break; 846 case BluetoothA2dp.STATE_CONNECTING: 847 assertFalse(mA2dp.isSinkConnected(device)); 848 assertTrue(mA2dp.disconnectSink(device)); 849 break; 850 case BluetoothA2dp.STATE_DISCONNECTING: 851 assertFalse(mA2dp.isSinkConnected(device)); 852 // Don't check for received intents since we might have missed them. 853 mask = a2dpMask = 0; 854 break; 855 default: 856 fail("disconnectA2dp() invalid state: state=" + state); 857 } 858 859 long s = System.currentTimeMillis(); 860 while (System.currentTimeMillis() - s < DISCONNECT_A2DP_TIMEOUT) { 861 state = mA2dp.getSinkState(device); 862 if (state == BluetoothA2dp.STATE_DISCONNECTED) { 863 assertFalse(mA2dp.isSinkConnected(device)); 864 if ((mBluetoothReceiver.getFiredFlags() & mask) == mask 865 && (mBluetoothReceiver.getA2dpFiredFlags() & a2dpMask) == a2dpMask) { 866 mBluetoothReceiver.resetFiredFlags(); 867 writeOutput(String.format("disconnectA2dp() completed in %d ms: device=%s", 868 (System.currentTimeMillis() - s), device)); 869 return; 870 } 871 } 872 sleep(POLL_TIME); 873 } 874 875 int firedFlags = mBluetoothReceiver.getFiredFlags(); 876 int a2dpFiredFlags = mBluetoothReceiver.getA2dpFiredFlags(); 877 mBluetoothReceiver.resetFiredFlags(); 878 fail(String.format("disconnectA2dp() timeout: state=%d (expected %d), " 879 + "flags=0x%x (expected 0x%x), a2dpFlags=0x%x (expected 0x%x)", state, 880 BluetoothA2dp.STATE_DISCONNECTED, firedFlags, mask, a2dpFiredFlags, a2dpMask)); 881 } 882 883 public void connectHeadset(BluetoothAdapter adapter, BluetoothDevice device) { 884 int mask = BluetoothReceiver.PROFILE_HEADSET_FLAG; 885 int headsetMask = (BluetoothReceiver.HEADSET_STATE_CONNECTING 886 | BluetoothReceiver.HEADSET_STATE_CONNECTED); 887 mBluetoothReceiver.resetFiredFlags(); 888 889 if (!adapter.isEnabled()) { 890 fail("connectHeadset() bluetooth not enabled"); 891 } 892 893 if (!adapter.getBondedDevices().contains(device)) { 894 fail("connectHeadset() device not paired: device=" + device); 895 } 896 897 while (!mHeadsetServiceListener.isConnected()) { 898 sleep(POLL_TIME); 899 } 900 901 int state = mHeadset.getState(device); 902 switch (state) { 903 case BluetoothHeadset.STATE_CONNECTED: 904 assertTrue(mHeadset.isConnected(device)); 905 return; 906 case BluetoothHeadset.STATE_DISCONNECTED: 907 assertFalse(mHeadset.isConnected(device)); 908 mHeadset.connectHeadset(device); 909 break; 910 case BluetoothHeadset.STATE_CONNECTING: 911 assertFalse(mHeadset.isConnected(device)); 912 // Don't check for received intents since we might have missed them. 913 mask = headsetMask = 0; 914 break; 915 case BluetoothHeadset.STATE_ERROR: 916 fail("connectHeadset() error state"); 917 break; 918 default: 919 fail("connectHeadset() invalid state: state=" + state); 920 } 921 922 long s = System.currentTimeMillis(); 923 while (System.currentTimeMillis() - s < CONNECT_HEADSET_TIMEOUT) { 924 state = mHeadset.getState(device); 925 if (state == BluetoothHeadset.STATE_CONNECTED) { 926 assertTrue(mHeadset.isConnected(device)); 927 if ((mBluetoothReceiver.getFiredFlags() & mask) == mask 928 && (mBluetoothReceiver.getHeadsetFiredFlags() & headsetMask) == headsetMask) { 929 mBluetoothReceiver.resetFiredFlags(); 930 writeOutput(String.format("connectHeadset() completed in %d ms: device=%s", 931 (System.currentTimeMillis() - s), device)); 932 return; 933 } 934 } 935 sleep(POLL_TIME); 936 } 937 938 int firedFlags = mBluetoothReceiver.getFiredFlags(); 939 int headsetFiredFlags = mBluetoothReceiver.getHeadsetFiredFlags(); 940 mBluetoothReceiver.resetFiredFlags(); 941 fail(String.format("connectHeadset() timeout: state=%d (expected %d), " 942 + "flags=0x%x (expected 0x%x), headsetFlags=0x%s (expected 0x%x)", state, 943 BluetoothHeadset.STATE_CONNECTED, firedFlags, mask, headsetFiredFlags, 944 headsetMask)); 945 } 946 947 public void disconnectHeadset(BluetoothAdapter adapter, BluetoothDevice device) { 948 int mask = BluetoothReceiver.PROFILE_HEADSET_FLAG; 949 int headsetMask = BluetoothReceiver.HEADSET_STATE_DISCONNECTED; 950 mBluetoothReceiver.resetFiredFlags(); 951 952 if (!adapter.isEnabled()) { 953 fail("disconnectHeadset() bluetooth not enabled"); 954 } 955 956 if (!adapter.getBondedDevices().contains(device)) { 957 fail("disconnectHeadset() device not paired: device=" + device); 958 } 959 960 while (!mHeadsetServiceListener.isConnected()) { 961 sleep(POLL_TIME); 962 } 963 964 int state = mHeadset.getState(device); 965 switch (state) { 966 case BluetoothHeadset.STATE_CONNECTED: 967 mHeadset.disconnectHeadset(device); 968 break; 969 case BluetoothHeadset.STATE_CONNECTING: 970 mHeadset.disconnectHeadset(device); 971 break; 972 case BluetoothHeadset.STATE_DISCONNECTED: 973 return; 974 case BluetoothHeadset.STATE_ERROR: 975 fail("disconnectHeadset() error state"); 976 break; 977 default: 978 fail("disconnectHeadset() invalid state: state=" + state); 979 } 980 981 long s = System.currentTimeMillis(); 982 while (System.currentTimeMillis() - s < DISCONNECT_HEADSET_TIMEOUT) { 983 state = mHeadset.getState(device); 984 if (state == BluetoothHeadset.STATE_DISCONNECTED) { 985 assertFalse(mHeadset.isConnected(device)); 986 if ((mBluetoothReceiver.getFiredFlags() & mask) == mask 987 && (mBluetoothReceiver.getHeadsetFiredFlags() & headsetMask) == headsetMask) { 988 mBluetoothReceiver.resetFiredFlags(); 989 writeOutput(String.format("disconnectHeadset() completed in %d ms: device=%s", 990 (System.currentTimeMillis() - s), device)); 991 return; 992 } 993 } 994 sleep(POLL_TIME); 995 } 996 997 int firedFlags = mBluetoothReceiver.getFiredFlags(); 998 int headsetFiredFlags = mBluetoothReceiver.getHeadsetFiredFlags(); 999 mBluetoothReceiver.resetFiredFlags(); 1000 fail(String.format("disconnectHeadset() timeout: state=%d (expected %d), " 1001 + "flags=0x%x (expected 0x%x), headsetFlags=0x%s (expected 0x%x)", state, 1002 BluetoothHeadset.STATE_DISCONNECTED, firedFlags, mask, headsetFiredFlags, 1003 headsetMask)); 1004 } 1005 1006 public void writeOutput(String s) { 1007 Log.i(mTag, s); 1008 if (mOutputWriter == null) { 1009 return; 1010 } 1011 try { 1012 mOutputWriter.write(s + "\n"); 1013 mOutputWriter.flush(); 1014 } catch (IOException e) { 1015 Log.w(mTag, "Could not write to output file", e); 1016 } 1017 } 1018 1019 private BluetoothReceiver getBluetoothReceiver(Context context) { 1020 BluetoothReceiver receiver = new BluetoothReceiver(); 1021 IntentFilter filter = new IntentFilter(); 1022 filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 1023 filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); 1024 filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); 1025 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 1026 filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); 1027 filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); 1028 context.registerReceiver(receiver, filter); 1029 return receiver; 1030 } 1031 1032 private PairReceiver getPairReceiver(Context context, BluetoothDevice device, int passkey, 1033 byte[] pin) { 1034 PairReceiver receiver = new PairReceiver(device, passkey, pin); 1035 IntentFilter filter = new IntentFilter(); 1036 filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST); 1037 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 1038 context.registerReceiver(receiver, filter); 1039 return receiver; 1040 } 1041 1042 private void sleep(long time) { 1043 try { 1044 Thread.sleep(time); 1045 } catch (InterruptedException e) { 1046 } 1047 } 1048 } 1049