Android-蓝牙sco通话

  • Post author:
  • Post category:其他


APP调用AudioManager::startBluetoothSco()

// frameworks/base/media/java/android/media/AudioManager.java
public void startBluetoothSco(){
    service.startBluetoothSco(mICallBack,
                    getContext().getApplicationInfo().targetSdkVersion);
}

// frameworks/base/services/core/java/com/android/server/audio/AudioService.java
public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
    final int scoAudioMode =
                (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
                 BtHelper.SCO_MODE_VIRTUAL_CALL : BtHelper.SCO_MODE_UNDEFINED;
    final String eventSource = new StringBuilder("startBluetoothSco()")
                .append(") from u/pid:").append(uid).append("/")
                .append(pid).toString();
    startBluetoothScoInt(cb, uid, pid, scoAudioMode, eventSource);
}

void startBluetoothScoInt(IBinder cb, int uid, int pid, int scoAudioMode, @NonNull String eventSource) {
        final long ident = Binder.clearCallingIdentity();
        mDeviceBroker.startBluetoothScoForClient(cb, uid, pid, scoAudioMode, eventSource);
        Binder.restoreCallingIdentity(ident);
        mmi.record();
}

// frameworks/base/services/core/java/com/android/server/audio/AudioDeviceBroker.java
void startBluetoothScoForClient(IBinder cb, int uid, int pid, int scoAudioMode, @NonNull String eventSource) {
    AudioDeviceAttributes device = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
    setCommunicationRouteForClient(cb, uid, pid, device, scoAudioMode, eventSource);
}

void setCommunicationRouteForClient(IBinder cb, int uid, int pid, AudioDeviceAttributes device, int scoAudioMode, String eventSource) {
    ......
    mBtHelper.startBluetoothSco(scoAudioMode, eventSource);
    ......
}

// frameworks/base/services/core/java/com/android/server/audio/BtHelper.java
synchronized boolean startBluetoothSco(int scoAudioMode,  @NonNull String eventSource) {
    AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
    return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
}

private boolean requestScoState(int state, int scoAudioMode) {
    if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
        connectBluetoothScoAudioHelper(mBluetoothHeadset, mBluetoothHeadsetDevice, mScoAudioMode)
        ......
    } 
    // 断开连接的时候发送断连的广播
}

这里做了两件事,一个是发送广播,一个是调用connectBluetoothScoAudioHelper

先看广播发送

private void broadcastScoConnectionState(int state) {
    mDeviceBroker.postBroadcastScoConnectionState(state);
}

// frameworks/base/services/core/java/com/android/server/audio/AudioDeviceBroker.java
void postBroadcastScoConnectionState(int state) {
   sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
}

// 收到消息MSG_I_BROADCAST_BT_CONNECTION_STATE之后调用onBroadcastScoConnectionState

// frameworks/base/services/core/java/com/android/server/audio/BtHelper.java
synchronized void onBroadcastScoConnectionState(int state) {
   Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
   newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
   newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
                mScoConnectionState);
   sendStickyBroadcastToAll(newIntent);
   mScoConnectionState = state;
}

private void sendStickyBroadcastToAll(Intent intent) {
    mDeviceBroker.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
// 最终这个广播发出去就被一个app接收去显示蓝牙俩呢及状态了。

再看connectBluetoothScoAudioHelper

// frameworks/base/services/core/java/com/android/server/audio/BtHelper.java
 private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode) {
        switch (scoAudioMode) {
            case SCO_MODE_RAW:
                return bluetoothHeadset.connectAudio();
            case SCO_MODE_VIRTUAL_CALL:
                return bluetoothHeadset.startScoUsingVirtualVoiceCall();
            case SCO_MODE_VR:
                return bluetoothHeadset.startVoiceRecognition(device);
            default:
                return false;
        }
}

Audio调用蓝牙的connectAudio

// 这里也是各种client server交互,最终调用如下
// packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java
boolean connectAudio(BluetoothDevice device) {
    final HeadsetStateMachine stateMachine = mStateMachines.get(device);
    stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device);
    return true;
}

// packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
public boolean processMessage(Message message) {
    case CONNECT_AUDIO:
        mSystemInterface.getAudioManager().setParameters("A2dpSuspended=true"); // 先把A2dpSuspended设置成true
        broadcastAudioState(mDevice, BluetoothHeadset.STATE_AUDIO_DISCONNECTED, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
        transitionTo(mAudioConnecting);
}

/**
 * A Bluetooth Handset StateMachine
 *                        (Disconnected)
 *                           |      ^
 *                   CONNECT |      | DISCONNECTED
 *                           V      |
 *                  (Connecting)   (Disconnecting)
 *                           |      ^
 *                 CONNECTED |      | DISCONNECT
 *                           V      |
 *                          (Connected)
 *                           |      ^
 *             CONNECT_AUDIO |      | AUDIO_DISCONNECTED
 *                           V      |
 *             (AudioConnecting)   (AudioDiconnecting)
 *                           |      ^
 *           AUDIO_CONNECTED |      | DISCONNECT_AUDIO
 *                           V      |
 *                           (AudioOn)
 */

// 接下来蓝牙就回进入audioon状态,进入audioon状态就会调用audio的setBluetoothScoOn
(蓝牙app默认走高通的逻辑)
class AudioOn extends ConnectedBase {
     public void enter() {
         ......
         mSystemInterface.getAudioManager().setBluetoothScoOn(true);
         ......
     }
}

蓝牙进入audioon之后调用audio setBluetoothScoOn

这里需要注意,在蓝牙进入audio on 之前就已经先把a2dpsuspended设置成了true,先让a2dp停止,然后再进入audio on,然后再设置audio的setBluetoothScoOn。

接来下看看setBluetoothScoOn的处理:

蓝牙进入audioon模式就会调用mSystemInterface.getAudioManager().setBluetoothScoOn(true);
// frameworks/base/services/core/java/com/android/server/audio/AudioDeviceBroker.java
void setBluetoothScoOn(boolean on, String eventSource) {
    synchronized (mDeviceStateLock) {
        mBluetoothScoOn = on; // 这个标记在选择device的时候使用,如果是on就会选择sco设备
        sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource); // 这里就是更新route,选择设备
     }
 }

// 收到MSG_L_UPDATE_COMMUNICATION_ROUTE信息之后就会调用onUpdateCommunicationRoute
private void onUpdateCommunicationRoute(String eventSource) {
    AudioDeviceAttributes preferredCommunicationDevice = preferredCommunicationDevice(); // 这里选择设备
    if (preferredCommunicationDevice == null || preferredCommunicationDevice.getType() != AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
        AudioSystem.setParameters("BT_SCO=off");
    } else {
        AudioSystem.setParameters("BT_SCO=on");
    }
    postSetPreferredDevicesForStrategy(mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
}

private AudioDeviceAttributes preferredCommunicationDevice() {
    boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn();
    if (btSCoOn) {
        AudioDeviceAttributes device = mBtHelper.getHeadsetAudioDevice();
        return device;
    }
    AudioDeviceAttributes device = requestedCommunicationDevice();
    return device;
}

setBluetoothScoOn的作用就是: 1. 选择到sco device 2. 设置参数”BT_SCO=on”

Audio setparameter “A2dpSuspended=true” & “BT_SCO=on”

从目前的调用逻辑来看,在蓝牙app进入audioon状态之前就设置了A2dpSuspended=true,在蓝牙进入audioon之后告诉audio sco on 的时候audio侧设置了BT_SCO=on,所以这两个的先后顺序就是先设置”A2dpSuspended=true”再设置”BT_SCO=on”。

先看看设置”A2dpSuspended=true”

// vendor/qcom/opensource/audio-hal/primary-hal/hal/AudioDevice.cpp
int AudioDevice::SetParameters(const char *kvpairs) {
     ret = str_parms_get_str(parms, "A2dpSuspended" , value, sizeof(value));
     if (ret >= 0) {
         pal_param_bta2dp_t param_bt_a2dp;

         if (strncmp(value, "true", 4) == 0)
             param_bt_a2dp.a2dp_suspended = true;
         else
             param_bt_a2dp.a2dp_suspended = false;

         ret = pal_set_param(PAL_PARAM_ID_BT_A2DP_SUSPENDED /* PAL_PARAM_ID_BT_A2DP_SUSPENDED = 16*/, (void *)&param_bt_a2dp, sizeof(pal_param_bta2dp_t));
     }
}

pal_set_param 调用ResourceManager::setParameter -----> a2dp_dev->setDeviceParameter ---> rm->a2dpSuspend(); // 当suspend是true就调用rm->a2dpSuspend(), 是false就是rm->a2dpResume()

设置”BT_SCO=on”

// vendor/qcom/opensource/audio-hal/primary-hal/hal/AudioDevice.cpp
int AudioDevice::SetParameters(const char *kvpairs) {
    ret = str_parms_get_str(parms, "BT_SCO", value, sizeof(value));
    if (ret >= 0) {
        pal_param_btsco_t param_bt_sco;
        if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) {
            param_bt_sco.bt_sco_on = true;
        } else {
            param_bt_sco.bt_sco_on = false;
        }
        ret = pal_set_param(PAL_PARAM_ID_BT_SCO/*PAL_PARAM_ID_BT_SCO = 11*/, (void *)&param_bt_sco, sizeof(pal_param_btsco_t));
    }
}

