张牧雨名字打分:Android跟蓝牙耳机建立连接有两种方式

来源:百度文库 编辑:偶看新闻 时间:2024/05/07 06:44:29
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;