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. if (mLocalAdapter != null) {
  10. mLocalAdapter.setBluetoothEnabled(isChecked);
  11. }
  12. mSwitch.setEnabled(false);
  13. }

这里在判断是飞行模式不知道为什么没有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. if (success) {
  4. setBluetoothStateInt(enabled
  5. ? BluetoothAdapter.STATE_TURNING_ON
  6. : BluetoothAdapter.STATE_TURNING_OFF);
  7. } else {
  8. .........
  9. }
  10. }

在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. //Enable
  6. boolean ret = mAdapterService.enableNative();
  7. if (!ret) {
  8. Log.e(TAG, "Error while turning Bluetooth On");
  9. notifyAdapterStateChange(BluetoothAdapter.STATE_OFF);
  10. transitionTo(mOffState);
  11. } else {
  12. sendMessageDelayed(ENABLE_TIMEOUT, ENABLE_TIMEOUT_DELAY);
  13. }

看到那个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. /* sanity check */
  5. if (interface_ready() == FALSE)
  6. return BT_STATUS_NOT_READY;
  7. return btif_enable_bluetooth();
  8. }

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

[cpp] view plaincopy

 

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

忘了写路径了 好在可以直接跳转,下面是/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. #if (defined (BT_CLEAN_TURN_ON_DISABLED) && BT_CLEAN_TURN_ON_DISABLED == TRUE)
  6. APPL_TRACE_DEBUG1("%s  Not Turninig Off the BT before Turninig ON", __FUNCTION__);
  7. #else
  8. /* toggle chip power to ensure we will reset chip in case
  9. a previous stack shutdown wasn't completed gracefully */
  10. bt_hc_if->set_power(BT_HC_CHIP_PWR_OFF);
  11. #endif
  12. bt_hc_if->set_power(BT_HC_CHIP_PWR_ON);
  13. bt_hc_if->preload(NULL);
  14. }
  15. .............................
  16. }

路径在这里/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. BTHCDBG("set_power %d", state);
  5. /* Calling vendor-specific part */
  6. pwr_state = (state == BT_HC_CHIP_PWR_ON) ? BT_VND_PWR_ON : BT_VND_PWR_OFF;
  7. if (bt_vnd_if)
  8. bt_vnd_if->op(BT_VND_OP_POWER_CTRL, &pwr_state);
  9. else
  10. ALOGE("vendor lib is missing!");
  11. }

这下又有新东西了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. 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

