Android 跟蓝牙耳机建立连接有两种方式。

1. Android 主动跟蓝牙耳机连BluetoothSettings 中和蓝牙耳机配对上之后, BluetoothHeadsetService 会收到BONDING_CREATED_ACTION,这个时候BluetoothHeadsetService 会主动去和蓝牙耳机建立RFCOMM 连接。
if (action.equals(BluetoothIntent.BONDING_CREATED_ACTION)) {
  if (mState == BluetoothHeadset.STATE_DISCONNECTED) {
  // Lets try and initiate an RFCOMM connection
   try {
    mBinder.connectHeadset(address, null);
   } catch (RemoteException e) {}
  }
}

RFCOMM 连接的真正实现是在ConnectionThread 中,它分两步,第一步先通过SDPClient 查询蓝牙设备时候支持Headset 和Handsfree profile。
// 1) SDP query
SDPClient client = SDPClient.getSDPClient(address);
if (DBG) log("Connecting to SDP server (" + address + ")...");
if (!client.connectSDPAsync()) {
  Log.e(TAG, "Failed to start SDP connection to " + address);
  mConnectingStatusHandler.obtainMessage(SDP_ERROR).sendToTarget();
  client.disconnectSDP();
  return;
}
if (isInterrupted()) {
  client.disconnectSDP();
  return;
}
if (!client.waitForSDPAsyncConnect(20000)) { // 20 secs
  if (DBG) log("Failed to make SDP connection to " + address);
  mConnectingStatusHandler.obtainMessage(SDP_ERROR).sendToTarget();
  client.disconnectSDP();
  return;
}
if (DBG) log("SDP server connected (" + address + ")");
int headsetChannel = client.isHeadset();
if (DBG) log("headset channel = " + headsetChannel);
int handsfreeChannel = client.isHandsfree();
if (DBG) log("handsfree channel = " + handsfreeChannel);
client.disconnectSDP(); 第2步才是去真正建立RFCOMM 连接。
// 2) RFCOMM connect

