Home | History | Annotate | Download | only in bluetooth
      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