文档章节

4.2 蓝牙 还不错的原创分析系列

b
 bluetootbiao
发布于 2015/10/08 14:38
字数 6114
阅读 20
收藏 0

  前段时间在 网上看了一些关于android蓝牙的文章,发现大部分是基于老版本(4.1以前含4.1)的源码,虽然无碍了解蓝牙的基本原理和工作流程,但对着4.2.2的代码看起来总是有些遗憾。所以针对4.2.2版本代码整理下相关知识,当然蓝牙工作的主干流程是没有变的,上电、加载驱动这些动作少不了的,只是这些功能的实现代码位置变了不少。希望本文可以让大家对android4.2的蓝牙部分代码有一个初步的了解。


        正文开始前,先明确代码版本:android  jellyBean 4.2.2,后续的蓝牙相关文章同样如此。

        另推荐个源码在线阅读网址 http://androidxref.com/,已经知道的童鞋无视这行吧。


        入手一个新的模块或应用,当然首先要知道它都有什么了,与它相关的代码在那里,所以先一起看下蓝牙代码分布吧。


1. 代码分布:

    packages/apps/Bluetooth/     

         看这路径肯定是蓝牙应用方面的代码了,主要是关于蓝牙应用协议的表现代码,包括opp、hfp、hdp、a2dp、pan等等,这些名词后面再解释。

    frameworks/base/core/java/android/server/  

          4.2以后这个目录虽然还有了,但里面代码已经转移到应用层了,就是前面那个目录,所以4.2.2上的蓝牙这里可以忽略。

    framework/base/core/java/android/bluetooth  

           这个目录里的代码更像一个桥梁,里面有供java层使用一些类,也有对应的aidl文件联系C、C++部分的代码,还是挺重要的。

    kernel\drivers\bluetoothBluetooth    

           具体协议实现。包括hci,hid,rfcomm,sco,SDP等协议

    kernel\net\bluetooth Linux kernel

           对各种接口的Bluetoothdevice的驱动。例如:USB接口,串口等,上面kernel这两个目录有可能看不到的,但一定会有的。

    external\bluetooth\bluedroid      BlueZ (应用空间协议),官方蓝牙协议栈。

    system\bluetoothBluetooth        适配层代码,和framework那个作用类似,是串联framework与blueZ的工具。

        大致代码分布就是这些,初步查看后让我们再来看下蓝牙的整体结构。


2.整体结构:

        这部分直接上图了,看着直观些。图中把JNI部分虽然在目前4.2的代码中在packages层,这里还是画在Framework层了,说明下希望

不要引起理解的误会。从图上可以感觉到整体流程和以前变化不大,所以流程方面的文章看4.1或更早的应该问题也不大。



 PS:上图关于蓝牙协议栈的说明有误,4.2里已经不再是bluez了,在些更正一下,当然协议栈这一部分还是要有的,新的协议栈看下面英文:


Android 4.2 introduces a new Bluetooth stack optimized for use with Android devices. The new Bluetooth stack developed in 

collaboration between Google and Broadcom replaces the stack based on BlueZ and provides improved compatibility and reliability.

google和broadcom合作开发了一个新蓝牙协议栈,老版本的兼容性问题在所难免了。在此感谢网友andger032的提醒。


