车机蓝牙通话流程分析的流程分析

Posted Fresh_Air_Life

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了车机蓝牙通话流程分析的流程分析相关的知识,希望对你有一定的参考价值。

PlantUML Web Server

部分内容参照Android HeadSetClient端通话的传递_天花板之恋的博客-CSDN博客

android源代码中,如果通话状态有改变,会沿着这样的顺序传递:
蓝牙chip >> HCI接口 >> BlueDroid协议栈 >> Bluetooth >> 广播传递 >> Telecom ,

2. bluetooth 上层流程分析

2.1 收到JNI回调

通话状态有改变,会通过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去处理。

2.2 service 封装消息

在HeadsetClientService中,对于协议栈上来的数据,它只是做一个转接,会把消息给到对应的状态机HeadsetClientStateMachine处理:

public class HeadsetClientService extends ProfileService 
		........
	public void messageFromNative(StackEvent stackEvent) 
        HeadsetClientStateMachine sm = getStateMachine(stackEvent.device);

        sm.sendMessage(StackEvent.STACK_EVENT, stackEvent);  //2.消息交给状态机处理
    
	........

2.3状态机处理

在状态机中,此时状态正常情况下是处于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方法,去查询对应设备的通话状态。

2.4 在执行了通话状态查询的请求指令,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对象,没有再继续传递下去。

2.5 

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


3.telecom 处理逻辑

1. telecomManager 开始处理事件

    @SystemApi
    public void addNewUnknownCall(PhoneAccountHandle phoneAccount, Bundle extras) 
        try 
            if (isServiceConnected()) 
                getTelecomService().addNewUnknownCall(
                        phoneAccount, extras == null ? new Bundle() : extras);
            
         catch (RemoteException e) 
            Log.e(TAG, "RemoteException adding a new unknown call: " + phoneAccount, e);
        
    

具体是实现在 TelecomServiceImpl

        @Override
        public void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) 
                。。。

                synchronized (mLock) 
                    if (phoneAccountHandle != null &&
                            phoneAccountHandle.getComponentName() != null) 
                        mAppOpsManager.checkPackage(
                                Binder.getCallingUid(),
                                phoneAccountHandle.getComponentName().getPackageName());

                        // Make sure it doesn't cross the UserHandle boundary
                        enforceUserHandleMatchesCaller(phoneAccountHandle);
                        enforcePhoneAccountIsRegisteredEnabled(phoneAccountHandle,
                                Binder.getCallingUserHandle());
                        long token = Binder.clearCallingIdentity();

                        try 
                            Intent intent = new Intent(TelecomManager.ACTION_NEW_UNKNOWN_CALL);
                            if (extras != null) 
                                extras.setDefusable(true);
                                intent.putExtras(extras);
                            
                            intent.putExtra(CallIntentProcessor.KEY_IS_UNKNOWN_CALL, true);
                            intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
                                    phoneAccountHandle);
   // 发送intent
                            mCallIntentProcessorAdapter.processUnknownCallIntent(mCallsManager, intent);
                         finally 
                            Binder.restoreCallingIdentity(token);
                        
                     else 
                        Log.i(this,
                                "Null phoneAccountHandle or not initiated by Telephony. " +
                                        "Ignoring request to add new unknown call.");
                    
                  。。。

        

CallIntentProcessor  将事件交给 callmanager 处理。

    static void processUnknownCallIntent(CallsManager callsManager, Intent intent) 
        PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
                TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);

        if (phoneAccountHandle == null) 
            Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null phone account");
            return;
        
        if (phoneAccountHandle.getComponentName() == null) 
            Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null component name");
            return;
        

        callsManager.addNewUnknownCall(phoneAccountHandle, intent.getExtras());
    

callManager 会新建一个call 事件, 

    void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) 
        Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
        Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
        Call call = new Call(
                getNextCallId(),
                mContext,
                this,
                mLock,
                mConnectionServiceRepository,
                mPhoneNumberUtilsAdapter,
                handle,
                null /* gatewayInfo */,
                null /* connectionManagerPhoneAccount */,
                phoneAccountHandle,
                Call.CALL_DIRECTION_UNKNOWN /* callDirection */,
                // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
                // to the existing connection instead of trying to create a new one.
                true /* forceAttachToExistingConnection */,
                false, /* isConference */
                mClockProxy);
        call.initAnalytics();  

        setIntentExtrasAndStartTime(call, extras);
        call.addListener(this);  
        call.startCreateConnection(mPhoneAccountRegistrar);
    

