Android 蓝牙( Bluetooth)耳机连接分析及实现


  1. Android 实现了对Headset 和Handsfree 两种profile 的支持。其实现核心是BluetoothHeadsetService,在PhoneApp 创建的时候会启动它。  
  2.   
  3.  if (getSystemService(Context.BLUETOOTH_SERVICE) != null) {  
  4.   mBtHandsfree = new BluetoothHandsfree(this, phone);  
  5.   startService(new Intent(this, BluetoothHeadsetService.class));  
  6.  } else {  
  7.   // Device is not bluetooth capable  
  8.   mBtHandsfree = null;  
  9.  }  
  10. BluetoothHeadsetService 通过接收ENABLED_ACTION、BONDING_CREATED_ACTION 、DISABLED_ACTION 和REMOTE_DEVICE_DISCONNECT_REQUESTEDACTION 来改变状态,它也会监听Phone 的状态变化。  
  11.   
  12. IntentFilter filter = new IntentFilter(BluetoothIntent.BONDING_CREATED_ACTION);  
  13. filter.addAction(BluetoothIntent.REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION);  
  14. filter.addAction(BluetoothIntent.ENABLED_ACTION);  
  15. filter.addAction(BluetoothIntent.DISABLED_ACTION);  
  16. registerReceiver(mBluetoothIntentReceiver, filter);  
  17. mPhone.registerForPhoneStateChanged(mStateChangeHandler,PHONE_STATE_CHANGED, null);  
  18. BluetoothHeadsetService 收到ENABLED_ACTION时,会先向BlueZ注册Headset 和Handsfree 两种profile(通过执行sdptool 来实现的,均作为Audio Gateway),然后让BluetoothAudioGateway 接收RFCOMM 连接,让BluetoothHandsfree 接收SCO连接(这些操作都是为了让蓝牙耳机能主动连上Android)。  
  19.   
  20.  if (action.equals(BluetoothIntent.ENABLED_ACTION)) {  
  21.  // SDP server may not be ready, so wait 3 seconds before  
  22.  // registering records.  
  23.  // TODO: Use a different mechanism to register SDP records,  
  24.  // that actually ACK’s on success, so that we can retry rather  
  25.  // than hardcoding a 3 second guess.  
  26.   mHandler.sendMessageDelayed(mHandler.obtainMessage(REGISTER_SDP_RECORDS),3000);  
  27.   mAg.start(mIncomingConnectionHandler);  
  28.   mBtHandsfree.onBluetoothEnabled();  
  29.  }  
  30.  BluetoothHeadsetService 收到DISABLED_ACTION 时,会停止BluetoothAudioGateway 和BluetoothHandsfree。  
  31.   
  32.  if (action.equals(BluetoothIntent.DISABLED_ACTION)) {  
  33.   mBtHandsfree.onBluetoothDisabled();  
  34.   mAg.stop();  
  35.  }  
  36.   
  37. Android 跟蓝牙耳机建立连接有两种方式。  
  38.   
  39. 1. Android 主动跟蓝牙耳机连BluetoothSettings 中和蓝牙耳机配对上之后, BluetoothHeadsetService 会收到BONDING_CREATED_ACTION,这个时候BluetoothHeadsetService 会主动去和蓝牙耳机建立RFCOMM 连接。  
  40.   
  41.  if (action.equals(BluetoothIntent.BONDING_CREATED_ACTION)) {  
  42.   if (mState == BluetoothHeadset.STATE_DISCONNECTED) {  
  43.   // Lets try and initiate an RFCOMM connection  
  44.    try {  
  45.     mBinder.connectHeadset(address, null);  
  46.    } catch (RemoteException e) {}  
  47.   }  
  48.  }  
  49. RFCOMM 连接的真正实现是在ConnectionThread 中,它分两步,第一步先通过SDPClient 查询蓝牙设备时候支持Headset 和Handsfree profile。  
  50.   
  51.  // 1) SDP query  
  52.  SDPClient client = SDPClient.getSDPClient(address);  
  53.  if (DBG) log(”Connecting to SDP server (” + address + “)…”);  
  54.  if (!client.connectSDPAsync()) {  
  55.   Log.e(TAG, “Failed to start SDP connection to ” + address);  
  56.   mConnectingStatusHandler.obtainMessage(SDP_ERROR).sendToTarget();  
  57.   client.disconnectSDP();  
  58.   return;  
  59.  }  
  60.  if (isInterrupted()) {  
  61.   client.disconnectSDP();  
  62.   return;  
  63.  }  
  64.   
  65.  if (!client.waitForSDPAsyncConnect(20000)) { // 20 secs  
  66.   if (DBG) log(”Failed to make SDP connection to ” + address);  
  67.   mConnectingStatusHandler.obtainMessage(SDP_ERROR).sendToTarget();  
  68.   client.disconnectSDP();  
  69.   return;  
  70.  }  
  71.   
  72.  if (DBG) log(”SDP server connected (” + address + “)”);  
  73.  int headsetChannel = client.isHeadset();  
  74.  if (DBG) log(”headset channel = ” + headsetChannel);  
  75.  int handsfreeChannel = client.isHandsfree();  
  76.  if (DBG) log(”handsfree channel = ” + handsfreeChannel);  
  77.  client.disconnectSDP();  
  78.    
  79. 2步才是去真正建立RFCOMM 连接。  
  80. // 2) RFCOMM connect  
  81.   
  82.  mHeadset = new HeadsetBase(mBluetooth, address, channel);  
  83.  if (isInterrupted()) {  
  84.   return;  
  85.  }  
  86.  int result = mHeadset.waitForAsyncConnect(20000// 20 secs  
  87.  mConnectedStatusHandler);  
  88.  if (DBG) log(”Headset RFCOMM connection attempt took ” +(System.currentTimeMillis() – timestamp) + ” ms”);  
  89.  if (isInterrupted()) {  
  90.   return;  
  91.  }  
  92.  if (result < 0) {  
  93.   Log.e(TAG, “mHeadset.waitForAsyncConnect() error: ” + result);  
  94.   mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();  
  95.   return;  
  96.  } else if (result == 0) {  
  97.   Log.e(TAG, “mHeadset.waitForAsyncConnect() error: ” + result +”(timeout)”);  
  98.   mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();  
  99.   return;  
  100.  } else {  
  101.   if (DBG) log(”mHeadset.waitForAsyncConnect() success”);  
  102.   mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED).sendToTarget();  
  103.  }  
  104. 当RFCOMM连接成功建立后,BluetoothHeadsetDevice 会收到RFCOMM_CONNECTED消息,它会调用BluetoothHandsfree 来建立SCO 连接,广播通知Headset状态变化的Intent(PhoneApp 和BluetoothSettings 会接收这个Intent)。  
  105.  case RFCOMM_CONNECTED:  
  106.  // success  
  107.  if (DBG) log(”Rfcomm connected”);  
  108.  if (mConnectThread != null) {  
  109.   try {  
  110.    mConnectThread.join();  
  111.   } catch (InterruptedException e) {  
  112.    Log.w(TAG, “Connect attempt cancelled, ignoring  
  113.    RFCOMM_CONNECTED”, e);  
  114.    return;  
  115.   }  
  116.   mConnectThread = null;  
  117.  }  
  118.  setState(BluetoothHeadset.STATE_CONNECTED,BluetoothHeadset.RESULT_SUCCESS);  
  119.  mBtHandsfree.connectHeadset(mHeadset, mHeadsetType);  
  120.  break;  
  121.   
  122.    
  123.   
  124. BluetoothHandsfree 会先做一些初始化工作,比如根据是Headset 还是Handsfree 初始化不同的ATParser,并且启动一个接收线程从已建立的RFCOMM上接收蓝牙耳机过来的控制命令(也就是AT 命令),接着判断如果是在打电话过程中,才去建立SCO 连接来打通数据通道。  
  125.   
  126.    
  127.   
  128.  /* package */  
  129.  void connectHeadset(HeadsetBase headset, int headsetType) {  
  130.   mHeadset = headset;  
  131.   mHeadsetType = headsetType;  
  132.   if (mHeadsetType == TYPE_HEADSET) {  
  133.    initializeHeadsetAtParser();  
  134.   } else {  
  135.    initializeHandsfreeAtParser();  
  136.   }  
  137.   headset.startEventThread();  
  138.   configAudioParameters();  
  139.   if (inDebug()) {  
  140.    startDebug();  
  141.   }  
  142.   if (isIncallAudio()) {  
  143.    audioOn();  
  144.   }  
  145.  }  
  146.   
  147.    
  148.   
  149. 建立SCO 连接是通过SCOSocket 实现的  
  150.   
  151.  /** Request to establish SCO (audio) connection to bluetooth 
  152.  * headset/handsfree, if one is connected. Does not block. 
  153.  * Returns false if the user has requested audio off, or if there 
  154.  * is some other immediate problem that will prevent BT audio. 
  155.  */  
  156.  /* package */  
  157.  synchronized boolean audioOn() {  
  158.   mOutgoingSco = createScoSocket();  
  159.   if (!mOutgoingSco.connect(mHeadset.getAddress())) {  
  160.    mOutgoingSco = null;  
  161.   }  
  162.   return true;  
  163.  }  
  164. 当SCO 连接成功建立后,BluetoothHandsfree 会收到SCO_CONNECTED 消息,它就会去调用AudioManager 的setBluetoothScoOn函数,从而通知音频系统有个蓝牙耳机可用了。  
  165. 到此,Android 完成了和蓝牙耳机的全部连接。  
  166.   
  167.  case SCO_CONNECTED:  
  168.  if (msg.arg1 == ScoSocket.STATE_CONNECTED && isHeadsetConnected()&&mConnectedSco == null) {  
  169.   if (DBG) log(”Routing audio for outgoing SCO conection”);  
  170.   mConnectedSco = (ScoSocket)msg.obj;  
  171.   mAudioManager.setBluetoothScoOn(true);  
  172.  } else if (msg.arg1 == ScoSocket.STATE_CONNECTED) {  
  173.   if (DBG) log(”Rejecting new connected outgoing SCO socket”);  
  174.   ((ScoSocket)msg.obj).close();  
  175.   mOutgoingSco.close();  
  176.  }  
  177.  mOutgoingSco = null;  
  178.  break;  
  179.   
  180.    
  181.   
  182. 2. 蓝牙耳机主动跟Android 连首先BluetoothAudioGateway 会在一个线程中收到来自蓝牙耳机的RFCOMM 连接,然后发送消息给BluetoothHeadsetService。  
  183.   
  184.    
  185.   
  186.  mConnectingHeadsetRfcommChannel = -1;  
  187.  mConnectingHandsfreeRfcommChannel = -1;  
  188.  if(waitForHandsfreeConnectNative(SELECT_WAIT_TIMEOUT) == false) {  
  189.   if (mTimeoutRemainingMs > 0) {  
  190.    try {  
  191.     Log.i(tag, “select thread timed out, but ” +  
  192.     mTimeoutRemainingMs + “ms of  
  193.     waiting remain.”);  
  194.     Thread.sleep(mTimeoutRemainingMs);  
  195.    } catch (InterruptedException e) {  
  196.     Log.i(tag, “select thread was interrupted (2),  
  197.     exiting”);  
  198.     mInterrupted = true;  
  199.    }  
  200.   }  
  201.  }  
  202.    
  203. BluetoothHeadsetService 会根据当前的状态来处理消息,分3 种情况,第一是当前状态是非连接状态,会发送RFCOMM_CONNECTED 消息,后续处理请参见前面的分析。  
  204.  case BluetoothHeadset.STATE_DISCONNECTED:  
  205.  // headset connecting us, lets join  
  206.  setState(BluetoothHeadset.STATE_CONNECTING);  
  207.  mHeadsetAddress = info.mAddress;  
  208.  mHeadset = new HeadsetBase(mBluetooth, mHeadsetAddress,info.mSocketFd,info.mRfcommChan,mConnectedStatusHandler);  
  209.  mHeadsetType = type;  
  210.  mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED).sendToTarget();  
  211.  break;  
  212. 如果当前是正在连接状态, 则先停掉已经存在的ConnectThread,并直接调用BluetoothHandsfree 去建立SCO 连接。  
  213.  case BluetoothHeadset.STATE_CONNECTING:  
  214.  // If we are here, we are in danger of a race condition  
  215.  // incoming rfcomm connection, but we are also attempting an  
  216.  // outgoing connection. Lets try and interrupt the outgoing  
  217.  // connection.  
  218.  mConnectThread.interrupt();  
  219.  // Now continue with new connection, including calling callback  
  220.  mHeadset = new HeadsetBase(mBluetooth,mHeadsetAddress,info.mSocketFd,info.mRfcommChan,mConnectedStatusHandler);  
  221.  mHeadsetType = type;  
  222.  setState(BluetoothHeadset.STATE_CONNECTED,BluetoothHeadset.RESULT_SUCCESS);  
  223.  mBtHandsfree.connectHeadset(mHeadset,mHeadsetType);  
  224.  // Make sure that old outgoing connect thread is dead.  
  225.  break;  
  226.    
  227. 如果当前是已连接的状态,这种情况是一种错误case,所以直接断掉所有连接。  
  228.  case BluetoothHeadset.STATE_CONNECTED:  
  229.  if (DBG) log(”Already connected to ” + mHeadsetAddress + “,disconnecting” +info.mAddress);  
  230.  mBluetooth.disconnectRemoteDeviceAcl(info.mAddress);  
  231.  break;  
  232. 蓝牙耳机也可能会主动发起SCO 连接, BluetoothHandsfree 会接收到一个SCO_ACCEPTED消息,它会去调用AudioManager 的setBluetoothScoOn 函数,从而通知音频系统有个蓝牙耳机可用了。到此,蓝牙耳机完成了和Android 的全部连接。  
  233.   
  234.    
  235.   
  236.  case SCO_ACCEPTED:  
  237.  if (msg.arg1 == ScoSocket.STATE_CONNECTED) {  
  238.   if (isHeadsetConnected() && mAudioPossible && mConnectedSco ==null) {  
  239.    Log.i(TAG, “Routing audio for incoming SCO connection”);  
  240.    mConnectedSco = (ScoSocket)msg.obj;  
  241.    mAudioManager.setBluetoothScoOn(true);  
  242.   } else {  
  243.    Log.i(TAG, “Rejecting incoming SCO connection”);  
  244.    ((ScoSocket)msg.obj).close();  
  245.   }  
  246.  } // else error trying to accept, try again  
  247.  mIncomingSco = createScoSocket();  
  248.  mIncomingSco.accept();  
  249.  break;  



你可能感兴趣的:(Android 蓝牙( Bluetooth)耳机连接分析及实现)