3.常用类和名词解释:  

   \packages\apps\Settings\src\com\android\settings\bluetooth 目录下

     BluetoothEnabler.java   界面上蓝牙开启、关闭的开关就是它了, 

     BluetoothSettings.java  主界面,用于管理配对和连接设备

     LocalBluetoothManager.java  提供了蓝牙API上的简单调用接口,这里只是开始。

    CachedBluetoothDevice.java   描述蓝牙设备的类,对BluetoothDevice的再封装

    BluetoothPairingDialog.java  那个配对提示的对话框


  /packages/apps/Phone/src/com/android/phone/

    BluetoothPhoneService.java  在phone的目录肯定和电话相关了,蓝牙接听挂断电话会用到这个


 /packages/apps/Bluetooth/src/com/android/bluetooth/btservice/

        AdapterService.java    4.2后才有的代码,蓝牙打开、关闭、扫描、配对都会走到这里,其实更准确的说它替代了4.1之前的BluetoothService.java,原来的工作就由这个类来完成了。说到这里不能不说4.2蓝牙的目录变了,在4.1及以前的代码中packages层的代码只有opp协议相关应用的代码,也就是文件传输那部分,而4.2的代码应用层的代码则丰富了许多,按具体的蓝牙应用协议来区别,分为以下文件夹(这里一并对蓝牙一些名词作个简单解释):

       a2dp    蓝牙立体声,和蓝牙耳机听歌有关那些,另还有个avrcp--音频/视频远程控制配置文件,是用来听歌时暂停,上下歌曲选择的。
       btservice  这个前面AdapterService.java的描述大家应该能猜到一些,关于蓝牙基本操作的目录,一切由此开始。
       hdp      蓝牙关于医疗方面的应用 Bluetooth Health Device Profile
       hfp       和电话相关,蓝牙接听、挂断电话  Hands-free Profile
       hid      人机交互接口,蓝牙鼠标键盘什么的就是这个了
      opp     不多解释,以前就有。
      pan      描述了两个或更多个 Bluetooth 设备如何构成一个即时网络,和网络有关的还有串行端口功能(SPP),拨号网络功能(DUN)

      pbap    电话号码簿访问协议(Phonebook Access Profile)

       android 4.2的蓝牙应用层部分代码更丰富了,虽然有些目录还没具体代码,不过说不准哪个版本更新就有了,就像4.0添加了hdp医疗那部分一样。

另外原本在framework的JNI代码也被移到packages/apps/bluetooth当中。     

   /frameworks/base/core/java/android/bluetooth/目录下


      BluetoothA2dp.java A2DP的功能实现
     BluetoothAdapter.java 蓝牙action的定义,虚拟设备属性以及操作方法
     BluetoothAudioGateway.java 蓝牙语音网关
     BluetoothClass.java 蓝牙设备类型的定义
     BluetoothDevice.java 蓝牙设备属性
     BluetoothDevicePicker.java 定义远程蓝牙设备的特性,比如需要认证,设备类型
     BluetoothHeadset.java 定义蓝牙headset功能的属性以及接口
     BluetoothInputStream.java 蓝牙流接口的实现(输入流)
    BluetoothOutputStream.java 蓝牙流接口的实现(输出流)
    BluetoothServerSocket.java 蓝牙socket服务端具备的方法
    BluetoothSocket.java 蓝牙socket的封装
    BluetoothUuid.java 蓝牙uuid的定义以及uuid的解析

    以上java文件在使用具体功能会用到,现在只是简单描述下,至于具体使用在后续文章用到时再给出。同时代码说明部分也就写这些了

对于C、C++部分的代码一方面没看那么多,另一方面根据android JNI的命名习惯,大家找起来也很容易。


4.后续分析:

       前面从整体上描述蓝牙的基本知识,落实在具体的代码分析上,我们按几个主线功能来走,蓝牙的开关、搜索配对、蓝牙耳机与电话和文件传输,

这几个也算是蓝牙的常用必备功能了,所以在后续文章中将按着这个顺序来跟一下它们代码调用流程。希望可以让你快速的了解蓝牙,当然如果有失误

写错的地方,欢迎反馈,谢谢。

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

分类: Android的原生应用分析2013-05-31 22:03 13114人阅读 评论(11) 收藏 举报

bluetooth蓝牙startScanningAndroid4.2源码


        接上篇打开蓝牙继续,来一起看下蓝牙搜索的流程,触发蓝牙搜索的条件形式上有两种,一是在蓝牙设置界面开启蓝牙会直接开始搜索,另一个是先打开蓝牙开关在进入蓝牙设置界面也会触发搜索,也可能还有其它触发方式,但最后都要来到BluetoothSettngs.java的startScanning(),我们分析的起点也从这里开始,起步代码如下

