Android源代码中,如果通话状态有改变,会沿着这样的顺序传递:
蓝牙chip >> HCI接口 >> BlueDroid协议栈 >> Bluetooth >> 广播传递 >> Telecom ,下面重点介绍一下数据在Bluetooth内的传递过程:
通话状态有改变,会通过NativeInterface这个类里面的onCallSetup方法回调通知:
public class NativeInterface {
.........
private void onCallSetup(int callsetup, byte[] address) {
StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CALLSETUP);
event.valueInt = callsetup;
event.device = getDevice(address);
HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
service.messageFromNative(event); //1.通话状态改变的消息给到HeadsetClientService
}
........
}
我们以HF端发起拨号请求为例,那么最开始回调的状态就是CALL_STATE_DIALING,callsetup的值为2。然后NativeInterface会把消息封装成一个StackEvent类型的数据结构,给到HeadsetClientService去处理。
在HeadsetClientService中,对于协议栈上来的数据,它只是做一个转接,会把消息给到对应的状态机HeadsetClientStateMachine处理:
public class HeadsetClientService extends ProfileService {
........
public void messageFromNative(StackEvent stackEvent) {
HeadsetClientStateMachine sm = getStateMachine(stackEvent.device);
sm.sendMessage(StackEvent.STACK_EVENT, stackEvent); //2.消息交给状态机处理
}
........
}
在状态机中,此时状态正常情况下是处于Connected状态,看看对此消息的处理:
class Connected extends State {
......
case StackEvent.EVENT_TYPE_CALLSETUP:
sendMessage(QUERY_CURRENT_CALLS); // 3.这里仅是给自己发一个QUERY_CURRENT_CALLS的消息
break;
好吧,这里状态机只是给自己发了一条QUERY_CURRENT_CALLS的消息,让自己去查询当前的通话状态:
case QUERY_CURRENT_CALLS:
removeMessages(QUERY_CURRENT_CALLS);
if (mCalls.size() > 0) {
// If there are ongoing calls periodically check their status.
sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS);
//3.1这里值得注意,如果已经存在通话,那么就会定期查询通话状态
}
queryCallsStart();
break;
private boolean queryCallsStart() {
clearPendingAction();
mNativeInterface.queryCurrentCalls(getByteAddress(mCurrentDevice)); //3.2调用JNI方法去查询当前的远程设备的通话
addQueuedAction(QUERY_CURRENT_CALLS, 0); //3.3这里会在mQueuedActions消息队列里添加一条记录
return true;
}
最后调用了NativeInterface提供的JNI方法,去查询对应设备的通话状态。
在执行了通话状态查询的请求指令,AG端反馈状态后,首先会回调NativeInterface的onCurrentCalls这个方法:
private void onCurrentCalls(int index, int dir, int state, int mparty, String number,
byte[] address) {
StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CURRENT_CALLS);
event.valueInt = index;
event.valueInt2 = dir;
event.valueInt3 = state;
event.valueInt4 = mparty;
event.valueString = number;
event.device = getDevice(address);
HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
service.messageFromNative(event);
}
同样是给到HeadsetClientService,然后再给到HeadsetClientStateMachine处理:
class Connected extends State {
......
case StackEvent.EVENT_TYPE_CURRENT_CALLS:
queryCallsUpdate(event.valueInt, event.valueInt3, event.valueString,
event.valueInt4
== HeadsetClientHalConstants.CALL_MPTY_TYPE_MULTI,
event.valueInt2
== HeadsetClientHalConstants.CALL_DIRECTION_OUTGOING);
break;
......
}
通话状态的查询结果处理,只是在mCallsUpdate 这个map中添加一个BluetoothHeadsetClientCall对象,没有再继续传递下去。
在onCurrentCalls这个方法之后,还会回调onCmdResult这个方法,它反馈的是HF端请求指令的执行结果,这里对应我们刚刚发起的查询当前通话状态的请求:
private void onCmdResult(int type, int cme, byte[] address) {
StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT);
event.valueInt = type;
event.valueInt2 = cme;
event.device = getDevice(address);
HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
service.messageFromNative(event);
}
同样是给到HeadsetClientService,然后再给到HeadsetClientStateMachine处理:
case StackEvent.EVENT_TYPE_CMD_RESULT:
Pair<Integer, Object> queuedAction = mQueuedActions.poll();
//从请求队列中取出队头的一条数据(之前我们往队列里放了数据)
switch (queuedAction.first) {
case QUERY_CURRENT_CALLS:
queryCallsDone(); //代表查询所有通话的指令已经完成
break;
.......
}
break;
private void queryCallsDone() {
......
// Add the new calls.
for (Integer idx : callAddedIds) { //对于新增加的通话,会走到这里
BluetoothHeadsetClientCall c = mCallsUpdate.get(idx);
mCalls.put(idx, c); //把这个新增加通话放到mCalls里面
sendCallChangedIntent(c); //把通话改变的消息广播出去
}
}
private void sendCallChangedIntent(BluetoothHeadsetClientCall c) { //把通话改变后的状态广播出去
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c);
mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
}
可以看到,最后的通话状态是通过广播的形式传递出去的,BluetoothHeadsetClientCall继承自Parcelable接口,是可以实现序列化传递的。
如果我们要实现一个蓝牙电话的功能,那么直接接收BluetoothHeadsetClient.ACTION_CALL_CHANGED这个广播就可以获取到通话的状态。
在Bluetooth内部,有一个HfpClientConnectionService的类,在HeadsetClientService初始化的时候,就会把它调起,如下:
// Start the HfpClientConnectionService to create connection with telecom when HFP
// connection is available.
Intent startIntent = new Intent(this, HfpClientConnectionService.class);
startService(startIntent);
可以看出,它是连接HFP和Telecom的桥梁。
在它的内部,也实现了BluetoothHeadsetClient.ACTION_CALL_CHANGED这个广播的接收处理,并且把状态传递给Telecom:
if (BluetoothHeadsetClient.ACTION_CALL_CHANGED.equals(action)) {
BluetoothHeadsetClientCall call =
intent.getParcelableExtra(BluetoothHeadsetClient.EXTRA_CALL);
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
HfpClientDeviceBlock block = findBlockForDevice(call.getDevice());
block.handleCall(call);
}
看到HfpClientDeviceBlock里面的handleCall方法:
synchronized void handleCall(BluetoothHeadsetClientCall call) {
HfpClientConnection connection = findConnectionKey(call);
if (connection != null) {
connection.updateCall(call);
connection.handleCallChanged(); //这里就通过之前创建的连接,去把通话改变后的状态给到Telecom
}
if (connection == null) { //第一次的通话变化会走到这里
// Create the connection here, trigger Telecom to bind to us.
buildConnection(call, null);//创建一个连接
mTelecomManager.addNewUnknownCall(mPhoneAccount.getAccountHandle(), b);//告诉Telecom有新的通话
}
}