android -- 蓝牙 bluetooth解读

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


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

[java]  view plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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的上电算在一起,这个也是要注意的,小心误判。



三:搜索蓝牙

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

[cpp]  view plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  1. method_deviceFoundCallback = env->GetMethodID(jniCallbackClass, "deviceFoundCallback""([B)V");  
deviceFoundCallback方法最后会来java层的/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java
[java]  view plain copy
  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 plain copy
  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 plain copy
  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应用里了
[java]  view plain copy
  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 plain copy
  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. }  

        到目前为止,包括前面的打开流程分析,还仅是针对代码流程做的分析,对于蓝牙协议方面东西还没有涉及,比如蓝牙是如何发现其它蓝牙设备,这个流程究竟是怎么工作还不是很清楚,后续会尽量关注这些问题,估计看起来就没那么容易


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值