蓝牙之六-A2dp代码调用流程_getbluetootha2dp_飞_哥的博客-程序员宅基地

技术标签: android  蓝牙  

这里写图片描述 
上图描述的是蓝牙协议栈,通过该图,查看A2dp的代码在协议栈的调用流程。其分层架构如下: 
这里写图片描述 
1.蓝牙的系统服务service通过JNI与bluedroid协议栈进行通信。协议栈分为两层,Bluetooth Embedded System(BTE)和Bluetooth Application Layer(BTA)。这两层和framework层应用进行通信。 
2.蓝牙服务通过Binder IPC通信与应用程序交互。 
3.系统服务给开发者提供了获取各种profile的接口。

Application framework

该层使用android.bluetooth APIs和Bluetooth hardware进行通信。通过Binder IPC机制和Bluetooth进程进行通信。该层代码位于frameworks/base/core/java/android/bluetooth/目录下。 
如A2DP连接frameworks/base/core/java/android/bluetooth/BluetoothA2dp.java

    public boolean connect(BluetoothDevice device) {
        if (DBG) log("connect(" + device + ")");
        if (mService != null && isEnabled() &&
            isValidDevice(device)) {
            try {
                return mService.connect(device);
            } catch (RemoteException e) {
                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                return false;
            }
        }
        if (mService == null) Log.w(TAG, "Proxy not attached to service");
        return false;
    }

 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

该方法通过Binder IPC通信机制,调用packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java下的内部私有类。通过aidl机制实现。

<packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java>
private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub {
    
        public boolean connect(BluetoothDevice device) {
            A2dpService service = getService();
            if (service == null) return false;
            return service.connect(device);
        }
}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

然后调用到A2dpService的connect的方法。

<packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java>
public class A2dpService extends ProfileService {
    
        ...
        public boolean connect(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                       "Need BLUETOOTH ADMIN permission");
        if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {
            return false;
        }
        ParcelUuid[] featureUuids = device.getUuids();
        if ((BluetoothUuid.containsAnyUuid(featureUuids, A2DP_SOURCE_UUID)) &&
            !(BluetoothUuid.containsAllUuids(featureUuids ,A2DP_SOURCE_SINK_UUIDS))) {
            Log.e(TAG,"Remote does not have A2dp Sink UUID");
            return false;
         }
        int connectionState = mStateMachine.getConnectionState(device);
        if (connectionState == BluetoothProfile.STATE_CONNECTED ||
            connectionState == BluetoothProfile.STATE_CONNECTING) {
            return false;
        }
        mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device);
        return true;
    }
    ....
        private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub {
    
            public boolean connect(BluetoothDevice device) {
                A2dpService service = getService();
                if (service == null) return false;
                return service.connect(device);
           }
       }
  }
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

上述过程就是Bluetooth application framework与Bluetooth process的调用过程。

Bluetooth System service

这部分代码位于packages/apps/Bluetooth下,在framework层实现蓝牙服务和各种profile,以apps形式存在。该层通过JNI调用HAL层代码。 
A2dpService的connect方法会发送状态机改变消息, 
mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device); 
这个消息会被A2dpStateMachine对象的processMessage(Message)方法接收:

            switch(message.what) {
                case CONNECT:
                    BluetoothDevice device = (BluetoothDevice) message.obj;
                    broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
                                   BluetoothProfile.STATE_DISCONNECTED);

                    if (!connectA2dpNative(getByteAddress(device)) ) {
                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
                                       BluetoothProfile.STATE_CONNECTING);
                        break;
                    }

                    synchronized (A2dpStateMachine.this) {
                        mTargetDevice = device;
                        transitionTo(mPending);
                    }
                    // TODO(BT) remove CONNECT_TIMEOUT when the stack
                    //          sends back events consistently
                    sendMessageDelayed(CONNECT_TIMEOUT, 30000);
                    break;
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

上述的connectA2dpNative(getByteAddress(device))会通过JNI调用Native方法。

<packages/apps/Bluetooth/jni/com_android_bluetooth_a2dp.cpp>
static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
    jbyte *addr;
    bt_bdaddr_t * btAddr;
    bt_status_t status;

    ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface);
    if (!sBluetoothA2dpInterface) return JNI_FALSE;

    addr = env->GetByteArrayElements(address, NULL);
    btAddr = (bt_bdaddr_t *) addr;
    if (!addr) {
        jniThrowIOException(env, EINVAL);
        return JNI_FALSE;
    }

    if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
        ALOGE("Failed HF connection, status: %d", status);
    }
    env->ReleaseByteArrayElements(address, addr, 0);
    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

JNI

和JNI相关的代码在package/apps/Bluetooth/jni目录下,上一节的connectA2dpNative方法就是这个目录的代码。JNI层代码调用到HAL代码并且从HAL接收到回调。 
A2dp连接中,会调用sBluetoothA2dpInterface->connect实现。

