Android-蓝牙sco通话

2023-05-16

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);
        }
    }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android-蓝牙sco通话 的相关文章

随机推荐

  • 虚拟帧缓冲驱动

    17 2 2 虚拟帧缓冲驱动 嵌入式Linux系统开发全程解析 是一本全面介绍嵌入式Linux开发的专著 xff0c 书中涵盖了程序生成工具 调试工具 引导加载器 Linux系统结构 Linux内核 驱动程序 用户空间编程 用户空间中间件等
  • 神经网络每次输出不一样,神经网络输出值相同

    BP神经网络最后得出的误差很大 1 看看是不是训练效果好 xff0c 预测效果不好 如果是这样那就是过拟合 网上搜搜有很多解决过拟合的方法 2 如果训练和预测都不好 xff0c 那就是模型有问题 可能原因是 xff08 1 xff09 数据
  • 深度神经网络应用实例

    深度神经网络目前有哪些成功的应用 深度学习最成功的应用是在音视频的识别上 xff0c 几乎所有的商用语音识别都是深度学习来完成的 其次深度学习应用最成功的领域就是图像识别 xff0c 目前识别准确率已经超越人类 深度学习成了图像识别的标配
  • 研究pixhawk的makefile的结构(-)

    首先研究 xff50 xff49 xff58 xff48 xff41 xff57 xff4b 的 xff4d xff41 xff4b xff45 xff46 xff49 xff4c xff45 的结构是怎样的 方便以后开发测试 xff11
  • 制作ROS移动机器人地盘

    制作ROS移动机器人地盘 摘要概述硬件需求车体设计电路设计程序设计PID控制轮速 摘要 本教程讲述如何利用扫地机轮子制作ROS移动机器人地盘 概述 原本不打算自己造轮子的 xff0c 但是网上的移动机器人地盘要么巨贵 对于学生党 xff0c
  • Lumia520刷安卓教程

    Date 2017 09 02 Author SuperDeveloper Descreption install android on luima 520 devices Email na1206 64 live com 警告 本教程只适
  • openwrt编译及第一个安装包教程

    Date 2017 03 14 Made SuperDeverloper Email na1206 64 live com Target For mt7688 based board 说明 xff1a 本人在学习过程中走了不少弯路 xff0
  • turtlebot3 Slam+nvigation仿真 ROS-lunar

    Date 2017 09 06 Author SuperDeveloper Description Slam simulation 说明 xff1a 1 Slam 初学笔记 xff0c 搭建slam仿真环境 xff1b 2 文章里的连接可能
  • 基于NVIDIA Xavier NX(ubuntu20.04)的Optitrack视觉定位 PX4+ros noetic(实物运行记录)

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 前言一 xff1a 硬件准备两种界面化显示的方式无线连接有线连接 二 xff1a 软件准备1 远程登录软件 NoMachine2
  • ros机器人搭建总纲

    author xff1a superDeveloper date 2017 11 29 type note 近期准备搭建一个ROS机器人平台 xff0c 建立此博客记录搭建过程以及相关问题的解决办法 xff0c 作为笔记 xff0c 亦供相
  • ros gmapping 运行错误:Assertion 'beams<LASER_MAXBEAMS' failed>

    在使用真实激光器发布数据的时候 xff0c 出现了 Laser is mounted upward警告 xff0c 以及slam gmapping tmp buildd ros hydro openslam gmapping 0 1 0 2
  • realloc():invalid next size....错误

    Author SuperDeveloper Date 2018 1 2 在程序中使用了realloc函数 xff0c 更改结构体数组的大小 xff0c 错误代码如下 xff1a struct point span class hljs su
  • git简单命令笔记

    这是一篇关于git的使用笔记 xff0c 刚刚开始使用git 1 创建git本地仓库 xff1a 在你需要版本控制的项目Project根目录下右键点击Git Bash here执行git init 然后在该目录下生成 一个 git的隐藏文件
  • 源码编译Boost库的正确姿态

    源码编译Boost库的正确姿态 写在前面step 1 step 2 step 3 step4 写在前面 项目需要编译pcl库到arm平台 xff0c 交叉编译Boost xff0c Eigen3 Flann 之后再编译pcl库的时候总是报错
  • 第一讲、四旋翼的整体控制方案

    各位朋友 xff0c 我们工作室以后会长期更新一些飞行器干货 xff0c 本部分先介绍四旋翼的整体控制方案及相关设计 控制系统的框架如下 xff0c 借鉴网上来源图片 xff0c 传感器主要是姿态传感器 xff0c 对于大四轴而言 xff0
  • 网页中屏蔽鼠标右键、Ctrl+N、Shift+F10

    lt script language 61 34 Javascript 34 gt 屏蔽鼠标右键 Ctrl 43 N Shift 43 F10 F5刷新 退格键 屏蔽F1帮助 function window onhelp return fa
  • asp.net上一页下一页的部分代码

    lt asp linkbutton id 61 34 btnFirst 34 nclick 61 34 PagerButtonClick 34 runat 61 34 server 34 CommandArgument 61 34 0 34
  • Visual C# 编程操作Excel

    Visual C 编程操作Excel 2004 08 20 作者 xff1a 邵回祖 出处 xff1a ahcit http www yesky com SoftChannel 72342380468109312 20040819 1844
  • C#保存图片到IMAGE字段

    byte FileByteArray System IO MemoryStream ImageStream this sqlConnection1 ConnectionString 61 strConn try if this sqlCon
  • Android-蓝牙sco通话

    APP调用AudioManager startBluetoothSco frameworks base media java android media AudioManager java public void startBluetoot