[cpp] view plaincopy

  1. private void updateContent(int bluetoothState, boolean scanState) {  

  2.     if (numberOfPairedDevices == 0) {  

  3.          preferenceScreen.removePreference(mPairedDevicesCategory);  

  4.          if (scanState == true) {  

  5.              mActivityStarted = false;  

  6.              startScanning();  

  7.         } else<span style="font-family: Arial, Helvetica, sans-serif;">    ........</span>  

  8. }         

  9. private void startScanning() {  

  10.         if (!mAvailableDevicesCategoryIsPresent) {  

  11.             getPreferenceScreen().addPreference(mAvailableDevicesCategory);  

  12.         }  

  13.         mLocalAdapter.startScanning(true);  

  14.     }  


        其实在这里蓝牙搜索和打开流程是结构上是一致的,利用LocalBluetoothAdapter.java过渡到BluetoothAdapter.java再跳转至AdapterService.java要稍微留意下的是在这个过渡中startScaning()方法变成了startDiscovery()方法,看下代码:packages/apps/Settings/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java

[java] view plaincopy

  1. void startScanning(boolean force) {  

  2. if (!mAdapter.isDiscovering()) {  

  3.      if (!force) {  

  4.          // Don't scan more than frequently than SCAN_EXPIRATION_MS,  

  5.          // unless forced  

  6.          if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {  

  7.              return;  

  8.          }  

  9.          // If we are playing music, don't scan unless forced.  

  10.          A2dpProfile a2dp = mProfileManager.getA2dpProfile();  

  11.          if (a2dp != null && a2dp.isA2dpPlaying()) {  

  12.              return;  

  13.          }  

  14.      }  

  15. //这里才是我们最关注的,前面限制条件关注一下就行了  

  16.      if (mAdapter.startDiscovery()) {  

  17.          mLastScan = System.currentTimeMillis();  

  18.      }  

  19. }  

       BluetoothAdapter.java的那一段,路径 /frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java

[java] view plaincopy

  1. public boolean startDiscovery() {  

  2.     .............................  

  3.     AdapterService service = getService();  

  4.     if (service == nullreturn false;  

  5.     return service.startDiscovery();  

  6. }  

      这个service代码写得很明白AdapterService,转了一圈从framework又回到packages了,

      下面的代码路径自然是 :packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java,

[java] view plaincopy

  1. boolean startDiscovery() {  

  2.     enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,  

  3.             "Need BLUETOOTH ADMIN permission");  

  4.   

  5.     return startDiscoveryNative();  

  6. }  

      和打开蓝牙根本就是一个套路,上面的流程略过一小步,很简单的不写了,下面要怎么走,估计大家也都猜到了,JNI应该出场了,

       路径:/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

[cpp] view plaincopy

  1. static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) {                                                     

  2.     ALOGV("%s:",__FUNCTION__);                                  

  3.                                                                 

  4.     jboolean result = JNI_FALSE;                                

  5.     if (!sBluetoothInterface) return result;                    

  6.                                                                 

  7.     int ret = sBluetoothInterface->start_discovery();           

  8.     result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;  

  9.     return result;                                              

  10. }   

      在下面要去哪?稍微要动下脑筋,不过我们在上一篇android -- 蓝牙 bluetooth (二) 打开蓝牙已经说过怎么找了,注意android.mk文件,先找头文件,再找对应的实现C文件代码。就是现在回顾下,蓝牙打开和搜索的代码流程我们都看了,跳转都是一个套路,settings界面发起,LocalBluetoothAdapter.java过渡,去framework的转转(BluetoothAdapter.java)后回到packages的AdapterService.java,再走JNI来的external。流程就是这样的,相信类似的功能跳转(比如蓝牙配对,关闭蓝牙,停止扫描这些)大家都应该熟悉了,后面再有类似的功能就写函数名一笔带过了,还有这里要注意的就是这个start_discovery()实现代码的寻找,留意mk文件就是了,不复杂。小结结束,继续看代码    路径:/external/bluetooth/bluedroid/btif/src/bluetooth.c

[cpp] view plaincopy

  1. static int start_discovery(void)  

  2. {  

  3.     /* sanity check */  

  4.     if (interface_ready() == FALSE)  

  5.         return BT_STATUS_NOT_READY;  

  6.   

  7.     return btif_dm_start_discovery();  

  8. }     

        下面代码直接跳转就可以找到,路径external/bluetooth/bluedroid/btif/src/btif_dm.c  

        这个代码有点多,不过里面的信息也很多,所以连注释也一起保留的贴出来了,蓝牙的搜索实现并没有像蓝牙打开那样交由vendor厂商实现,在这里已经写出来了,仔细看下那些#if和#else,都是一些查询条件的调置,#if (BLE_INCLUDED == TRUE)   这个应该就google为蓝牙4.0 LE作的准备了,也算是今年google I/O大会上宣布即将支持蓝牙4.0低能耗版一个佐证吧,对于代码里面那些字符串的含义看这里好了external/bluetooth/bluedroid/bta/include/bta_api.h,一个头文件,大部分字符串和结构体的定义都在这了,多少还有些注释。

[cpp] view plaincopy

  1. bt_status_t btif_dm_start_discovery(void)                                    

  2. {                                                                            

  3.     tBTA_DM_INQ inq_params;                                                  

  4.     tBTA_SERVICE_MASK services = 0;                                          

  5.                                                                              

  6.     BTIF_TRACE_EVENT1("%s", __FUNCTION__);                                   

  7.     /* TODO: Do we need to handle multiple inquiries at the same time? */    

  8.                                                                              

  9.     /* Set inquiry params and call API */                                    

  10. #if (BLE_INCLUDED == TRUE)                                                   

  11.     inq_params.mode = BTA_DM_GENERAL_INQUIRY|BTA_BLE_GENERAL_INQUIRY;        

  12. #else                                                                        

  13.     inq_params.mode = BTA_DM_GENERAL_INQUIRY;                                

  14. #endif                                                                       

  15.     inq_params.duration = BTIF_DM_DEFAULT_INQ_MAX_DURATION;                  

  16.                                                                              

  17.     inq_params.max_resps = BTIF_DM_DEFAULT_INQ_MAX_RESULTS;                  

  18.     inq_params.report_dup = TRUE;                                            

  19.                                                                              

  20.     inq_params.filter_type = BTA_DM_INQ_CLR;                                 

  21.     /* TODO: Filter device by BDA needs to be implemented here */            

  22.                                                                              

  23.     /* Will be enabled to TRUE once inquiry busy level has been received */  

  24.     btif_dm_inquiry_in_progress = FALSE;                                     

  25.     /* find nearby devices */                                                

  26.     BTA_DmSearch(&inq_params, services, bte_search_devices_evt);             

  27.                                                                              

  28.     return BT_STATUS_SUCCESS;                                                

  29. }   

      BTA_DmSearch()方法是看起来是要搜索了,不过里面这个家伙bte_search_devices_evt才是真正干活的主力,所以我们先看它,在这个函数里

[cpp] view plaincopy

  1. static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)                                                                      {                                                                                                                    

  2.        UINT16 param_len = 0;                                                                                           

  3.                                                                                                                         

  4.        if (p_data)                                                                                                      

  5.            param_len += sizeof(tBTA_DM_SEARCH);                                                                         

  6.        /* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */  

  7.        switch (event)                                                                                                   

  8.        {                                                                                                                

  9.            case BTA_DM_INQ_RES_EVT:                                                                                     

  10.            {                                                                                                            

  11.                if (p_data->inq_res.p_eir)                                                                               

  12.                    param_len += HCI_EXT_INQ_RESPONSE_LEN;                                                               

  13.            }                                                                                                            

  14.            break;                                                                                                              

  15.           ..............................                                                                                                

  16.        }                                                                                                                

  17.        BTIF_TRACE_DEBUG3("%s event=%s param_len=%d", __FUNCTION__, dump_dm_search_event(event), param_len);             

  18.                                                                                                                         

  19.        /* if remote name is available in EIR, set teh flag so that stack doesnt trigger RNR */                          

  20.        if (event == BTA_DM_INQ_RES_EVT)                                                                                 

  21.            p_data->inq_res.remt_name_not_required = check_eir_remote_name(p_data, NULL, NULL);                          

  22.                                                                                                                         

  23.        btif_transfer_context (btif_dm_search_devices_evt , (UINT16) event, (void *)p_data, param_len,                   

  24.            (param_len > sizeof(tBTA_DM_SEARCH)) ? search_devices_copy_cb : NULL);                                       

  25.    }     

         在上面的这个函数里又有这个bte_search_devices_evt,在它里我们能看一个 HAL_CBACK,这是要往回发消息了,看下这个函数的全貌,说是全貌,不过还是只贴出一个case分支,太长了,大家还是自行还源码吧。到这里已经可以知道扫描到蓝牙设备的mac地址和设备名,那个bdcpy函数就是在解析mac地址,有了这些,蓝牙搜索是到应该在界面展示成果的时候了,开始回调,忘记代码路径了,这个函数都在这个文件里:  /external/bluetooth/bluedroid/btif/src/btif_dm.c