<packages/apps/Bluetooth/jni/com_android_bluetooth_a2dp.cpp>
static void initNative(JNIEnv *env, jobject object) {
    const bt_interface_t* btInf;
    bt_status_t status;
...
    if ( (sBluetoothA2dpInterface = (btav_interface_t *)
          btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID)) == NULL) {
        ALOGE("Failed to get Bluetooth A2DP Interface");
        return;
    }

    if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {
        ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status);
        sBluetoothA2dpInterface = NULL;
        return;
    }

    mCallbacksObj = env->NewGlobalRef(object);
}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在initNative中获得sBluetoothA2dpInterface对象。

HAL

硬件抽象层定义了android.bluetooth APIs和Bluetooth process调用的接口。Bluetooth 的HAL层代码在: 
hardware/libhardware/include/hardware/bluetooth.h,HAL层的以下代码也要重视:

hardware/libhardware/include/hardware/bt_common_types.h  
//A2DP profile定义
hardware/libhardware/include/hardware/bt_av.h   
//GATT profile 定义         
hardware/libhardware/include/hardware/bt_gatt_types.h  
hardware/libhardware/include/hardware/bt_gatt_client.h 
hardware/libhardware/include/hardware/bt_gatt_server.h 
hardware/libhardware/include/hardware/bt_gatt.h  
//HFP profile定义
hardware/libhardware/include/hardware/bt_hf.h  
//HDP profile 定义
hardware/libhardware/include/hardware/bt_hl.h 
//MAP profile
hardware/libhardware/include/hardware/bt_mce.h
//HFP client profile
hardware/libhardware/include/hardware/bt_hf_client.h 
//pan profile  
hardware/libhardware/include/hardware/bt_pan.h
//AVRCP profile     
hardware/libhardware/include/hardware/bt_rc.h     
hardware/libhardware/include/hardware/bt_hh.h 
//SDP profile         
hardware/libhardware/include/hardware/bt_sdp.h
//RFCOMM sockets         
hardware/libhardware/include/hardware/bt_sock.h
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

JNI中的sBluetoothA2dpInterface是btav_interface_t结构体,位于hardware/libhardware/include/hardware/bt_av.h中,定义为:

typedef struct {
    /** set to sizeof(btav_interface_t) */
    size_t          size;
    /**
     * Register the BtAv callbacks
     */
    bt_status_t (*init)( btav_callbacks_t* callbacks );

    /** connect to headset */
    bt_status_t (*connect)( bt_bdaddr_t *bd_addr );

    /** dis-connect from headset */
    bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr );

    /** Closes the interface. */
    void  (*cleanup)( void );
} btav_interface_t;
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

BT stack

协议栈在/system/bt下,实现了HAL层和扩展配置等。包括BTA和BTE量大组件,BTA实现了蓝牙设备管理、状态管理及一些应用规范。而BTE则通过HCI与厂商蓝牙芯片交互以实现了蓝牙协议栈的通用功能和相关协议。另外,BTE还包括一个统一内核接口(GKI),蓝牙芯片厂商可借助GKI快速轻松得移植蓝牙协议栈到其他操作系统或手机平台上。 
A2dp的连接将调用

<system/bt/btif/src/btif_av.c>
static bt_status_t connect_int(bt_bdaddr_t *bd_addr, uint16_t uuid)
{
    btif_av_connect_req_t connect_req;
    connect_req.target_bda = bd_addr;
    connect_req.uuid = uuid;
    BTIF_TRACE_EVENT("%s", __FUNCTION__);

    btif_sm_dispatch(btif_av_cb.sm_handle, BTIF_AV_CONNECT_REQ_EVT, (char*)&connect_req);

    return BT_STATUS_SUCCESS;
}
 
 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Vendor extensions

增加自定义扩展或者HCI层追踪,可以创建libbt-vendor模块并指定这些组件。


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/pashanhu6402/article/details/72905626

智能推荐

美团,大众下拉菜单的实现_山寨戴王的博客-程序员宅基地

最近项目中用到了类似美团,大众的下拉菜单。于是上网找资料,下了个demo,两个listview实现的,感觉不是很好,后面又找到了ExpandTab这个自定义控件,封装了两个listview,用起来确实方便了很多,只是数据绑定太麻烦,于是重新搜索,最后整合了一下,产生了下面的这个demo。用起来绝对简单方便,目前listview的item就是一个textView,需要用的只需自己改一下item的

一入侯门“深”似海,深度学习深几许(入门系列之一)_宁延安的博客-程序员宅基地

