android -- 蓝牙 bluetooth (二) 打开蓝牙

4.2的蓝牙打开流程这一部分还是有些变化的,从界面上看蓝牙开关就是设置settings里那个switch开关,widget开关当然也可以,起点不同而已,后续的流程是一样的。先来看systemServer.java的代码,蓝牙服务开启的地方,最后一个else分支是我们关心的,前两个是模拟器的一个测试模式的。

 

[java] view plaincopy
 
  1. if (SystemProperties.get("ro.kernel.qemu").equals("1")) {                                            
  2.                Slog.i(TAG, "No Bluetooh Service (emulator)");                                     
  3.            } else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {                       
  4.                Slog.i(TAG, "No Bluetooth Service (factory test)");                                
  5.            } else {                                                                               
  6.                Slog.i(TAG, "Bluetooth Manager Service");                                          
  7.                bluetooth = new BluetoothManagerService(context);                                  
  8.                ServiceManager.addService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, bluetooth);  
  9.            }     

         暂且看下bluetoothManagerService的构造方法,代码有点多,我们只看两个地方, loadStoredNameAndAddress()是读取蓝牙打开默认名称的地方,isBluetoothPersistedStateOn()是判断是否已打开蓝牙的,如果已打开,后续操作要执行开启蓝牙的动作,前面那几行注册广播其中就有这个作用。

 

[java] view plaincopy
 
  1. BluetoothManagerService(Context context) {  
  2.         ...一些变量声明初始化...  
  3.         IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);  
  4.         filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);  
  5.         filter.addAction(Intent.ACTION_USER_SWITCHED);  
  6.         registerForAirplaneMode(filter);  
  7.         mContext.registerReceiver(mReceiver, filter);  
  8.         loadStoredNameAndAddress();  
  9.         if (isBluetoothPersistedStateOn()) {  
  10.             mEnableExternal = true;  
  11.         }  
  12.     }  

 

        回到界面开关那个看得着的地方,界面上开关就是BluetoothEnabler.java这个类了,而setBluetoothEnabled()则是具体开关动作。看下代码

 

[java] view plaincopy
 
  1. public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {  
  2.        // Show toast message if Bluetooth is not allowed in airplane mode  
  3.        if (isChecked &&  
  4.                !WirelessSettings.isRadioAllowed(mContext, Settings.Global.RADIO_BLUETOOTH)) {  
  5.            Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();  
  6.            // Reset switch to off  
  7.            buttonView.setChecked(false);            
  8.        }  
  9.   
  10.        if (mLocalAdapter != null) {  
  11.            mLocalAdapter.setBluetoothEnabled(isChecked);  
  12.        }  
  13.        mSwitch.setEnabled(false);  
  14.    }  

这里在判断是飞行模式不知道为什么没有return,如果是飞行模式会有提示toast弹出,既然这样源码为什么还要执行下面打开流程呢,也许是个bug?不细究这个了,继续看setBluetoothEnabled()方法做什么了,很明显mLocalAdapter(LocalBluetoothAdapter )只是个过渡,里面的 mAdapter(BluetoothAdapter)才是真正的主角,代码如下:

 

[java] view plaincopy
 
  1. public void setBluetoothEnabled(boolean enabled) {  
  2.     boolean success = enabled   ? mAdapter.enable() : mAdapter.disable();  
  3.   
  4.     if (success) {  
  5.         setBluetoothStateInt(enabled  
  6.             ? BluetoothAdapter.STATE_TURNING_ON  
  7.             : BluetoothAdapter.STATE_TURNING_OFF);  
  8.     } else {  
  9.        .........  
  10.     }  
  11. }  

        在BluetoothAdapter.java里可以看到一个单例模式的应用,主要提供给其它程序调用蓝牙的一些方法用的,外部程序想调用蓝牙的方法就要先用这个

拿到BluetoothAdapter对象,代码也简单看下吧,里面是典型的binder应用。

 

[java] view plaincopy
 
  1. public static synchronized BluetoothAdapter getDefaultAdapter() {  
  2.    if (sAdapter == null) {  
  3.        IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);  
  4.        if (b != null) {  
  5.            IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);  
  6.            sAdapter = new BluetoothAdapter(managerService);  
  7.        } else {  
  8.            Log.e(TAG, "Bluetooth binder is null");  
  9.        }  
  10.    }  
  11.    return sAdapter;  

        此时我们更关心mAdapter.enable()的后续操作,外部其它应用到getDefaultAdapter()也是调用enable(),注意,到了BluetoothAdapter我们已经在framework层了,顺着BluetoothAdapter.java的enable()调用先回到BluetoothManagerService.java的enable(),再进一步来到BluetoothManagerService.java中的handleEnable()