[cpp] view plaincopy

  1. static void btif_dm_search_devices_evt (UINT16 event, char *p_param)  

  2.   

  3.    tBTA_DM_SEARCH *p_search_data;  

  4.    BTIF_TRACE_EVENT2("%s event=%s", __FUNCTION__, dump_dm_search_event(event));  

  5.   

  6.    switch (event)  

  7.    {  

  8.        case BTA_DM_DISC_RES_EVT:  

  9.        {  

  10.            p_search_data = (tBTA_DM_SEARCH *)p_param;  

  11.            /* Remote name update */  

  12.            if (strlen((const char *) p_search_data->disc_res.bd_name))  

  13.            {  

  14.                bt_property_t properties[1];  

  15.                bt_bdaddr_t bdaddr;  

  16.                bt_status_t status;  

  17.   

  18.                properties[0].type = BT_PROPERTY_BDNAME;  

  19.                properties[0].val = p_search_data->disc_res.bd_name;  

  20.                properties[0].len = strlen((char *)p_search_data->disc_res.bd_name);  

  21.                bdcpy(bdaddr.address, p_search_data->disc_res.bd_addr);  

  22.   

  23.                status = btif_storage_set_remote_device_property(&bdaddr, &properties[0]);  

  24.                ASSERTC(status == BT_STATUS_SUCCESS, "failed to save remote device property", status);  

  25.                HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,  

  26.                                 status, &bdaddr, 1, properties);  

  27.            }  

  28.            /* TODO: Services? */  

  29.        }  

  30.        break;  

       一小段log,下面的文字就在上面的函数里打出来的,即便上面的写的函数没有,肯定也在附近了。

