ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Android HeadSetClient端通话的传递

2021-09-24 15:06:19  阅读:246  来源: 互联网

标签:CALLS event CURRENT 通话 HeadsetClientService HeadSetClient StackEvent Android


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

}

标签:CALLS,event,CURRENT,通话,HeadsetClientService,HeadSetClient,StackEvent,Android
来源: https://blog.csdn.net/yus201120/article/details/120451400

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有