方法,后面要跳转到新类了,贴出来一起看下,这部分好像不同版本还有些出入,不过核心的启动service是一样的,不影响理解。

 

[java] view plaincopy
 
  1.  private void handleEnable(boolean persist, boolean quietMode) {  
  2.  synchronized(mConnection) {  
  3.     if ((mBluetooth == null) && (!mBinding)) {  
  4.         //Start bind timeout and bind  
  5.         Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);  
  6.         mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);  
  7.         mConnection.setGetNameAddressOnly(false);  
  8.         Intent i = new Intent(IBluetooth.class.getName());  
  9.         if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE,  
  10.                                   UserHandle.USER_CURRENT)) {  
  11.             mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);  
  12.             Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());  
  13.         } else {  
  14.             mBinding = true;  
  15.         }  
  16.     }   

       下面跑到哪个service里去了呢,在log信息里可以看到"ActivityManager: Start proc com.android.bluetooth for service com.android.bluetooth/.btservice.AdapterService:"

 

这样的信息,那就是去AdapterService里看看,里面一共有三个enable(),跳转关系不复杂,我们直接看最后一个关键的。

 

[java] view plaincopy
 
  1. public synchronized boolean enable(boolean quietMode) {  
  2.      enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,  
  3.              "Need BLUETOOTH ADMIN permission");  
  4.      if (DBG)debugLog("Enable called with quiet mode status =  " + mQuietmode);  
  5.      mQuietmode  = quietMode;  
  6.      Message m =  
  7.              mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON);  
  8.      mAdapterStateMachine.sendMessage(m);  
  9.      return true;  
  10.  }  

 状态机来了,状态转换图,从一个状态接受命令跳到另一个状态,因为我们是在开启蓝牙,所以先去的AdapterState.java内部类offstate.java里面找,在这个分支USER_TURN_ON看到mAdapterService.processStart();在这里面可以看到蓝牙遍历下所支持的profile,最后又发出个带AdapterState.STARTED标识的消息

处理在同文件下面的代码里

 

[java] view plaincopy
 
  1. case STARTED:   {  
  2.   if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STARTED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff);  
  3.   //Remove start timeout  
  4.   removeMessages(START_TIMEOUT);  
  5.   
  6.   //Enable  
  7.   boolean ret = mAdapterService.enableNative();  
  8.   if (!ret) {  
  9.       Log.e(TAG, "Error while turning Bluetooth On");  
  10.       notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);  
  11.       transitionTo(mOffState);  
  12.   } else {  
  13.       sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);  
  14.   }   


看到那个enableNative()函数调用了吧,又要用到JNI了,稍微回头看下前面的代码,我们先从应用界面开关BluetoothEnabler走到framework的BluetoothAdapter,又回到package的adapterService,现在又要去JNI的C++代码了,往常一般是packages -->framework-->下面一层,这次顺序有些颠倒了,不过这不能影响我们跟踪代码,最后

 

还是要到下面去的。一起往下看吧。

        根据android JNI的函数命名惯例很容易找到enableNative对应的C++函数在packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp里面

 

[java] view plaincopy
 
  1. static jboolean enableNative(JNIEnv* env, jobject obj) {  
  2.    ALOGV("%s:",__FUNCTION__);  
  3.    jboolean result = JNI_FALSE;  
  4.     if (!sBluetoothInterface) return result;  
  5.     int ret = sBluetoothInterface->enable();  
  6.     result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;  
  7.     return result;  
  8. }  

 

代码瞬间简洁了不少,看来更多的故事还在下面,sBluetoothInterface这是什么,直接关系到下一步去哪的问题,看下变量声明,原来是

Const bt_interface_t *sBluetoothInterface = NULL; 再去找在哪初始化,搜索external目录可以找到/external/bluetooth/bluedroid/btif/src/bluetooth.c

 

[cpp] view plaincopy
 
  1.     static const bt_interface_t bluetoothInterface = {  
  2.     sizeof(bt_interface_t),  
  3.     init,  
  4.     enable,  
  5.     disable,  
  6.     .............  
  7.     start_discovery,  
  8.     cancel_discovery,  
  9.     create_bond,  
  10.     remove_bond,  
  11.     cancel_bond,  
  12.    ...............  
  13. };  

原来在这里,说下怎么找到,直接跳转是不成了,看这个文件夹下的mk文件,那里面有libhardware目录是编译的时候要用到,这个多半在hardware目录里,在这里面很快可以看到bluetooth.h,那里面有最我们要找的结构体定义,头文件找到了,再找同名C文件就快了,好了继续吧看下enable()里是怎么实现的

 

 