05-30 13:52:14.890  1578  2612 D bt-btif : bte_search_devices_evt event=BTA_DM_INQ_RES_EVT param_len=524
05-30 13:52:14.890  1578  2612 D bt-btif : search_devices_copy_cb: event=BTA_DM_INQ_RES_EVT
05-30 13:52:14.890  1578  2584 I bt-btif : btif_dm_search_devices_evt event=BTA_DM_INQ_RES_EVT
05-30 13:52:14.890  1578  2584 D bt-btif : btif_dm_search_devices_evt() ec:89:f5:ba:fb:03 device_type = 0x1

        当然回过头我们还要看下那个BTA_DmSearch(),看它的实现,更应该是起消息发送的作用,代码在/external/bluetooth/bluedroid/bta/dm/bta_dm_api.c,这个函数具体流程并没有看多少,当工具方法看了,有时间看看还是没坏处的。

[cpp] view plaincopy

  1. void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, tBTA_DM_SEARCH_CBACK *p_cback)  

  2. {  tBTA_DM_API_SEARCH    *p_msg;  

  3.     if ((p_msg = (tBTA_DM_API_SEARCH *) GKI_getbuf(sizeof(tBTA_DM_API_SEARCH))) != NULL)  

  4.     {  

  5.         memset(p_msg, 0, sizeof(tBTA_DM_API_SEARCH));  

  6.   

  7.         p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;  

  8.         memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ));  

  9.         p_msg->services = services;  

  10.         p_msg->p_cback = p_cback;  

  11.         p_msg->rs_res  = BTA_DM_RS_NONE;  

  12.         bta_sys_sendmsg(p_msg);  

  13.     }  

  14. }  

      看了上面方法后我们 要回去了看看,代码通过JNI下来的,回去也是看JNI的回调方法