Call 会创建CreateConnectionProcessor 处理

    void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) 
        if (mCreateConnectionProcessor != null) 
            Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +
                    " due to a race between NewOutgoingCallIntentBroadcaster and " +
                    "phoneAccountSelected, but is harmlessly resolved by ignoring the second " +
                    "invocation.");
            return;
        
        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
                phoneAccountRegistrar, mContext);
        mCreateConnectionProcessor.process();
    
CreateConnectionProcessor 
    public void process() 
        Log.v(this, "process");
        clearTimeout();
        mAttemptRecords = new ArrayList<>();
        if (mCall.getTargetPhoneAccount() != null) 
            mAttemptRecords.add(new CallAttemptRecord(
                    mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
        
        if (!mCall.isSelfManaged()) 
            adjustAttemptsForConnectionManager();
            adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
        
        mAttemptRecordIterator = mAttemptRecords.iterator();
        attemptNextPhoneAccount();
    

    private void attemptNextPhoneAccount() 
        Log.v(this, "attemptNextPhoneAccount");
        CallAttemptRecord attempt = null;
        if (mAttemptRecordIterator.hasNext()) 
            attempt = mAttemptRecordIterator.next();

            if (!mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(
                    attempt.connectionManagerPhoneAccount)) 
                Log.w(this,
                        "Connection mgr does not have BIND_TELECOM_CONNECTION_SERVICE for "
                                + "attempt: %s", attempt);
                attemptNextPhoneAccount();
                return;
            

            // If the target PhoneAccount differs from the ConnectionManager phone acount, ensure it
            // also requires the BIND_TELECOM_CONNECTION_SERVICE permission.
            if (!attempt.connectionManagerPhoneAccount.equals(attempt.targetPhoneAccount) &&
                    !mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(
                            attempt.targetPhoneAccount)) 
                Log.w(this,
                        "Target PhoneAccount does not have BIND_TELECOM_CONNECTION_SERVICE for "
                                + "attempt: %s", attempt);
                attemptNextPhoneAccount();
                return;
            
        

        if (mCallResponse != null && attempt != null) 
            Log.i(this, "Trying attempt %s", attempt);
            PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
            mService = mRepository.getService(phoneAccount.getComponentName(),
                    phoneAccount.getUserHandle());
            if (mService == null) 
                Log.i(this, "Found no connection service for attempt %s", attempt);
                attemptNextPhoneAccount();
             else 
                mConnectionAttempt++;
                mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
                mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
                mCall.setConnectionService(mService);
                setTimeoutIfNeeded(mService, attempt);
                if (mCall.isIncoming()) 
                    mService.createConnection(mCall, CreateConnectionProcessor.this);
                 else 
                    // Start to create the connection for outgoing call after the ConnectionService
                    // of the call has gained the focus.
                    mCall.getConnectionServiceFocusManager().requestFocus(
                            mCall,
                            new CallsManager.RequestCallback(new CallsManager.PendingAction() 
                                @Override
                                public void performAction() 
                                    Log.d(this, "perform create connection");
                                    mService.createConnection(
                                            mCall,
                                            CreateConnectionProcessor.this);
                                
                            ));

                
            
         else 
            Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");
            DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?
                    mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);
            notifyCallConnectionFailure(disconnectCause);
        
    

 ???????????????

ConnectionServiceWrapper 这里会执行createConnection 操作
        @Override
        public void handleCreateConnectionComplete(String callId, ConnectionRequest request,
                ParcelableConnection connection, Session.Info sessionInfo) 
            Log.startSession(sessionInfo, LogUtils.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE);
            long token = Binder.clearCallingIdentity();
            try 
                synchronized (mLock) 
                    logIncoming("handleCreateConnectionComplete %s", callId);
                    ConnectionServiceWrapper.this
                            .handleCreateConnectionComplete(callId, request, connection);

                    if (mServiceInterface != null) 
                        logOutgoing("createConnectionComplete %s", callId);
                        try 
                            mServiceInterface.createConnectionComplete(callId,
                                    Log.getExternalSession());
                         catch (RemoteException e) 
                        
                    
                
             catch (Throwable t) 
                Log.e(ConnectionServiceWrapper.this, t, "");
                throw t;
             finally 
                Binder.restoreCallingIdentity(token);
                Log.endSession();
            
        

调用Call.java 的handleCreateConnectionSuccess

    @Override
    public void handleCreateConnectionSuccess(
            CallIdMapper idMapper,
            ParcelableConnection connection) 
        Log.d(this, "handleCreateConnectionSuccessful %s", connection);
        setTargetPhoneAccount(connection.getPhoneAccount());
        setHandle(connection.getHandle(), connection.getHandlePresentation());
        setCallerDisplayName(
                connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());

        setConnectionCapabilities(connection.getConnectionCapabilities());
        setConnectionProperties(connection.getConnectionProperties());
        setIsVoipAudioMode(connection.getIsVoipAudioMode());
        setSupportedAudioRoutes(connection.getSupportedAudioRoutes());
        setVideoProvider(connection.getVideoProvider());
        setVideoState(connection.getVideoState());
        setRingbackRequested(connection.isRingbackRequested());
        setStatusHints(connection.getStatusHints());
        putExtras(SOURCE_CONNECTION_SERVICE, connection.getExtras());

        mConferenceableCalls.clear();
        for (String id : connection.getConferenceableConnectionIds()) 
            mConferenceableCalls.add(idMapper.getCall(id));
        

        switch (mCallDirection) 
            case CALL_DIRECTION_INCOMING:
                // Listeners (just CallsManager for now) will be responsible for checking whether
                // the call should be blocked.
                for (Listener l : mListeners) 
                    l.onSuccessfulIncomingCall(this);
                
                break;
            case CALL_DIRECTION_OUTGOING:
                for (Listener l : mListeners) 
                    l.onSuccessfulOutgoingCall(this,
                            getStateFromConnectionState(connection.getState()));
                
                break;
            case CALL_DIRECTION_UNKNOWN:
                for (Listener l : mListeners) 
                    l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection
                            .getState()));
                
                break;
        
    