[cpp] view plaincopy
 
  1. static int enable( void )  
  2. {  
  3.     ALOGI("enable");  
  4.   
  5.     /* sanity check */  
  6.     if (interface_ready() == FALSE)  
  7.         return BT_STATUS_NOT_READY;  
  8.   
  9.     return btif_enable_bluetooth();  
  10. }  

又是一个新函数,直接跳转,比起刚才的寻觅这太幸福了

[cpp] view plaincopy
 
  1. bt_status_t btif_enable_bluetooth(void)  
  2. {  
  3.     BTIF_TRACE_DEBUG0("BTIF ENABLE BLUETOOTH");  
  4.   
  5.     if (btif_core_state != BTIF_CORE_STATE_DISABLED)  
  6.     {  
  7.         ALOGD("not disabled\n");  
  8.         return BT_STATUS_DONE;  
  9.     }  
  10.   
  11.     btif_core_state = BTIF_CORE_STATE_ENABLING;  
  12.   
  13.     /* Create the GKI tasks and run them */  
  14.     bte_main_enable(btif_local_bd_addr.address);  
  15.   
  16.     return BT_STATUS_SUCCESS;  
  17. }  

忘了写路径了 好在可以直接跳转,下面是/external/bluetooth/bluedroid/main/bte_main.c,有点长,暂时只关心set_power那部分就好了,

[cpp] view plaincopy
 
  1. void bte_main_enable(uint8_t *local_addr)  
  2. {  
  3.     APPL_TRACE_DEBUG1("%s", __FUNCTION__);  
  4.     ........................  
  5.   
  6. #if (defined (BT_CLEAN_TURN_ON_DISABLED) && BT_CLEAN_TURN_ON_DISABLED == TRUE)  
  7.         APPL_TRACE_DEBUG1("%s  Not Turninig Off the BT before Turninig ON", __FUNCTION__);  
  8.   
  9. #else  
  10.         /* toggle chip power to ensure we will reset chip in case 
  11.            a previous stack shutdown wasn't completed gracefully */  
  12.         bt_hc_if->set_power(BT_HC_CHIP_PWR_OFF);  
  13. #endif  
  14.         bt_hc_if->set_power(BT_HC_CHIP_PWR_ON);  
  15.   
  16.         bt_hc_if->preload(NULL);  
  17.     }  
  18.   
  19.      .............................  
  20. }  

 

路径在这里/external/bluetooth/bluedroid/hci/src/bt_hci_bdroid.c,看看set_power里面有什么,快到头了

 

[cpp] view plaincopy
 
  1. static void set_power(bt_hc_chip_power_state_t state)  
  2. {  
  3.     int pwr_state;  
  4.   
  5.     BTHCDBG("set_power %d", state);  
  6.   
  7.     /* Calling vendor-specific part */  
  8.     pwr_state = (state == BT_HC_CHIP_PWR_ON) ? BT_VND_PWR_ON : BT_VND_PWR_OFF;  
  9.   
  10.     if (bt_vnd_if)  
  11.         bt_vnd_if->op(BT_VND_OP_POWER_CTRL, &pwr_state);  
  12.     else  
  13.         ALOGE("vendor lib is missing!");  
  14. }  

        这下又有新东西了bt_vnd_if,这个是什么,bt_vendor_interface_t *bt_vnd_if=NULL;和刚才的bt_interface_t 一样,我们希望可以找到它的初始化,那样就可以继续跟踪了,不过看到下面的代码和注释,在源码中我们要绝望了。路径:/external/bluetooth/bluedroid/hci/include/bt_vendor_lib.h

 

 

[cpp] view plaincopy
 
  1. /* Entry point of DLib -- 
  2.  *      Vendor library needs to implement the body of bt_vendor_interface_t 
  3.  *      structure and uses the below name as the variable name. HCI library 
  4.  *      will use this symbol name to get address of the object through the 
  5.  *      dlsym call. 
  6.  */  
  7. extern const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE;  
  8.   
  9. bt_vendor_interface_t *bt_vnd_if=NULL;  


 google定义好了接口,具体实现要看vendor厂商来做了,这后面怎么实现就看各家芯片商怎么写了,肯定各有不同,而且这一部分代码一般是不会公开,当然授权购买后除外了。所以在4.2的源码中我们只跟到这里了,那后面会做什么呢,加载驱动和上电这两项肯定要有了,打开蓝牙没这两步怎么行,类似下面的字符串

 

 