mHeadset = new HeadsetBase(mBluetooth, address, channel);
if (isInterrupted()) {
  return;
}
int result = mHeadset.waitForAsyncConnect(20000, // 20 secs
mConnectedStatusHandler);
if (DBG) log("Headset RFCOMM connection attempt took " +(System.currentTimeMillis() - timestamp) + " ms");
if (isInterrupted()) {
  return;
}
if (result < 0) {
  Log.e(TAG, "mHeadset.waitForAsyncConnect() error: " + result);
  mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
  return;
} else if (result == 0) {
  Log.e(TAG, "mHeadset.waitForAsyncConnect() error: " + result +"(timeout)");
  mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
  return;
} else {
  if (DBG) log("mHeadset.waitForAsyncConnect() success");
  mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED).sendToTarget();
}
当RFCOMM连接成功建立后,BluetoothHeadsetDevice 会收到RFCOMM_CONNECTED消息,它会调用BluetoothHandsfree 来建立SCO 连接,广播通知Headset状态变化的Intent
(PhoneApp 和BluetoothSettings 会接收这个Intent)。
case RFCOMM_CONNECTED:
// success
if (DBG) log("Rfcomm connected");
if (mConnectThread != null) {
  try {
   mConnectThread.join();
  } catch (InterruptedException e) {
   Log.w(TAG, "Connect attempt cancelled, ignoring
   RFCOMM_CONNECTED", e);
   return;
  }
  mConnectThread = null;
}
setState(BluetoothHeadset.STATE_CONNECTED,BluetoothHeadset.RESULT_SUCCESS);
mBtHandsfree.connectHeadset(mHeadset, mHeadsetType);
break; BluetoothHandsfree 会先做一些初始化工作,比如根据是Headset 还是Handsfree 初始化不同的ATParser,并且启动一个接收线程从已建立的RFCOMM上接收蓝牙耳机过来的控制命令(也就是AT 命令),接着判断如果是在打电话过程中,才去建立SCO 连接来打通数据通道。
/* package */
void connectHeadset(HeadsetBase headset, int headsetType) {
  mHeadset = headset;
  mHeadsetType = headsetType;
  if (mHeadsetType == TYPE_HEADSET) {
   initializeHeadsetAtParser();
  } else {
   initializeHandsfreeAtParser();
  }
  headset.startEventThread();
  configAudioParameters();
  if (inDebug()) {
   startDebug();
  }
  if (isIncallAudio()) {
   audioOn();
  }
} 建立SCO 连接是通过SCOSocket 实现的
/** Request to establish SCO (audio) connection to bluetooth
* headset/handsfree, if one is connected. Does not block.
* Returns false if the user has requested audio off, or if there
* is some other immediate problem that will prevent BT audio.
*/
/* package */
synchronized boolean audioOn() {
  mOutgoingSco = createScoSocket();
  if (!mOutgoingSco.connect(mHeadset.getAddress())) {
   mOutgoingSco = null;
  }
  return true;
}
当SCO 连接成功建立后,BluetoothHandsfree 会收到SCO_CONNECTED 消息,它就会去调用AudioManager 的setBluetoothScoOn函数,从而通知音频系统有个蓝牙耳机可用了。
到此,Android 完成了和蓝牙耳机的全部连接。
case SCO_CONNECTED:
if (msg.arg1 == ScoSocket.STATE_CONNECTED && isHeadsetConnected()&&mConnectedSco == null) {
  if (DBG) log("Routing audio for outgoing SCO conection");
  mConnectedSco = (ScoSocket)msg.obj;
  mAudioManager.setBluetoothScoOn(true);
} else if (msg.arg1 == ScoSocket.STATE_CONNECTED) {
  if (DBG) log("Rejecting new connected outgoing SCO socket");
  ((ScoSocket)msg.obj).close();
  mOutgoingSco.close();
}
mOutgoingSco = null;
break; 2. 蓝牙耳机主动跟Android 连首先BluetoothAudioGateway 会在一个线程中收到来自蓝牙耳机的RFCOMM 连接,然后发送消息给BluetoothHeadsetService。
mConnectingHeadsetRfcommChannel = -1;
mConnectingHandsfreeRfcommChannel = -1;
if(waitForHandsfreeConnectNative(SELECT_WAIT_TIMEOUT) == false) {
  if (mTimeoutRemainingMs > 0) {
   try {
    Log.i(tag, "select thread timed out, but " +
    mTimeoutRemainingMs + "ms of
    waiting remain.");
    Thread.sleep(mTimeoutRemainingMs);
   } catch (InterruptedException e) {
    Log.i(tag, "select thread was interrupted (2),
    exiting");
    mInterrupted = true;
   }
  }
}

BluetoothHeadsetService 会根据当前的状态来处理消息,分3 种情况,第一是当前状态是非连接状态,会发送RFCOMM_CONNECTED 消息,后续处理请参见前面的分析。
case BluetoothHeadset.STATE_DISCONNECTED:
// headset connecting us, lets join
setState(BluetoothHeadset.STATE_CONNECTING);
mHeadsetAddress = info.mAddress;
mHeadset = new HeadsetBase(mBluetooth, mHeadsetAddress,info.mSocketFd,info.mRfcommChan,mConnectedStatusHandler);
mHeadsetType = type;
mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED).sendToTarget();
break;
如果当前是正在连接状态, 则先停掉已经存在的ConnectThread,并直接调用BluetoothHandsfree 去建立SCO 连接。
case BluetoothHeadset.STATE_CONNECTING:
// If we are here, we are in danger of a race condition
// incoming rfcomm connection, but we are also attempting an
// outgoing connection. Lets try and interrupt the outgoing
// connection.
mConnectThread.interrupt();
// Now continue with new connection, including calling callback
mHeadset = new HeadsetBase(mBluetooth,mHeadsetAddress,info.mSocketFd,info.mRfcommChan,mConnectedStatusHandler);
mHeadsetType = type;
setState(BluetoothHeadset.STATE_CONNECTED,BluetoothHeadset.RESULT_SUCCESS);
mBtHandsfree.connectHeadset(mHeadset,mHeadsetType);
// Make sure that old outgoing connect thread is dead.
break;
如果当前是已连接的状态,这种情况是一种错误case,所以直接断掉所有连接。 case BluetoothHeadset.STATE_CONNECTED:
if (DBG) log("Already connected to " + mHeadsetAddress + ",disconnecting" +info.mAddress);
mBluetooth.disconnectRemoteDeviceAcl(info.mAddress);
break;
蓝牙耳机也可能会主动发起SCO 连接, BluetoothHandsfree 会接收到一个SCO_ACCEPTED消息,它会去调用AudioManager 的setBluetoothScoOn 函数,从而通知音频系统有个蓝牙耳机可用了。到此,蓝牙耳机完成了和Android 的全部连接。
case SCO_ACCEPTED:
if (msg.arg1 == ScoSocket.STATE_CONNECTED) {
  if (isHeadsetConnected() && mAudioPossible && mConnectedSco ==null) {
   Log.i(TAG, "Routing audio for incoming SCO connection");
   mConnectedSco = (ScoSocket)msg.obj;
   mAudioManager.setBluetoothScoOn(true);
  } else {
   Log.i(TAG, "Rejecting incoming SCO connection");
   ((ScoSocket)msg.obj).close();
  }
} // else error trying to accept, try again
mIncomingSco = createScoSocket();
mIncomingSco.accept();
break;

更多相关文章

  1. Android用户界面开发(21):状态栏提示
  2. 之View state changes(视图状态改变)
  3. Android(安卓)Stomp客户端
  4. Android中文API(133) —— LocationListener
  5. 彻底解决Android(安卓)8.0启动服务问题
  6. 在Android中监视wifi状态
  7. 【Android】PA4D_CH7 文件、保存状态和首选项
  8. 添加接口节点
  9. Android下的RTSP客户端搭建

随机推荐

  1. android 模拟器不能上网问题
  2. 在android使用i2c tools访问i2c
  3. android google map的使用
  4. 细数Android Bug
  5. react中使用微信jssdk分享总结
  6. Android新线程中更新主线程UI中的View方
  7. Android buttom textview 颜色平滑过渡的
  8. vue+cordova移动端Android开发环境配置
  9. Android OpenGL ES 分析与实践
  10. 在Eclips中更改Android工程中包的名字