【轉】android 藍牙

      前段時間在 網上看了一些關於android藍牙的文章,發現大部分是基於老版本(4.1之前含4.1)的源碼,雖然無礙瞭解藍牙的基本原理和工做流程,但對着4.2.2的代碼看起來老是有些遺憾。因此針對4.2.2版本代碼整理下相關知識,固然藍牙工做的主幹流程是沒有變的,上電、加載驅動這些動做少不了的,只是這些功能的實現代碼位置變了很多。但願本文可讓你們對android4.2的藍牙部分代碼有一個初步的瞭解。java

 

        正文開始前,先明確代碼版本:android  jellyBean 4.2.2,後續的藍牙相關文章一樣如此。android

        另推薦個源碼在線閱讀網址 http://androidxref.com/,已經知道的童鞋無視這行吧。shell

 

        入手一個新的模塊或應用,固然首先要知道它都有什麼了,與它相關的代碼在那裏,因此先一塊兒看下藍牙代碼分佈吧。api

 

1. 代碼分佈:

 

    packages/apps/Bluetooth/     網絡

         看這路徑確定是藍牙應用方面的代碼了,主要是關於藍牙應用協議的表現代碼,包括opp、hfp、hdp、a2dp、pan等等,這些名詞後面再解釋。app

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

          4.2之後這個目錄雖然還有了,但裏面代碼已經轉移到應用層了,就是前面那個目錄,因此4.2.2上的藍牙這裏能夠忽略。socket

    framework/base/core/java/android/bluetooth  ide

           這個目錄裏的代碼更像一個橋樑,裏面有供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 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 == null) return 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. }  

        到目前爲止,包括前面的打開流程分析,還僅是針對代碼流程作的分析,對於藍牙協議方面東西尚未涉及,好比藍牙是如何發現其它藍牙設備,這個流程到底是怎麼工做還不是很清楚,後續會盡可能關注這些問題,估計看起來就沒那麼容易,歡迎有經驗的朋友指點一二,固然對於本文不足,歡迎拍磚討論。分享是快樂的,謝謝!

相關文章
相關標籤/搜索