文章开始之前,先看两张图

原谅我这个不擅长美工没有艺术细菌的程序媛,图画的很简陋,但也算是勾勒出了大致的流程如果你对这两张图感兴趣,那么欢迎继续阅读本文,也期待多多提出意见如果无感,甚至感觉目前根本没有耐心看下去,那我建议你此时可以放弃阅读本文的想法。每个人的时间都是很宝贵的,没有必要逼着自己,完成这项违背意愿的任务你看或不看,文章就在这里...只有在心情愉悦时,才能创造出令人愉悦的代码接下来的任务,就是和我一起对这两张图进行分析上文分析了enable的上层的处理蓝牙,信息获取和enable状态切换,本文分析协议栈的处理分析过程中关于前面两个文章中分析过的代码,就不再进行分析通过两篇文章的分析

  • 蓝牙,从系统开机说起

  • 蓝牙,信息获取和enable状态切换

可以确定BluetoothManagerService是蓝牙系统级别的服务,运行在system进程。并且在开机之后长存,与蓝牙的开关状态无关蓝牙开启的第一个服务是AdapterService,也就是蓝牙的入口。AdapterService是和蓝牙的开关状态保持一致的在开启蓝牙时最先要bind起来的就是该服务。所以直接来看该服务的处理蓝牙应用中有一个Application:AdapterApp.代码很少,一起顺带分析了
在AdapterService创建时静态加载jni的库

1//AdapterApp.java2 System.loadLibrary("bluetooth_jni");

静态初始化native

1//AdapterService.java2 classInitNative();

接下来在服务的生命周期方法onCreate,创建时又再次初始化native,但是调用的方法不一样

1//AdapterService.java2initNative();

打住,到此为止先分析一下,已经做了三件事儿

  1. 加载jni库libbluetooth_jni

  2. 初始化操作classInitNative

  3. 初始化操作initNative

库加载过程属于common的方法就不再分析,重点关注初始化
方法实现都在jni层

首先是classInitNative方法

 1//com_android_bluetooth_btservice_AdapterService.cpp 2static void classInitNative(JNIEnv* env, jclass clazz) { 3... 4jclass jniCallbackClass = 5  env->FindClass("com/android/bluetooth/btservice/JniCallbacks"); 6... 7method_stateChangeCallback = 8  env->GetMethodID(jniCallbackClass, "stateChangeCallback", "(I)V"); 9...10if (hal_util_load_bt_library((bt_interface_t const**)&sBluetoothInterface)) {11  ALOGE("No Bluetooth Library found");12}13...14}

这个方法关注两件事儿首先是把java层的方法映射过来,供jni层调用。映射过来的方法都是JniCallbacks.java中的方法,用来在hal层有消息回调时触发给上层另一个操作就是去加载协议栈的so包,并获取到变量名为bluetoothInterface的地址以后jni层再往下的调用就要依靠这个bluetoothInterface了加载的是哪个so包?拿到的so包的谁的地址?继续分析