接着  CallsManager 的 onSuccessfulUnknownCall() 到 addCall()

    public void addCall(Call call) 
        Trace.beginSection("addCall");
        Log.d(this, "addCall(%s)", call);
        call.addListener(this);
        mCalls.add(call);

        // Specifies the time telecom finished routing the call. This is used by the dialer for
        // analytics.
        Bundle extras = call.getIntentExtras();
        extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS,
                SystemClock.elapsedRealtime());

        updateCanAddCall();
        // onCallAdded for calls which immediately take the foreground (like the first call).
        for (CallsManagerListener listener : mListeners) 
            if (LogUtils.SYSTRACE_DEBUG) 
                Trace.beginSection(listener.getClass().toString() + " addCall");
            
            listener.onCallAdded(call);
            if (LogUtils.SYSTRACE_DEBUG) 
                Trace.endSection();
            
        
        Trace.endSection();
    
InCallController 这个对象是连接类
    @Override
    public void onCallAdded(Call call) 
        if (!isBoundAndConnectedToServices()) 
            Log.i(this, "onCallAdded: %s; not bound or connected.", call);
            // We are not bound, or we're not connected.
            bindToServices(call);
         else 
            // We are bound, and we are connected.
            adjustServiceBindingsForEmergency();

            // This is in case an emergency call is added while there is an existing call.
            mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call,
                    mCallsManager.getCurrentUserHandle());

            Log.i(this, "onCallAdded: %s", call);
            // Track the call if we don't already know about it.
            addCall(call);

            Log.i(this, "mInCallServiceConnection isConnected=%b",
                    mInCallServiceConnection.isConnected());

            List<ComponentName> componentsUpdated = new ArrayList<>();
            for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) 
                InCallServiceInfo info = entry.getKey();

                if (call.isExternalCall() && !info.isExternalCallsSupported()) 
                    continue;
                

                if (call.isSelfManaged() && !info.isSelfManagedCallsSupported()) 
                    continue;
                

                // Only send the RTT call if it's a UI in-call service
                boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());

                componentsUpdated.add(info.getComponentName());
                IInCallService inCallService = entry.getValue();

                ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
                        true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),
                        info.isExternalCallsSupported(), includeRttCall,
                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI);
                try 
                    inCallService.addCall(parcelableCall);
                 catch (RemoteException ignored) 
                
            
            Log.i(this, "Call added to components: %s", componentsUpdated);
        
    

