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


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

    分类: Android的原生应用分析 4773人阅读 评论(20) 收藏 举报

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

    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()是判断是否已打开蓝牙的,如果已打开,后续操作要执行开启蓝牙的动作,前面那几行注册广 播其中就有这个作用。

    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()则是具体开关动作。看下代码

    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)才是真正的主角,代码如下:

    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应用。

    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是一样的,不影响理解。

    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(),跳转关系不复杂,我们直接看最后一个关键的。

    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标识的消息

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

    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里面

    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

    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()里是怎么实现的
    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. }  
    又是一个新函数,直接跳转,比起刚才的寻觅这太幸福了
    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 ");  
    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那部分就好了,
    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里面有什么,快到头了

    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
    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的源码中我们只跟到这里了,那后面会做什么呢,加载驱动和上电这两项肯定要有了,打开蓝牙没这两步怎么行,类似下 面的字符串
    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控制上下电,具体厂商具体做法也不同。

    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的上电算在一起,这个也是要注意的,小心误判。最后 呢,这次文章写得有点仓促,写错的或遗漏地方希望朋友指出来,非常感谢。






    更多 0
     
    查看评论
    12楼 reality_jie 前天 22:31发表 [回复]
    顶一下表示支持!
    11楼 默默dede 3天前 17:16发表 [回复]
    遇到连接蓝牙设备的时候出现p-data->con.status=12 authentication failed的问题,请问高手们怎么解决?
    10楼 vicalwu 2014-01-08 16:52发表 [回复]
    文章写的很好,支持楼主继续分享
    9楼 syngalon螳螳 2013-10-26 15:35发表 [回复]
    今天发现了一篇,看看是不是很像哈哈
    http://blog.csdn.net/yutao52shi/article/details/12690353
    8楼 躺着享受 2013-10-25 16:13发表 [回复]
    就要到我最想看的部分之时嘎然而止,不过博主写的很好!!!!
    Re: balmy 2013-10-25 21:57发表 [回复]
    回复fankyo:哎,水平不够写不下去了。再深入的还不知道从哪下手呢。
    7楼 xyp5299 2013-08-19 18:14发表 [回复]
    帮助很大,请楼主继续 分享
    6楼 bm7638 2013-08-19 17:57发表 [回复]
    写的不错哦,整体思路很清晰,但是跟踪流程过程太随意了,建议能有个流程图保护起来,很多地方直接就跳过了。
    Re: balmy 2013-08-25 09:13发表 [回复]
    回复bm7638:呵呵 写这个的时候是懒了没有画图
    5楼 kuna 2013-06-28 10:36发表 [回复]
    请教一下,我一直在看初始化和使能部分log和代码,有一个问题一直没有搞懂:BluetoothAdapterApp是怎么起来的?

    谢谢!
    4楼 windjkl 2013-06-20 10:38发表 [回复]
    设置-打开蓝牙:出现以下错误是什么原因:
    D/BluetoothAdapter( 3463): 657778856: getState() : mService = null. Returning STATE_OFF
    D/LocalBluetoothManager( 3463): setting foreground activity to non-null context
    D/BluetoothAdapter( 3463): 657778856: getState() : mService = null. Returning STATE_OFF
    Re: balmy 2013-06-20 21:37发表 [回复]
    回 复windjkl:这个没遇到过呢,不了解你当时的环境,没啥好想法。另外答复你添加USB外置蓝牙模块的问题,也是我咨询来的,仅供参考吧。usb设备 应该都会有注册流程吧,这一部分好像android部分的源码没有,在linux的源码是有的,也就是android源码里kernel。目录下的类似这 个路径:kernel/drivers/bluetooth/btusb.c,按你说的已经识别出来是不是可以认为注册成功了?后续代码没见过,可能没有 也可能是没发现的,目前接触到都是走串口的,目前只知道这些,希望不会误导你。
    Re: windjkl 2013-06-21 08:29发表 [回复]
    回复baimy1985:谢谢楼主,注册已经成功,现在疑问是service的启动?jb4.2是否还需要在初始化时启动d-bus,hciattach这些服务呢?我看有些方案商的init.rc中并没有这些服务?是不是换成别的方式呢?
    3楼 kuna 2013-06-14 16:19发表 [回复]
    看了你的文章,受益不浅,不知道4.2时候还有4.0上hcidump那样的分析工具?
    Re: balmy 2013-06-16 22:43发表 [回复]
    回复kuna:这个我也想知道,目前也在求证中
    Re: xuexingyang 2013-06-20 19:27发表 [回复]
    回复baimy1985:4.2中hcidump没有了,只有靠confi文件来开关了。
    Re: balmy 2013-06-20 21:13发表 [回复]
    回复xuexingyang:谢谢分享
    2楼 kuna 2013-06-01 15:16发表 [回复]
    请教一下:

    handleEnable(boolean persist, boolean quietMode)是怎么跳转到启动service的呢?

    就 是“下面跑到哪个service里去了呢,在log信息里可以看到"ActivityManager: Start proc com.android.bluetooth for service com.android.bluetooth/.btservice.AdapterService:" ”这段。
    Re: balmy 2013-06-02 20:23发表 [回复]
    回复kuna:IBluetooth.class.getName()这个当时的取值是AdapterService,可以看下面的4.2.2源码,也许可以帮助你
    /packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java 480行的
    private static class AdapterServiceBinder extends IBluetooth.Stub
  • 相关阅读:
    jenkins
    k8s 驱逐限制
    jenkins+k8s 实现持续集成
    镜像更新备份
    【工具分享】2020年4月 phpstorm2020.1 for mac
    【排坑】mac安装homebrew会遇到的各种问题解决方案
    记一次C盘扩容
    2018夏季工作台之再设计
    left join后面加上where条件浅析
    编程随想篇(2018夏)
  • 原文地址:https://www.cnblogs.com/jeanschen/p/3528254.html
Copyright © 2020-2023  润新知