1int hal_util_load_bt_library(const bt_interface_t** interface) {2  const char* sym = BLUETOOTH_INTERFACE_STRING;//"bluetoothInterface"3  ...4  property_get(PROPERTY_BT_LIBRARY_NAME, path, DEFAULT_BT_LIBRARY_NAME);5  ...6 (bt_interface_t*)dlsym(handle, sym);7  ...8}

加载的是属性值“ro.bluetooth.library_name”(可以使用adb命令查看属性值)字段所声明的一个地址下的so包拿到是blutoothInterface的地址完事儿了,拿到之后把地址交给sBluetoothInterface,之后jni就可以通过该字段来访问蓝牙所提供的接口了也即是说,classInitNative的作用有两个

  1. 映射java层方法

  2. 获取协议栈的蓝牙接口对象地址

相当于在jni层获取两个对象,一个java层对象,一个是协议栈的so包中的对象获取对象之后,你就可以调用对象的方法了获取到的蓝牙接口对jni提供的方法有哪些?
都在bluetooth.cc文件中列出来了

bluetoothInterface接口方法如上,就不一一分析了

另一个initNative

在获取到对象之后,需要对这个对象进行初始化,配置一些基本信息。
先看一下代码

 1//com_android_bluetooth_btservice_AdapterService.cpp 2static bool initNative(JNIEnv* env, jobject obj) { 3  ... 4  sBluetoothInterface->init(&sBluetoothCallbacks); 5  ... 6  sBluetoothInterface->set_os_callouts(&sBluetoothOsCallouts); 7  ... 8  sBluetoothSocketInterface = 9   (btsock_interface_t*)sBluetoothInterface->get_profile_interface(10       BT_PROFILE_SOCKETS_ID);11   ...12}

主要就是这三件事儿了

  1. init调用蓝牙接口的初始化

  2. set_os_callouts这个主要是对wakelock唤醒锁的管理,目前以功能分析为主,暂时不分析代码设计

  3. 获取到socket的profile对象,有何用?继续分析

拆开分析,去bluetooth.cc文件中查看方法的实现第一个,init干了什么?

 1//system/bt/btif/src/bluetooth.cc 2static int init(bt_callbacks_t* callbacks) { 3  ... 4  bt_hal_cbacks = callbacks; 5  ... 6  stack_manager_get_interface()->init_stack(); 7  ... 8  btif_debug_init(); 9  ...10}

越往下分析你会发现代码越简单,java层要考虑各种功能和交互而hal层代码只需要认真的把功能实现就好了,顺便再把状态回调给上层就ok了所以init中也很简单,首先是把jni层的callback对象拿过来,方便后续进行回调之后,看函数名字应该就是初始化stack了,也就是蓝牙核心模块要初始化了最后是初始化log相关的了(初始化btsnoop)。重点关注stack的初始化,分析一下对stack的处理

 1//system/bt/btif/src/stack_manager.cc 2static void event_init_stack(void* context) { 3... 4   module_management_start(); 5   module_init(get_module(OSI_MODULE)); 6   module_init(get_module(BT_UTILS_MODULE)); 7   module_init(get_module(BTIF_CONFIG_MODULE)); 8   btif_init_bluetooth(); 9...10}

协议栈的初始化好像东西有点儿多啊,还有三个排比句get_module方法其实就是dlsym(RTLD_DEFAULT, name),会在当前进程中按照默认lib包的搜索顺序来搜索name这个symbol module_init方法就是对所传入的module调用init方法

  • 开启核心模块的管理,这个方法目前是空实现

  • OSI_MODULE,初始化osi模块:搜索到的symbol是osi_module.cc文件中的osi_module,也就是要调用osi_module的init方法。暂时不清楚用处,后续研究

  • BT_UTILS_MODULE,初始化bt_utils模块:搜索到的symbol是bt_utils.cc文件中的bt_utils_module,这个init方法大致看了下,确认过眼神,是我不懂的操作。一些赋值操作,看名字和任务栈有关,后续如果分析到再看吧

  • BTIF_CONFIG_MODULE,初始话蓝牙配置模块:搜索到的symbol是btif_config.cc文件中的btif_config_module,init的目的就是去管理前文说到的bt_config相关的文件,比如恢复出厂后需要删除文件重新创建啊,以及文件里边儿现在要写什么啊等等。总之就是管理蓝牙配置文件的一个模块

  • 初始化蓝牙,从这儿开始就会把整个蓝牙协议栈创建起来,为蓝牙的开启做准备工作

各个模块的初始化如上分析,不再深究了接下来看一下bt_init_bluetooth,也就是对蓝牙的初始化

1//system/bt/btif/src/btif_core.cc2bt_status_t btif_init_bluetooth() {3   //Creates BTIF task and prepares BT scheduler for startup4   ...5   bte_main_boot_entry();6   ...7}

btif_init_bluetooth方法会创建蓝牙接口任务栈,并且为蓝牙的开启做准备。主要是调用bte_main_boot_entry方法

 1//system/bt/main/bte_main.cc 2void bte_main_boot_entry(void) { 3  //Entry point for BTE chip/stack initialization 4  ... 5  module_init(get_module(INTEROP_MODULE)); 6  ... 7  hci = hci_layer_get_interface(); 8  ... 9  module_init(get_module(STACK_CONFIG_MODULE));10  ...11}

bte_main_boot_entry方法用于初始化蓝牙芯片和协议栈
初始化的话主要是三个方面

  1. INTEROP_MODULE初始化interop_module模块,init为空实现,不需要考虑

  2. hci,获取到hci层接口,方法实现位于/system/bt/hci/src/hci_layer.cc文件中

  3. STACK_CONFIG_MODULE初始化,会操作/etc/bluetooth/bt_stack.conf文件,文件中是对协议栈的一些配置,比如log是否开启啊之类

这个btif的init完成了到目前位置,上层的initNative的任务已经完成。也就是蓝牙接口中的一些基本模块已经初始化完成,基本上都是一些工具模块和配置模块,也包括把controller和host之间的接口hci给铺好。要依赖的工具和配置都准备好了,小角色都已经暖好场了,大明星们该压轴登场了,接下来enable的过程就是协议栈的核心角色的初始化了看到这里就有一个疑问了,为什么initNative是在onCreate方法中执行,而classInitNative是在静态代码段中和库加载一起执行?为什么两个不能都放在静态代码块中执行?个人观点是jni层加载so包需要一定的时间,需要放在静态代码块中。而initNative是调用具体的接口方法,而且还需要使用java层的callback对象,所以放在AdapterService的onCreate中执行init完成之后,就可以看enable的过程了

协议栈enable

还记得上一篇文章蓝牙,信息获取和enable状态切换吗?enable的方法调用是在状态切换过程中发生的,具体可以参考文章描写enable的第三大部分,enable过程中的状态切换此文不再赘述,上层调用jni层的enableNative方法开启jni层也是直接调用bluetoothInterface的enable方法,而btif只是一个接口而已,方法实现还要去看协议栈

 1//system/bt/btif/src/stack_manager.cc 2//该方法为同步方法 3static void event_start_up_stack(UNUSED_ATTR void* context) { 4 ... 5  module_start_up(get_module(BTIF_CONFIG_MODULE)); 6 ... 7  bte_main_enable(); 8 ... 9  btif_thread_post(event_signal_stack_up, NULL);10}

从这儿开始,可以很明显的知道,协议栈的enable过程就三件事儿

  1. 执行bt_config_module的start_up方法,但目前源码中该方法是空实现,所以不予考虑

  2. enable蓝牙系统

  3. 通知jni层,蓝牙状态发生了变化,新状态为BT_STATE_ON

主要是enable蓝牙系统啊朋友们,感觉代码分析马上要进入核心了,有些小激动啊不看代码还在等啥捏

1//system/bt/main/bte_main.cc2void bte_main_enable() {3  module_start_up(get_module(BTSNOOP_MODULE));4  module_start_up(get_module(HCI_MODULE));5  BTU_StartUp();6}

这个就是该方法的全部代码,用于创建所有bte的任务
三件事儿

  1. BTSNOOP_MODULE,调用btsnoop_module的start_up方法,会打开一个文件用于存储controller和host交互的log.平常所查询的hci日志说的就是这个了,默认存放路径为/data/misc/bluetooth/logs/btsnoop_hci.log

  2. HCI_MODULE,调用hci的start_up方法,hci准备好,监控host和controller之间的交互。同时,会在这儿开启controller

  3. btu开启,bluetooth upper layer开启,蓝牙上层协议栈开启

BTU_StartUp又是一个有很多事儿要做的方法,该方法用于初始化btu控制块儿。该方法中有一些对线程的控制代码,但是根本功能是会调用btu_task_start_up方法,继续对bt进行操作,来看看具体实现

 1//system/bt/stack/btu/btu_task.cc 2void btu_task_start_up(UNUSED_ATTR void* context) { 3 /* Initialize the mandatory core stack control blocks 4     (BTU, BTM, L2CAP, and SDP) 5   * 初始化一些必备的核心协议栈控制块儿 6   */ 7  btu_init_core(); 8 9  /* Initialize any optional stack components10  * 初始化一些可选的组件11  */12  BTE_InitStack();1314  bta_sys_init();1516  /* Initialise platform trace levels at this point as BTE_InitStack() and17   * bta_sys_init()18   * reset the control blocks and preset the trace level with19   * XXX_INITIAL_TRACE_LEVEL20   * 初始化bte的log模块21   */22  module_init(get_module(BTE_LOGMSG_MODULE));2324  message_loop_thread_ = thread_new("btu message loop");25  if (!message_loop_thread_) {26    LOG(FATAL) <" unable to create btu message loop thread.";27  }2829  thread_set_rt_priority(message_loop_thread_, THREAD_RT_PRIORITY);30  thread_post(message_loop_thread_, btu_message_loop_run, nullptr);31}

蓝牙芯片厂商在做蓝牙芯片时,必须要遵循SIG给出的蓝牙规范就像是现在大家公认的tcp/ip模型是统一的蓝牙协议栈有些协议是必须支持的,也就是mandatory强制要求必备的通过btu_init_core来初始化这些核心协议有些协议是可选的BTE_InitStack来初始化

 1//system/bt/stack/btu/btu_init.cc 2void btu_init_core(void) { 3  /* Initialize the mandatory core stack components */ 4  btm_init();//bluetoothManager初始化 5 6  l2c_init();//l2cap初始化 7 8  sdp_init();//sdp服务发现协议初始化 910  gatt_init();//gatt初始化1112  SMP_Init();//SecurityManager初始化1314  btm_ble_init();//bluetoothManager ble初始化15}

好熟悉,初始化l2cap,sdp,gatt,smp,ble同样的,可以看到BTE_InitStack方法中会初始化rfcomm,bnep,pan,a2dp,avrc,gap,hid_host,mca(multi-channels application)代码就不再贴出了具体各个协议赋值什么的,目前也不清楚有什么用,需要继续来分析蓝牙的配对,扫描,连接,通信等等功能,才能明白这些初始化的东西有什么用蓝牙开启的流程算是分析完了
总结一下,蓝牙开启中会完成两大任务

  1. init,把基础设施搭起来,

  2. enable,基础设施搭起来之后,要把核心架构都准备好,之后就可以顺利工作了

蓝牙有三大部分,host--hci--controller,蓝牙开启的过程,也是这三部分使能的过程

听说,多多分享转发这只锦鲤,好运会上门~

原创声明

本文为作者原创文章,未经本人同意,禁止转载和挪作他用

往期精彩回顾

我为什么要坚持写笔记?

码农常犯的4个问题,你中招了吗?Android鼠标源码研究(三)---获取输入事件数据结构探究系(七)--二叉树实现

数据结构(八)--平衡二叉树

戳嗨,你还在看吗?

蓝牙地址的name为null_蓝牙, enable协议栈流程相关推荐

  1. android9获取蓝牙地址,Android获取本机蓝牙地址

    从Android6.0开始,通过BluetoothAdapter.getDefaultAdapter().getAddress()获取的地址是一个固定值02:00:00:00:00:00,部分从低版本 ...

  2. android 蓝牙地址连接打印机,android 连接蓝牙打印机 BluetoothAdapter

    android 连接蓝牙打印机 BluetoothAdapter 源码下载地址:https://github.com/yylxy/BluetoothText.git public class Prin ...

  3. 蓝牙Mesh学习总结一(蓝牙Mesh介绍)

    1.Mesh简介 Blutooth Low Energy Mesh 是基于低功耗蓝牙技术(BLE)的网状网络解决方案.目前使用的是泛洪网状网络(flooding-based mesh network) ...

  4. android8.1获取蓝牙地址,[蓝牙] Android 8.1 获取蓝牙设备地址无效;02:00:00:00:00:00

    序言: 对于这个问题, 谷歌是这样说的: 为了向用户提供更好的数据保护,从此版本开始,Android删除了使用Wi-Fi和蓝牙API对应用程序的设备本地硬件标识符的编程访问. ok.至此我们已经知道为 ...

  5. Android获取本机蓝牙地址

    从Android6.0开始,通过BluetoothAdapter.getDefaultAdapter().getAddress()获取的地址是一个固定值02:00:00:00:00:00,部分从低版本 ...

  6. CSR867x — 如何修改BLE的蓝牙地址

    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX 作 者:文化人 XX 联系方式:(进群:471144274) XX 版权声明:原创文章, ...

  7. BLE学习(4):蓝牙地址类型和设备的隐私

    蓝牙地址也被称为蓝牙MAC地址,它能唯一标识一个蓝牙设备的48位的值.在蓝牙规范中,它被称为BD_ADDR.蓝牙的地址类型可以分为两种:public addresses和random addresse ...

  8. QCC300x笔记(4) -- 蓝牙地址的规则

    哈喽大家好,这是该系列博文的第四篇~ 篇~ <<[系列博文索引]快速通道 >> 蓝牙地址分为三部分: LAP(24位地址低端部分).UAP(8位地址高端部分)和NAP(16位无 ...

  9. 中科蓝汛通过蓝牙地址判断左右声道

    下面说下中科蓝汛通过地址去区分左右声道.我们都知道在中科蓝汛的配置列表中有6种声道的选择,如下图: 现在对上述的几种方式进行解释,(PS:下面的顺序就按上图中的顺序) 第一种方式对左右声道不分配的这种 ...

最新文章

  1. javaWeb防止恶意登陆或防盗链的使用
  2. 申请鸿蒙选择应用程序是什么,申请鸿蒙系统有一个应用选择怎么选择呢
  3. 函数可重入性及编写规范
  4. 江诗丹顿geneve系列_江诗丹顿的另一面
  5. Windows下运行jekyll,编码已不再是问题
  6. 将联网方式更改为桥接模式
  7. Kotlin中使用RxJAVA的map()操作符遇到的问题
  8. 【解题报告】SRM-08
  9. maven下载jar包慢及其他
  10. 短视频服务大PK,阿里云、腾讯云、又拍云、七牛云、金山云5强横向对比
  11. 南京玄武常发广场远程预付费电能管理系统的设计与应用(安科瑞 顾晓燚)
  12. Gradle下载及安装,配置IDEA
  13. 【全网世界区划最全整理输出之第二部分】全世界所有国家的行政区划整理,省市信息,已按照国家,省,市排好序,可直接复制使用,第二部分到8167行,总条数:21088
  14. 【学习技巧和心得】 - 如何将学习资源转化成图文笔记?
  15. 数字图像处理(6)——形态学图像处理
  16. 计算机小学生感兴趣的知识,计算机是 什 么-给小学生讲计算机知识.ppt
  17. 企业微信海外版此前将名称改为 “Wecom”,腾讯打算将其设置为微信的潜在替代方案
  18. 生成函数多项式操作合集
  19. 采购预制发票MIR7 发票校验屏幕格式调整
  20. Remix 以太坊Solidity IDE搭建与初步使用

热门文章

  1. 数字录音系统服务器,Internet呼叫中心坐席全程录音系统录音模块
  2. react gps坐标转换_手持GPS的三参数计算方法
  3. 【NOI2014】动物园 kmp性质
  4. 集群服务器状态命令------rs.status()各个字段的含义
  5. MSP430单片机输入与输出
  6. C++ 四种强制类型转变与区别之处
  7. UA MATH567 高维统计II 随机向量6 亚高斯随机向量的应用: 半正定规划
  8. UA MATH575B 数值分析下VI 统计物理的随机模拟方法2
  9. Matlab简单系统仿真示例1
  10. windbg 脚本学习总结