其中 bindToServices

    public void bindToServices(Call call) 
        if (mInCallServiceConnection == null) 
            InCallServiceConnection dialerInCall = null;
            InCallServiceInfo defaultDialerComponentInfo = getDefaultDialerComponent();
            Log.i(this, "defaultDialer: " + defaultDialerComponentInfo);
            if (defaultDialerComponentInfo != null &&
                    !defaultDialerComponentInfo.getComponentName().equals(
                            mSystemInCallComponentName)) 
                dialerInCall = new InCallServiceBindingConnection(defaultDialerComponentInfo);
            
            Log.i(this, "defaultDialer: " + dialerInCall);

            InCallServiceInfo systemInCallInfo = getInCallServiceComponent(
                    mSystemInCallComponentName, IN_CALL_SERVICE_TYPE_SYSTEM_UI);
            EmergencyInCallServiceConnection systemInCall =
                    new EmergencyInCallServiceConnection(systemInCallInfo, dialerInCall);
            systemInCall.setHasEmergency(mCallsManager.hasEmergencyCall());

            InCallServiceConnection carModeInCall = null;
            InCallServiceInfo carModeComponentInfo = getCarModeComponent();
            if (carModeComponentInfo != null &&
                    !carModeComponentInfo.getComponentName().equals(mSystemInCallComponentName)) 
                carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);
            

            mInCallServiceConnection =
                    new CarSwappingInCallServiceConnection(systemInCall, carModeInCall);
        

        mInCallServiceConnection.setCarMode(shouldUseCarModeUI());

        // Actually try binding to the UI InCallService.  If the response
        if (mInCallServiceConnection.connect(call) ==
                InCallServiceConnection.CONNECTION_SUCCEEDED) 
            // Only connect to the non-ui InCallServices if we actually connected to the main UI
            // one.
            connectToNonUiInCallServices(call);
            mBindingFuture = new CompletableFuture<Boolean>().completeOnTimeout(false,
                    mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(
                            mContext.getContentResolver()),
                    TimeUnit.MILLISECONDS);
         else 
            Log.i(this, "bindToServices: current UI doesn't support call; not binding.");
        
    

InCallController
  // 这个方法会绑定默认的电话应用
    public void bindToServices(Call call) 
        if (mInCallServiceConnection == null) 
            InCallServiceConnection dialerInCall = null;
            InCallServiceInfo defaultDialerComponentInfo = getDefaultDialerComponent();
            Log.i(this, "defaultDialer: " + defaultDialerComponentInfo);
            if (defaultDialerComponentInfo != null &&
                    !defaultDialerComponentInfo.getComponentName().equals(
                            mSystemInCallComponentName)) 
                dialerInCall = new InCallServiceBindingConnection(defaultDialerComponentInfo);
            
            Log.i(this, "defaultDialer: " + dialerInCall);

            InCallServiceInfo systemInCallInfo = getInCallServiceComponent(
                    mSystemInCallComponentName, IN_CALL_SERVICE_TYPE_SYSTEM_UI);
            EmergencyInCallServiceConnection systemInCall =
                    new EmergencyInCallServiceConnection(systemInCallInfo, dialerInCall);
            systemInCall.setHasEmergency(mCallsManager.hasEmergencyCall());

            InCallServiceConnection carModeInCall = null;
            InCallServiceInfo carModeComponentInfo = getCarModeComponent();
            if (carModeComponentInfo != null &&
                    !carModeComponentInfo.getComponentName().equals(mSystemInCallComponentName)) 
                carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);
            

            mInCallServiceConnection =
                    new CarSwappingInCallServiceConnection(systemInCall, carModeInCall);
        

        mInCallServiceConnection.setCarMode(shouldUseCarModeUI());

        // Actually try binding to the UI InCallService.  If the response
        if (mInCallServiceConnection.connect(call) ==
                InCallServiceConnection.CONNECTION_SUCCEEDED) 
            // Only connect to the non-ui InCallServices if we actually connected to the main UI
            // one.
            connectToNonUiInCallServices(call);
            mBindingFuture = new CompletableFuture<Boolean>().completeOnTimeout(false,
                    mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(
                            mContext.getContentResolver()),
                    TimeUnit.MILLISECONDS);
         else 
            Log.i(this, "bindToServices: current UI doesn't support call; not binding.");
        
    

走到这里看到有个defaultDialerApp 这个就是默认的电话应用

以上是关于车机蓝牙通话流程分析的流程分析的主要内容,如果未能解决你的问题,请参考以下文章

车机蓝牙通话流程分析的流程分析

Android博通BCM libbt-vendor.so 分析蓝牙初始化流程

Android博通BCM libbt-vendor.so 分析蓝牙初始化流程

蓝牙speaker配对流程源码分析

蓝牙Remove Bond的流程分析

蓝牙通话机制原理