[cpp] view plaincopy

  1. method_deviceFoundCallback = env->GetMethodID(jniCallbackClass, "deviceFoundCallback""([B)V");  

deviceFoundCallback方法最后会来java层的/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java

[java] view plaincopy

  1. void deviceFoundCallback(byte[] address) {  

  2.        // The device properties are already registered - we can send the intent  

  3.        // now  

  4.        BluetoothDevice device = getDevice(address);  

  5.        debugLog("deviceFoundCallback: Remote Address is:" + device);  

  6.        DeviceProperties deviceProp = getDeviceProperties(device);  

  7.        if (deviceProp == null) {  

  8.            errorLog("Device Properties is null for Device:" + device);  

  9.            return;  

  10.        }  

  11.   

  12.        Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);  

  13.        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);  

  14.        intent.putExtra(BluetoothDevice.EXTRA_CLASS,  

  15.                new BluetoothClass(Integer.valueOf(deviceProp.mBluetoothClass)));  

  16.        intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);  

  17.        intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);  

  18.   

  19.        mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);  

  20.    }  

        到这里就是给界面发广播,应用层收到广播显示出来,通过这个handle,这个handle可以在BluetoothEventManager.java的构造函数里找到,

[java] view plaincopy

  1. addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());  

  2.     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {  

  3.     @Override  

  4.     public void onReceive(Context context, Intent intent) {  

  5.         String action = intent.getAction();  

  6.         BluetoothDevice device = intent  

  7.                 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  

  8.   

  9.         Handler handler = mHandlerMap.get(action);  

  10.         if (handler != null) {  

  11.             handler.onReceive(context, intent, device);  

  12.         }  

  13.     }  

  14. };  

         这里handle对应要看DeviceFoundHandler,也就是下面贴出来的代码,

[java] view plaincopy

  1.  private class DeviceFoundHandler implements Handler {  

  2.     public void onReceive(Context context, Intent intent,  

  3.             BluetoothDevice device) {  

  4.        ........................  

  5.         // TODO Pick up UUID. They should be available for 2.1 devices.  

  6.         // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.  

  7.         CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);  

  8.         if (cachedDevice == null) {  

  9.             cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);  

  10.             Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "  

  11.                     + cachedDevice);  

  12.             // callback to UI to create Preference for new device  

  13.             dispatchDeviceAdded(cachedDevice);  

  14.         }  

  15.       ......................  

  16.     }  

  17. }  

        在if语句中dispatchDeviceAdded()向界面分发消息,最后处理消息的地方在这里,已经到settings应用里了

   /packages/apps/Settings/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java

[java] view plaincopy

  1. public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {                   

  2.    if (mDevicePreferenceMap.get(cachedDevice) != null) {                          

  3.        return;                                                                    

  4.    }                                                                              

  5.                                                                                   

  6.    // Prevent updates while the list shows one of the state messages              

  7.    if (mLocalAdapter.getBluetoothState() != BluetoothAdapter.STATE_ON) return;    

  8.                                                                                   

  9.    if (mFilter.matches(cachedDevice.getDevice())) {                               

  10.        createDevicePreference(cachedDevice);                                      

  11.    }                                                                              

  12. }                     

         上面代码中最后一个分支就是界面显示要做的事了,从settings界面开始再到settings界面显示出搜索到蓝牙结束,后面的代码不再写了,本文关心的东东到此结束。