[cpp] view plaincopy
 
  1. static const char* BT_DRIVER_MODULE_PATH =    "/system/lib/modules/mbt8xxx.ko";  
  2. static const char* BT_DRIVER_MODULE_NAME =     "bt8xxx";  
  3. static const char* BT_DRIVER_MODULE_INIT_ARG = " init_cfg=";  
  4. static const char* BT_DRIVER_MODULE_INIT_CFG_PATH = "bt_init_cfg.conf";  

在有类似下面的动作,insmod加载驱动,rfkill控制上下电,具体厂商具体做法也不同。

 

[cpp] view plaincopy
 
  1. ret = insmod(BT_DRIVER_MODULE_PATH, arg_buf);  
  2. ret = system("/system/bin/rfkill block all");  

       写到这,关于4.2源码的蓝牙打开流程就算结束了,比起4.1之前的代码感觉没有以前的直观,对于vendor那部分的代码大家只能看各自厂商的代码了,一般蓝牙开启后才会上电,这样也比较符合逻辑和节省电量,是否上电可以连上手机用adb shell看sys/class/rfkill目录下的state状态值,有些厂商会把蓝牙和wifi的上电算在一起,这个也是要注意的,小心误判。最后呢,这次文章写得有点仓促,写错的或遗漏地方希望朋友指出来,非常感谢。

转载于:https://www.cnblogs.com/Free-Thinker/p/4539973.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/459377.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开始nodejs+express的学习+实践(8)

为什么80%的码农都做不了架构师?>>> 1.session使用 介绍的非常详细: http://www.cnblogs.com/chenchenluo/p/4197181.html 对比我们的app.js需要引入express-session模块和使用这个模块,在package依赖,并加载。 我们修…

TCP的定时器系列 — 保活定时器(有图有代码有真相!!!)

转载 主要内容:保活定时器的实现,TCP_USER_TIMEOUT选项的实现。 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 原理 HTTP有Keepalive功能,TCP也有Keepalive功能,虽然都叫Keepalive&#xff0c…

浅谈 Scala 中下划线的用途

Scala 作为一门函数式编程语言,对习惯了指令式编程语言的同学来说,会不大习惯,这里除了思维方式之外,还有语法层面的,比如 underscore(下划线)就会出现在多种场合,令初学者相当疑惑&…

maven项目部署到linux上的奇葩问题

2019独角兽企业重金招聘Python工程师标准>>> 经常会遇到这样子的问题,maven项目在本地的eclipse配置的好好的,结果一到服务器就运行不起来。 当然遇到这种情况,我们首先会想到环境变量和相关的路径问题,但是当这两个条…

TCP的定时器系列 — 零窗口探测定时器(有图有代码有真相!!!)

转载 主要内容:零窗口探测定时器的实现。 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 出现以下情况时,TCP接收方的接收缓冲区将被塞满数据: 发送方的发送速度大于接收方的接收速度。 接收方的应用程序未…

java中XPATH操作xml,非常便捷

<?xml version"1.0" encoding"UTF-8"?> <MessageList><item type"1"><template_id value"p2ItJPj0taTTP4QRXP-z51nYuD3aDNhgvLOusWGY4p0"/><topcolor value"#173177"/><first value&quo…

【python】r+,w+ 全局变量

来源&#xff1a;http://www.educity.cn/wenda/352188.html r&#xff1a;可读可写&#xff0c;若文件不存在&#xff0c;报错w: 可读可写&#xff0c;若文件不存在&#xff0c;创建文本模式&#xff1a;遇换行符时根据操作系统不同自动转换换行符&#xff0c;比如读文件时遇\n…

网络:TCP通讯之 time_wait 状态

基于TCP协议的通讯流程1、TCP建立连接2、TCP断开连接3、TCP状态转换TCP状态解释&#xff1a; SYN-RECVD&#xff1a;再收到和发送一个连接请求后等待对方对连接请求的确认 ESTABLISHED&#xff1a;代表一个打开的连接 FIN-WAIT-1&#xff1a;等待远程TCP连接中断请求&#xff0…

linux下echo与time服务的程序实现

一、针对ECHO服务的TCP客户软件的实现 1.网络拓扑结构&#xff1a; 2.源码&#xff1a; 1 #include <stdio.h>2 #include <stdlib.h>3 #include <unistd.h>4 #include <string.h>5 #include <stdarg.h>6 #include <sys/types.h>7 #include…

iOS UI-AlertView(警示框)和ActionSheet(选择框、操作表单)

