Android HeadSetClient端通话的传递

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有新的通话
	}

}
上一篇:系统调用SystemCalls-1-总述


下一篇:第十七章:运行时特性-sys:系统特定配置-跟踪程序运行情况-监视栈