[java] view plaincopy

  1.   void createDevicePreference(CachedBluetoothDevice cachedDevice) {  

  2.     BluetoothDevicePreference preference = new BluetoothDevicePreference(  

  3.             getActivity(), cachedDevice);  

  4.   

  5.     initDevicePreference(preference);  

  6.     mDeviceListGroup.addPreference(preference);  

  7.     mDevicePreferenceMap.put(cachedDevice, preference);  

  8. }  

        到目前为止,包括前面的打开流程分析,还仅是针对代码流程做的分析,对于蓝牙协议方面东西还没有涉及,比如蓝牙是如何发现其它蓝牙设备,这个流程究竟是怎么工作还不是很清楚,后续会尽量关注这些问题,估计看起来就没那么容易,欢迎有经验的朋友指点一二,当然对于本文不足,欢迎拍砖讨论。分享是快乐的,谢谢!


--------------modify 2013.6.2 21:08--------

更新蓝牙搜索返回后的跳转代码



本文转载自:http://blog.csdn.net/baimy1985/article/details/8892410

b
粉丝 0
博文 7
码字总数 153
作品 0
长春
私信 提问
三星galaxy j2曝光:配置不如国产手机

迈入了2018,手机市场也开始了崭新的发展。相信国内大多数朋友对于三星的 S系列、Note系列 以及C系列都有所了解,但是提及三星J系列 ,就不是那么熟悉了。事实上,三星J系列是三星每年销量的...

明美无限
2018/01/05
0
0
想了解物联网,那2018年这五大趋势值得一览

时代推动着科学技术的发展,物联网一直被认为是“下一个工业革命”,因为它对我们生活的方方面面均产生着或多或少的影响。 事实上,物联网革命已经悄然开始。 不信我说给你听听:能够无线控制...

昇润科技
2018/03/02
0
0
蓝牙模块是什么,一文对比分析并介绍BLE蓝牙的主要功能特点

在物联网产业中,蓝牙或许是无线通讯应用中最好的选择,原因是基于蓝牙技术受众面广阔,以低功耗、智能化、低成本等特性,在物联网应用市场中占据主要核心位置。而蓝牙模块正是集成了蓝牙功能...

昇润科技
2018/02/01
0
0
物联网时代,为啥说可穿戴设备越来越重要?其市场到底有多大?一文解答

随着物联网的发展,人们越来越感受它所带来的便利。日常的吃喝拉撒睡行坐卧走,物联网几乎逐渐嵌入到了我们的生活当中,而可穿戴设备是其中最重要的一角。 据某机构预测,2017年到2020年可穿...

昇润科技
2017/12/14
0
0
低功耗蓝牙心率监测仪解决方案

低功耗蓝牙带来的热潮,影响着智能设备的兴起与创新,并向着物联网领域延伸。我们知道,现在移动设备上使用的蓝牙大多是低功耗蓝牙,低功耗蓝牙(BLE)相较于传统蓝牙,BLE技术除了低功耗特性...

昇润科技
2017/11/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

CentOS 7系统增加swap

转载请注明文章出处:CentOS 7系统增加swap swap是位于磁盘上的特殊文件(或分区),属于“虚拟内存”的一部分。通俗点就是内存的备胎,内存充足的情况下,基本上没swap什么事(和设置有关)...

tlanyan
16分钟前
2
0
基于Prometheus和Grafana的监控平台 - 环境搭建

相关概念 微服务中的监控分根据作用领域分为三大类,Logging,Tracing,Metrics。 Logging - 用于记录离散的事件。例如,应用程序的调试信息或错误信息。它是我们诊断问题的依据。比如我们说...

JAVA日知录
56分钟前
5
0
PHP运行时全局构造体

struct _php_core_globals { zend_bool magic_quotes_gpc; // 是否对输入的GET/POST/Cookie数据使用自动字符串转义。 zend_bool magic_quotes_runtime; //是否对运行时从外部资源产生的数据使...

冻结not
58分钟前
4
0
webpack插件html-webpack-plugin

本文转载于:专业的前端网站→webpack插件html-webpack-plugin 1、插件安装 npm install html-webpack-plugin --save-dev 2、插件使用 webpack.config.js配置文件为: var htmlWebpackPlugin=...

前端老手
今天
6
0
数据挖掘

zhengchen1996
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部