1 #import "ViewController.h"2 3 interface ViewController ()<UIAlertViewDelegate,UIActionSheetDelegate>4 5 end6 7 implementation ViewController8 9 #pragma mark - 生命周期10 - (void)viewDidLoad {11 [super viewDidLoad];12 // 创建展示Al…

Linux高性能服务器编程:进程池和线程池原理及应用(有图有代码有真相!!!)

一、问题引入 在前面编写多进程、多线程服务器时通过动态创建子进程和子线程来实现并发服务器&#xff0c;这样做有以下缺点&#xff1a; 1&#xff09;动态创建进程、线程将会比较耗费时间&#xff0c;将导致较慢的客户响应。 2&#xff09;动态创建的子进程只为一个客户服…

Linux:多进程、多线程服务器的实现解析(有图有代码有真相!!!)

一、问题引入 阻塞型的网络编程接口 几乎所有的程序员第一次接触到的网络编程都是从 listen()、send()、recv()等接口开始的。使用这些接口可以很方便的构建服务器 /客户机的模型。 我们假设希望建立一个简单的服务器程序&#xff0c;实现向单个客户机提供类似于“一问一答”的…

数据结构:神奇的B树实现解析(有图有代码有真相!!!)

一、B树引入 二叉搜索树、平衡二叉树、红黑树都是动态查找树&#xff0c;典型的二叉搜索树结构&#xff0c;查找的时间复杂度和树的高度相关O(log2N)。 1&#xff09;数据杂乱无章-------线性查找--O&#xff08;n&#xff09; 2&#xff09;数据有序-------二分查找 ---O(lo…

Linux:dup/dup2 文件描述符重定向函数(有图有代码有真相!!!)

一、dup/dup2 有时我们希望把标准输入重定向到一个文件&#xff0c;或者把标准输出重定向到一个网络连接。系统调用dup和dup2能够复制文件描述符。dup返回新的文件文件描述符&#xff08;没有用的文件描述符最小的编号&#xff09;。 dup2可以让用户指定返回的文件描述符的值…

Linux:I/O多路转接之select(有图有代码有真相!!!)

一、select引入 一次 I/O 分为两个部分&#xff1a;1&#xff09;等待数据就绪 2&#xff09;进行数据转移 1、select 原理&#xff1a; select的原理就是减少等待数据就绪的比重&#xff0c;巧妙的利用等待队列机制让用户进程适当在没有资源可读/写时睡眠&#xff0c;有资…

Linux: I/O多路转接之poll(有图有代码有真相!!!)

一、poll()函数解析 不同与select使⽤用三个位图来表⽰示三个fdset的⽅方式&#xff0c;poll使⽤用⼀一个 pollfd的指针实现。pollfd结构包含了要监视的event和发⽣生的event&#xff0c; 不再使⽤用select“参数-值”传递的⽅方式。同时&#xff0c;pollfd并没有最⼤大数量限…

kalilinux装到u盘上的弊端_付费下载的歌曲,竟然无法在汽车上播放!原因在这里...

『使用某音乐播放器下载了周杰伦的110首歌曲&#xff0c;其中106首是kgm格式&#xff0c;4首mp3格式&#xff0c;装到U盘后&#xff0c;在其它设备播放只有4首mp3格式的可以播放&#xff0c;其它的均无法播放&#xff0c;请问该如何处理&#xff1f;』网友留言截图这是一位网友…

iconsvg image怎么变为path_昆凌是怎么收服天王周杰伦的?这几招太高明了

周杰伦和昆凌又出来撒狗粮了&#xff01;就在前两天(6月2日)&#xff0c;在参加郎朗的婚礼时&#xff0c;#周杰伦搂昆凌看烟花#的消息悄悄上了热搜。视频中&#xff0c;两人并肩站立&#xff0c;一起欣赏着窗外的美景。周杰伦时不时在昆凌的耳边私语几句&#xff0c;看起来很是…

Linux: I/O多路转接之epoll(有图有代码有真相!!!)

一、基本概念 epoll是Linux内核为处理大批量文件描述符而作了改进的poll&#xff0c;是Linux下多路复用IO接口select/poll的增强版本&#xff0c;它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。 另一点原因就是获取事件的时候&#xff0c;它无须遍历整…

ewebeditor未授权:功能被禁用请先配置授权_SteamPY新功能——外区账号礼物自动领取...

自从PY平台增加了外区代购后发现许多玩家在购买礼物时常会发生收到礼物后准备点击入库时弹出地区不可用的提示这个问题在Steam外区账号一直频繁发生究其因在于Steam账号登录时的IP问题遇到该问题切勿拒收礼物&#xff01;通过Steam客户端清理登录授权注销退出后再次使用账号对应…