藍牙開啓源碼分析

首先介紹下幾個重要的類:

在Android 4.3中引入了一個新的類BluetoothManager,它是一個high level manager,被用於”to obtain an instance of an BluetoothAdapter and conduct overall Bluetooth Management“。

LocalBluetoothManager.java  提供了藍牙API上的簡單調用接口,本機藍牙設備管理,開啓關閉,搜索等等)。

BluetoothEnabler.java 界面的點擊和狀態文字的顯示。

流程爲:Settings的BluetoothEnabler類(對應於UI上看到的Bluetooth開關),得到代表local device的BluetoothAdapter。

看源碼:


可以看出BluetoothEnabler在構造裏會先調用 LocalBluetoothManager.getInstance(context),在getInstance裏會調用init()函數,BluetoothAdapter.getDefaultAdapter()獲得藍牙設備的句柄,如果當前沒有藍牙設備則返回null。 源碼中manager可以對本地藍牙進行管理,並且manager湖區得到mLocalAdapter(本地藍牙適配器)。同時設置藍牙適配器的事件過濾mIntentFilter。

即:BluetoothEnabler->LocalBluetoothManager->BluetoothAdapter;

這裏的BluetoothAdapter:framework封裝的類,提供本地藍牙設備的配置,包括開啓藍牙,搜索周圍藍牙設備,設置本地藍牙可見性。

enable藍牙:

1、開啓藍牙的service

從界面上看藍牙開關就是設置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這個類了。

2、Enable藍牙

當我們開啓藍牙的按鈕的時候會觸發一個事件:

看下代碼

[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)纔是真正的主角,即開始開啓enable,代碼如下:

[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;  
這裏先獲取服務器端的接口,也就是將客戶端與服務器進行了連接,這個時候可以認爲sAdapter爲服務器的代理。總結下:初始化完畢會監聽checkbox的狀態,當點擊checkbox會調用 LocalBluetoothManager.setBluetoothEnabled(enable)函數。調用mAdapter.enable()到 BluetoothService.enable()開啓EnableThread線程,進行打開操作,同時調到 CachedBluetoothDeviceManager.onBluetoothStateChanged來讀取上次關閉之前搜索到device。其中(BluetoothManagerService利用Binder機制會去connect AdapterService,最終會導致AdapterService::enable()被調用。BluetoothManagerService還會向AdapterService註冊callback函數,用於接收Adapter State Change消息。)

接着來說,此時我們更關心mAdapter.enable()的後續操作。

其中在這期間還做了些事情,AdapterService維護着一個狀態機。

1、AdapterService維護着一個狀態機AdapterState,所有工作都是通過驅動狀態機來完成的。AdapterState收到AdapterService發過來的USER_TURN_ON消息,就會調用AdapterService::processStart()來啓動Profie Services的初始化和Bluetooth hardware enable process。此時Bluetooth Adapter的狀態是BluetoothAdapter.STATE_TURNING_ON

2、每一個profile都有一個service。每個profile service啓動完成後,都會通知AdapterService。當AdapterService::processProfileServiceStateChanged()確認所有的profile services都啓動完成了,就會給狀態機AdapterState發AdapterState.STARTED消息。

3、狀態機AdapterState::PendingCommandState::processMessage()收到AdapterState.STARTED消息後就立刻調用AdapterService::enableNative()。

4、AdapterService::enableNative()就是用來enable Bluetooth的Bluetooth JNI接口。enableNative()會調用Bluetooth HAL的enable()。

5、Bluedroid用btif_enable_bluetooth()來實現了Bluetooth HAL的enable()。

6、當Bluedroid真正完成了enable Bluetooth hardware,就通過btif_enable_bluetooth_evt()中的HAL_CBACK調用Bluetooth JNI的adapter_state_change_callback(),這樣就把BT_STATE_ON消息傳遞給了狀態機AdapterState。

7、AdapterState會把Bluetooth Adapter的狀態轉換到BluetoothAdapter.STATE_ON,並通過AdapterState::notifyAdapterStateChanged()通知AdapterService。

8、AdapterService::updateAdapterState()會通過callback函數通知BluetoothManagerService,Adapter狀態改變了。

9、BluetoothManagerService確認狀態發生了改變就會發出一個BluetoothAdapter.ACTION_STATE_CHANGE的intent。

10、Settings的BluetoothEnabler收到這個intent之後,就會去更新UI上Bluetooth開關的狀態。
流程圖如下:




繼續分析的話:

那就是去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

  1.     static const bt_interface_t bluetoothInterface = {  
  2.     sizeof(bt_interface_t),  
  3.     init,  
  4.     enable,  
  5.     disable,  
  6.     .............  
  7.     start_discovery,  
  8.     cancel_discovery,  
  9.     create_bond,  
  10.     remove_bond,  
  11.     cancel_bond,  
  12.    ...............  
  13. };  
原來在這裏,說下怎麼找到,直接跳轉是不成了,看這個文件夾下的mk文件,那裏面有libhardware目錄是編譯的時候要用到,這個多半在hardware目錄裏,在這裏面很快可以看到bluetooth.h,那裏面有最我們要找的結構體定義,頭文件找到了,再找同名C文件就快了,好了繼續吧看下enable()裏是怎麼實現的

  1. static int enable( void )  
  2. {  
  3.     ALOGI("enable");  
  4.   
  5.     /* sanity check */  
  6.     if (interface_ready() == FALSE)  
  7.         return BT_STATUS_NOT_READY;  
  8.   
  9.     return btif_enable_bluetooth();  
  10. }  
又是一個新函數,直接跳轉,比起剛纔的尋覓這太幸福了
  1. bt_status_t btif_enable_bluetooth(void)  
  2. {  
  3.     BTIF_TRACE_DEBUG0("BTIF ENABLE BLUETOOTH");  
  4.   
  5.     if (btif_core_state != BTIF_CORE_STATE_DISABLED)  
  6.     {  
  7.         ALOGD("not disabled\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那部分就好了,
  1. void bte_main_enable(uint8_t *local_addr)  
  2. {  
  3.     APPL_TRACE_DEBUG1("%s", __FUNCTION__);  
  4.     ........................  
  5.   
  6. #if (defined (BT_CLEAN_TURN_ON_DISABLED) && BT_CLEAN_TURN_ON_DISABLED == TRUE)  
  7.         APPL_TRACE_DEBUG1("%s  Not Turninig Off the BT before Turninig ON", __FUNCTION__);  
  8.   
  9. #else  
  10.         /* toggle chip power to ensure we will reset chip in case 
  11.            a previous stack shutdown wasn't completed gracefully */  
  12.         bt_hc_if->set_power(BT_HC_CHIP_PWR_OFF);  
  13. #endif  
  14.         bt_hc_if->set_power(BT_HC_CHIP_PWR_ON);  
  15.   
  16.         bt_hc_if->preload(NULL);  
  17.     }  
  18.   
  19.      .............................  
  20. }  

路徑在這裏/external/bluetooth/bluedroid/hci/src/bt_hci_bdroid.c,看看set_power裏面有什麼,快到頭了

  1. static void set_power(bt_hc_chip_power_state_t state)  
  2. {  
  3.     int pwr_state;  
  4.   
  5.     BTHCDBG("set_power %d", state);  
  6.   
  7.     /* Calling vendor-specific part */  
  8.     pwr_state = (state == BT_HC_CHIP_PWR_ON) ? BT_VND_PWR_ON : BT_VND_PWR_OFF;  
  9.   
  10.     if (bt_vnd_if)  
  11.         bt_vnd_if->op(BT_VND_OP_POWER_CTRL, &pwr_state);  
  12.     else  
  13.         ALOGE("vendor lib is missing!");  
  14. }  
        這下又有新東西了bt_vnd_if,這個是什麼,bt_vendor_interface_t *bt_vnd_if=NULL;和剛纔的bt_interface_t 一樣,我們希望可以找到它的初始化,那樣就可以繼續跟蹤了,不過看到下面的代碼和註釋,在源碼中我們要絕望了。路徑:/external/bluetooth/bluedroid/hci/include/bt_vendor_lib.h

  1. /* Entry point of DLib -- 
  2.  *      Vendor library needs to implement the body of bt_vendor_interface_t 
  3.  *      structure and uses the below name as the variable name. HCI library 
  4.  *      will use this symbol name to get address of the object through the 
  5.  *      dlsym call. 
  6.  */  
  7. extern const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE;  
  8.   
  9. bt_vendor_interface_t *bt_vnd_if=NULL;  

 google定義好了接口,具體實現要看vendor廠商來做了,這後面怎麼實現就看各家芯片商怎麼寫了,肯定各有不同,而且這一部分代碼一般是不會公開,當然授權購買後除外了。所以在4.2的源碼中我們只跟到這裏了,那後面會做什麼呢,加載驅動和上電這兩項肯定要有了,打開藍牙沒這兩步怎麼行,類似下面的字符串

  1. static const char* BT_DRIVER_MODULE_PATH =    "/system/lib/modules/mbt8xxx.ko";  
  2. static const char* BT_DRIVER_MODULE_NAME =     "bt8xxx";  
  3. static const char* BT_DRIVER_MODULE_INIT_ARG = " init_cfg=";  
  4. static const char* BT_DRIVER_MODULE_INIT_CFG_PATH = "bt_init_cfg.conf";  

在有類似下面的動作,insmod加載驅動,rfkill控制上下電,具體廠商具體做法也不同。

  1. ret = insmod(BT_DRIVER_MODULE_PATH, arg_buf);  
  2. ret = system("/system/bin/rfkill block all");  




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章