摘要: 当你和女朋友在路边手拉手一起约会的时候,你可曾想,你们之间早已碰撞出了一种神秘的智慧–深度学习。恋爱容易,相处不易,不断磨合,打造你们的默契,最终才能决定你们是否在一起。深度学习也一样,输入各种不同的参数,进行训练拟合,最后输出拟合结果。 恋爱又不易,且学且珍惜! 【导言】目前人工智能非常火爆,而深度学习则是引领这一火爆现场的“火箭”。于是,有关“深度学习”的论文、书籍和网络博客..._一入侯门深似海,深度

学习Android最适合的方式(官方Sample Code)_android code sample_默尛铭的博客-程序员宅基地

导读过去我们会在各大社区,博客或者GitHub找一些大神封装好各种类库,但是我们忽略了一个真正的大牛,就是Google它老人家,下面内容将给同学们介绍这块内容..SDK 自带的API Demos从模拟器中我们可以看到以下以下的截图正是在SDK/samples/android-xx 下的官方API,我们只需要将源码运行即可得到我们所需的Api Demo效果图:将API Demos 运行的几种方式(An_android code sample

mysql 安全登陆_MySQL怎样做到安全登陆-程序员宅基地

首先MySQL的密码权限存储在mysql.user表中。我们不关注鉴权的部分,我们只关心身份认证,识别身份,后面的权限控制是很简单的事情。在mysql.user表中有个authentication_string字段,存储的是密码的两次sha1值。你可以用下面的语句,验证和mysql.user表中存储的是一致的。select sha1(UNHEX(sha1(‘password’)))以上就是服务端关..._msyql 安全登录

百度CES发布Apollo 3.5,全球首个自动驾驶物流解决方案,助力沃尔玛无人快递_conglu1891的博客-程序员宅基地

大数据文摘出品记者:蒋宝尚当地时间1月8日,百度世界大会美国场在拉斯维加斯的第52届国际消费类电子产品展览会(CES)召开,百度Apollo平台研发负责人王京傲发布最新版本Apollo3.5,并称其“跨越历史新阶段”,实现了支持..._无人物流自动驾驶系统

电商平台项目 Vue day1_独立站vue_kin0926的博客-程序员宅基地

尚硅谷的VUE项目实战,尚品汇_独立站vue

随便推点

Android工程师成长之路_我是黄大仙的博客-程序员宅基地

见习工程师 实习生(student engineer) 1、掌握基本的Android应用开发和调试技能,了解Android SDK,会用Eclipse开发工具; 2、掌握基础控件、UI布局,能够处理多分辨率适配; 3、具有较强的团队协作精神及高度的责任心,会使用SVN等协同开发工具; 4、了解面向对象编程思想、了解MVC、了解JSON数据。初级android工程师 菜鸟(junior eng

java配置ehcache_Java ehcache分布式缓存配置示例._Brandon Lu的博客-程序员宅基地

maxElementsInMemory =“ 1000”eternal =“ false”timeToIdleSeconds =“ 120”timeToLiveSeconds =“ 120”overflowToDisk =“ true”/>ehcache>通过这种方式,配置了192.168.10.114:40001和192.168.10.114:40002的两个ehcache缓存实例,..._ehcache配置java类

固态U盘量产:群联PS3111主控开卡量产工具使用教程_群联ps3111开卡工具_振兴乡村有你有我的博客-程序员宅基地

在选择完需要的操作和U盘之后,点击“启动”按钮,开始进行量产操作。PS3111开卡量产工具是一款专门用来进行量产的软件工具,下面将为大家提供使用教程,以帮助大家更加顺利地进行U盘量产。在主界面中,有“操作列表”、“扫描设备”、“工具选项”等选项,分别对应着不同的操作。该选项中包含了U盘的各种操作,例如清空U盘、格式化U盘、重置U盘等,根据需要进行选择即可。该选项用来扫描电脑系统中连接的U盘,会在列表中显示出已连接的U盘,选择需要操作的U盘即可。该选项中包含了一些高级设置操作,针对于专业用户进行设置。_群联ps3111开卡工具

Could not commit JPA transaction; nested exception is javax.persistence.RollbackException 解决方法..._weixin_34269583的博客-程序员宅基地

Could not commit JPA transaction; nested exception is javax.persistence.RollbackException 解决方法 Could not commit JPA transaction; nested exception is javax.persistence.Rollba...

自学编程需要注意什么?_自学编程要注意哪些坑_宇翔XGT的博客-程序员宅基地

来源|StormZhang公众号(ID:googdev)编辑|Judy前几天在知乎上看到这么一个问题,一位在读学生,自己非常想自学编程,但是很怕走一些弯路,于是提问"自学编程需要注意什么?",我看了一圈回答,看起来都不是自学过来的,很多回答抓不到重点。我的读者都知道,我是非科班 0 基础自学过来的,我很清楚一个人自学编程有哪些误区,有哪些需要注意的地方,以及哪些可以提升效率的地方,_自学编程要注意哪些坑

推荐文章

热门文章

相关标签