android -- 蓝牙 bluetooth (二) 打开蓝牙相关推荐

  1. 蓝牙(二)蓝牙搜索、配对、连接

    1.搜索 从上一节我们可以知道,蓝牙状态发生了改变,并发生了回调.咱们就从回调开始. DevicePickerFragment.java 用于蓝牙设置界面的蓝牙配置和管理 @Overridepubli ...

  2. 小程序蓝牙(二)蓝牙的具体操作步骤(搜索-连接-读写)

    1.第一步 初始化小程序蓝牙模块 初始化模块有可能会出现失败,没有开启手机蓝牙或者其他因素时,失败会有对应的失败编码,这个具体详情看官方文档 // 第一步 初始化蓝牙模块openBluetoothAd ...

  3. 【Android】Audio音频输出通道切换 - 蓝牙bluetooth、外放

    参考: [Android]Audio音频输出通道切换 - 蓝牙.外放 Android Audio 音频输出通道切换 为什么 iOS 或 Android 设备连接蓝牙设备后不能通过蓝牙设备接电话? xq ...

  4. Android打开蓝牙的两种方法

    1.隐式打开蓝牙 /*隐式打开蓝牙*/if (!mBluetoothAdapter.isEnabled()) {mBluetoothAdapter.enable();} 2.弹出对话框供用户选择是否打 ...

  5. android 连接蓝牙电子秤_电子秤蓝牙双模通讯Android低功耗蓝牙(蓝牙4.0)BLE开发(上)...

    电子秤蓝牙双模通讯Android低功耗蓝牙(蓝牙4.0)BLE开发(上) 前段时间,公司项目用到了手机APP和蓝牙设备的通讯开发,这里也正好对低功耗蓝牙(蓝牙4.0及以后标准)的开发,做一个总结. 蓝 ...

  6. Android中需要知道的蓝牙相关常识

    首先蓝牙设备有很多种,需要区分蓝牙设备的类型,市面上主要的蓝牙设置有 蓝牙鼠标,键盘,游戏手柄等 蓝牙耳机 蓝牙音箱 可穿戴的蓝牙手环手表 蓝牙健康设备,电子秤等 因此技术上大体分为经典蓝牙和低功耗( ...

  7. 计算机蓝牙快捷键,如何打开Windows10笔记本电脑的蓝牙,快捷键打开笔记本电脑的蓝牙!...

    如何打开Windows 10笔记本电脑的蓝牙?打开笔记本电脑的蓝牙快捷方式! 通常,笔记本电脑具有蓝牙功能,但是使用蓝牙的朋友并不多. 实际上,蓝牙还有很多用途. 例如,通过计算机和移动电话的蓝牙功能 ...

  8. 虾扯蓝牙(一)获取蓝牙当前状态,监听蓝牙手动开关,代码开关蓝牙

    前言 半年没有更新博客了,首要原因就是我太懒了,其次是换了新工作,新环境,一直都处于适应与学习阶段,因为涉及到的领域以前都是没接触的–有幸外派到海尔做智能家居,第一次到这种大的公司来敲代码,很多东西都 ...

  9. HC-05(ZS-040)蓝牙模块使用详情(蓝牙模块配置、手机蓝牙控制单片机、蓝牙与蓝牙之间的通信)含51、32程序

    HC-05是一款主从一体化的蓝牙模块,因此其使用起来比较方便,只需要进行简单的配置即可. 本文就手把手的介绍小白入手模块后如何使用. 对于模块使用:1.蓝牙配置→2.手机与蓝牙的传输→3.手机通过蓝牙 ...

  10. 熊晨沣蓝牙实战--小程序蓝牙连接2.0

    微信小程序蓝牙连接2.0说明: 1.本版本区分了ANDROID和IOS系统下蓝牙连接的不同方式. 2.兼容了更多情况下的链接包括: (1)未开启设备蓝牙,当监听到开启了蓝牙后自动开始连接. (2)初始 ...

最新文章

  1. 每天一个linux命令(2):文件权限详解
  2. linux shell 数组元素带空格,在bash中解析带有空格的JSON数组
  3. c语言三个数按大小排列怎么编程,C语言三个数排列大小的实现方法
  4. ODBC / OLEDB___DAO / RDO / ADO
  5. 分享几个接口自动化的实战练手项目
  6. 计算机编程 设计入门,计算机编程入门图文教程_计算机程序设计教程,计算机编程入门教程-其它文档类资源...
  7. ping命令使用集合
  8. 使用换IP软件的代理IP速度缓慢的原因是什么?详解!
  9. 代码规范 任重而道远
  10. 代理模式——远程代理(一)
  11. 粗糙集,邻域粗糙集与实域粗糙集概述
  12. POI IllegalArgumentException: Sheet index (0) is out of range (no sheets)问题解决
  13. asp群发微信公众号模板消息代码asp写的
  14. 解决M1处理器安装PS闪退问题Photoshop 2021 fo mac(支持最新M1芯片处理器款mac)
  15. 广州大学数据结构课程设计
  16. HTML制作简易百度下拉菜单栏
  17. Zabbix agent on XXXX is unreachable for 5 minutes
  18. SKF VOGEL ZM505-3+140 ZP505
  19. 寻宝机器人电路板焊接_寻宝活动——组装机器人
  20. 南方CASS格式DWG的读取

热门文章

  1. 怎么用计算机拟合数据,数据拟合的几个应用实例-毕业论文.doc
  2. 软件设计师下午题java_2018上半年软件设计师下午真题(三)
  3. java测试类和类_【测试开发】从测试角度看Java异常类(错误和异常区别介绍)
  4. mysql 报表统计sql使用实例_mysql 案例~mysql元数据的sql统计
  5. linux和windows启动,Linux和Windows双系统的启动
  6. 五轴编程_沙井万丰数控数控编程五轴编程那个软件好用
  7. leetcode485. 最大连续1的个数 *py:“又是一行就解决了,没意思”
  8. 算法(9)--两个数的最大公约数
  9. 《Python Cookbook 3rd》笔记(1.14):排序不支持原生比较的对象
  10. 如何查询oracle最近报警信息,教你怎样用Oracle方便地查看报警日志错误