pal_set_param 调用ResourceManager::setParameter
// vendor/qcom/opensource/pal/resource_manager/src/ResourceManager.cpp
int ResourceManager::setParameter(uint32_t param_id, void *param_payload, size_t payload_size) {
    case PAL_PARAM_ID_BT_SCO: {
        struct pal_device dattr;
        dattr.id = PAL_DEVICE_OUT_BLUETOOTH_SCO;
        dev = Device::getInstance(&dattr, rm); // 获取sco device
        
        param_bt_sco = (pal_param_btsco_t*)param_payload;
        status = dev->setDeviceParameter(param_id, param_payload);
        // 下面就是一些设备切换的逻辑
        std::vector <std::shared_ptr<Device>> rxDevices;
        std::vector <std::shared_ptr<Device>> txDevices;
        if (param_bt_sco->bt_sco_on == true) {
            for (auto& str : mActiveStreams) { // 循环处理所有stream
                 str->getStreamAttributes(&sAttr);
                 // 根据sAttr的type判断是否是rx且是需要切换到sco的stream如果是
                 if(上面的条件满足) {
                     str->getAssociatedDevices(associatedDevices);
                     for (int i = 0; i < associatedDevices.size(); i++) {
                         dAttr.id = (pal_device_id_t)associatedDevices[i]->getSndDeviceId();
                         dev = Device::getInstance(&dAttr, rm);
                         if (dev && (!isBtScoDevice(dAttr.id)) && (dAttr.id != PAL_DEVICE_OUT_PROXY) && isDeviceAvailable(PAL_DEVICE_OUT_BLUETOOTH_SCO)) {
                             rxDevices.push_back(dev); // 把device放入需要切换的列表中
7823                     }
                     }
                 } else if(如果是tx,且是需要切换到sco的stream) {
                     同上的方法,把需要切换到device放入txDevices列表中
                 }    
            }
        }
        
        sco_rx_dattr.id = PAL_DEVICE_OUT_BLUETOOTH_SCO;
        status = getDeviceConfig(&sco_rx_dattr, NULL);
        
        sco_tx_dattr.id = PAL_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
        status = getDeviceConfig(&sco_tx_dattr, NULL);

        SortAndUnique(rxDevices);
        SortAndUnique(txDevices);
        for (auto& device : rxDevices) {
            rm->forceDeviceSwitch(device, &sco_rx_dattr);
        }
        for (auto& device : txDevices) {
            rm->forceDeviceSwitch(device, &sco_tx_dattr);
        }
    }
}



版权声明:本文为cheriyou_原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。