藍牙基本概念以及Android中藍牙enable/discover流程分析

藍牙基本概念

兩種藍牙技術:Basic Rate (BR)和Low Energy(LE)
這兩種技術是不能互通的,也就是不能相互兼容。如果要確保和所有的藍牙設備互通,只能同時實現這兩種技術。

BR

Basic Rage是正宗的藍牙技術,包括可選的(optional)的EDR(Enhanced Data Rate)技術,以及Alternate(交替使用的)MAC層和PHY層擴展(簡稱AMP)。

LE

BR技術的進化路線,就是傳輸速率的加快。能量是守恆的,想要加快傳輸速率,代價就是要消耗更多的能量。再有些應用場景下,並不關心傳輸速率,反而關心功耗,這就是BLE產生的背景。

物理層

物理層負責提供數據傳輸的物理信道,藍牙的物理層分未Physical Channel 和Physiccal Links

Physical Channel

頻率範圍:2.400-2.4835GHz
BR/EDR物理信道定義:
1.ISM頻率範圍被分成79個Channel,每個Channel佔用1M的帶寬。在0 Channel和78 Channel設立gurad band(保護帶寬,Lower Guard Band爲2MHz,Upper Guard Band爲3.5MHz)
2.採用跳頻技術(hopping),也就是說摸一個物理信道並不是固定的佔用79個channel中的某一個,而是以一定的規律在跳動(該規律在技術上叫做“僞隨機碼”,就是假的隨機碼)。因此藍牙的物理信道也可以稱作跳頻信道(hopping channel);
3. BR/EDR技術定義了5種物理信道(跳頻信道),BR/EDR Basic Piconet Physical Channel(用於連接狀態的藍牙設備之間的通訊,使用全部79個頻點);BR/EDR Adapted Piconet Physical Channel(用於連接狀態下藍牙設備之間的通訊,使用79個跳頻點種的子集,但是跳頻數目不能少於20個);BR/EDR Page Scan Physical Channel(用於發現藍牙設備的發現操作Discovery);BR/EDR Inquriy Scan Physical Channel(用於藍牙設備的連接操作Connect);BR/EDR synchronization Scan Channel(用於無連接的廣播通訊).
4. 同一時刻,BT設備只能在其中一個物理信道上通信,爲了支持多個並行的操作,藍牙系統採用分時方式,即不同的時間點採用不同的信道
LE的物理信道定義:
1.ISM頻率範圍被分成40個channel,每個channel佔用2M的帶寬,在0channel和39channel之外設立guradband(保護帶寬,Lower Guard爲2MHz,Upper Guard Band爲3.5MHz)
2.LE技術定義了2種物理信道,LE Piconet channed(用於連接狀態的藍牙設備件的通訊,和BR/EDR一樣,採用調頻技術,但是只會在40個頻點種的37箇中進行跳頻)和LE advertisement Broadcast Channel(用於在設備間進行無連接的廣播通訊,這些廣播通信用於藍牙設備的返現,連接操作,亦可用於無連接的數據傳輸)
AMP爲高速數據傳輸設計的技術,其物理規範直接採用802.11的PHY規範,主要有如下特點:AMP物理信道只有一種,即AMP Physical Channel,主要用於已連接設備之間的數據通訊。

Physical Links(物理鏈路)
由上面的描述可知,藍牙協議爲BR/EDR, LE和AMP三種技術定義了8種類型的物理信道。物理鏈路是對這些物理信道的進一步封裝。

AMP Physical Channel /LE Piconet Channel/LE Advertisement Broadcast Channel均有一個對應的Physical Link,分別是Amp Physical Link/LE Active Physical Link/LE Advertising Physical Channel。

邏輯層

邏輯層的主要功能是再已連接的藍牙設備之間,基於物理鏈路,建立邏輯信道。
藍牙邏輯信道的劃分依據是傳輸類型,主要包含下面三類:
1.用於管理底層物理鏈路的控制類傳輸,包括AMP-C,ACL-C,PSB-C,LE-C,ADVB-C
2.傳輸用戶數據的用戶類傳輸,包括AMP-U,ACL-U,PSB-U,LE-U,ADVB-U
3.其他比較特殊的傳輸類型,包括流式傳輸stream,PBD(Profile Broadcast Data)
以上每種Logic Link都會在下層對應一個Logical Transport,這些Logical Transport具有一些屬性,如流控,應答,重傳機制等。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-TTgSQloV-1587893425132)(./images/1573809760433.png)]

Android Bluetooth系統框圖

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-mTyIIFJH-1587893425133)(./images/Bluetooth.png)]
com_android_bluetooth_btservice_AdapterService.cpp中的sBluetoothInterface對應下面文件
hardware/realtek/bluetooth/rtkbt/code/bluedroid/btif/src/bluetooth.c

藍牙源碼分析
typedef struct {
    int cmd_fdr, cmd_fdw;//通過socketpair獲得
    int poll_count;//記錄加入到poll中的fd個數,或者說是pollslot的個數
    poll_slot_t ps[MAX_POLL];//最大值爲8,
    int psi[MAX_POLL]; //index of poll slot
    volatile pid_t thread_id;
    btsock_signaled_cb callback;
    btsock_cmd_cb cmd_callback;
    int used; //標誌着這個thread slot有沒有被使用,最大值爲8
} thread_slot_t;
藍牙的構成

在android系統裏面,個人感覺是比較複雜的,起碼比無線複雜。下面就以amlogic sdk爲例來進行分析。總體來說,android中藍牙由以下部分組成:
1.libbt-vendor.so。這部分應該是由硬件決定的。比如說這個藍牙芯片是通過usb接入到主芯片還是通過uart接入到主芯片。這次代碼閱讀時通過uart的方式接入到主芯片的。代碼位置爲:hardware/realtek/bluetooth/rtkbt/code/libbt-vendor
2.bluetooth.default。這部分包含了藍牙協議部分;代碼位置爲:hardware/realtek/bluetooth/rtkbt/
3.Bluetooth.apk,通過jni與bluetooth.default進行數據的交換。代碼位置爲:packages/apps/Bluetooth。
4.Android System中藍牙有關的service & adapter。在打開藍牙時,將會通過AIDL(IBluetooth)連接上步中的Service。同時提供統一的api接口供apk進行調用;
5.Settings,也就是設置。在這部分主要是提供一個界面,供用戶進行操作。

隨手筆記

在設置中的藍牙部分的入口爲:BluetoothSettings.java.這部分主要就是對系統中的BluetoothAdapter進行了封裝,然後監聽藍牙的狀態進行更新以及負責ui部分的邏輯。這部分比較簡單就不進行源碼分析了

1.打開藍牙過程:

sequenceDiagram
BluetoothManagerService.java ->>BluetoothManagerService.java:handler send MESSAGE_ENABLE msg
BluetoothManagerService.java ->>BluetoothManagerService.java:handleEnable.bind service and call enable 
BluetoothManagerService.java ->>AdapterService.java:enable()
AdapterService.java ->>AdapterState.java:handler send USER_TURN_ON msg
AdapterState.java ->> AdapterService.java:OffState call processStart
Note right of AdapterService.java:processStart 這個函數裏面啓動profile對應的service並且發送廣播,init jnicallback回調函數,
JniCallbacks.java ->> AdapterService.java:processProfileServiceStateChanged,
Note right of AdapterService.java:更新profile狀態,如果都啓動的話,啓動native 層
AdapterService.java ->> AdapterState.java:Handler 發送STARTED消息
AdapterState.java ->> AdapterService.java:enableNative
AdapterService.java->>com_android_bluetooth_btservice_AdapterService.cpp:JNI調用 enableNative
com_android_bluetooth_btservice_AdapterService.cpp ->> bluetooth.c:調用enable()
bluetooth.c ->> btif_core.c:btif_enable_bluetooth
btif_core.c ->> bte_main.c:bte_main_enable
Note right of bte_main.c:初始化hci,創建一個線程運行btu_task,這個task有兩個作用:1.初始話底層的一些協議:2.創建一個循環,讀取mbox中的消息進行處理

在這裏面需要注意的是,底層產生的信息是通過JniCallbacks這個java類進行反饋上來的。
關於藍牙硬件初始化的邏輯結構參考如下圖所示:
在這裏插入圖片描述

藍牙Discovery流程分析
BluetoothAdapter初始化分析

1.在Android中,對藍牙進行操作都是通過BluetoothAdapter進行的。那麼先看一下它的初始化函數

BluetoothAdapter.java
    public static synchronized BluetoothAdapter getDefaultAdapter() {
        if (sAdapter == null) {
            IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);//獲得系統中的Bluetooth Manager Service
            if (b != null) {
                IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
                sAdapter = new BluetoothAdapter(managerService);
            } else {
                Log.e(TAG, "Bluetooth binder is null");
            }
        }
        return sAdapter;
    }

    /**
     * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
     */
    BluetoothAdapter(IBluetoothManager managerService) {

        if (managerService == null) {
            throw new IllegalArgumentException("bluetooth manager service is null");
        }
        try {
            mService = managerService.registerAdapter(mManagerCallback);//註冊callback函數,這裏需要注意的是返回的實際上是AdapterService代理。對藍牙的操作都是使用這個代理進行的。
        } catch (RemoteException e) {Log.e(TAG, "", e);}
        mManagerService = managerService;
        mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>();
        mHandler = new Handler(Looper.getMainLooper());
    }

BluetoothManagerService.java
    public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
        //Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
        //msg.obj = callback;
        //mHandler.sendMessage(msg);
        synchronized(mConnection) {
            boolean added = mCallbacks.register(callback);
            return mBluetooth;
        }
    }
	
private class BluetoothHandler extends Handler {
···
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
                      mBluetooth = IBluetooth.Stub.asInterface(service);
···
}

之前在介紹打開藍牙的分析中提到過,打開藍牙的時候Bind了AdapterService。從上面可以看出,BluetoothAdapter實際上是使用Bluetooth.apk中的AdapterService進行藍牙操作的。
下面言歸正傳,在Settings中的Bluetooth設置界面點擊搜索按鍵時就會按照下面的順序進行調用:
LocalBluetoothAdapter ->BluetoothAdapter ->AdapterService.下面就是調用native層中的startDiscoveryNative函數。
在JNI中 對應函數包含在文件:com_android_bluetooth_btservice_AdapterService.cpp中。

static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) {
   ALOGV("%s:",__FUNCTION__);

   jboolean result = JNI_FALSE;
   if (!sBluetoothInterface) return result;

   int ret = sBluetoothInterface->start_discovery();
   result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
   return result;
}

在這個函數中涉及到的sBluetoothInterface就是藍牙構成裏面介紹的bluetooth.default的入口。
那麼bluetooth.default是如何加載的呢?

static void classInitNative(JNIEnv* env, jclass clazz) {
···
   char value[PROPERTY_VALUE_MAX];
   property_get("bluetooth.mock_stack", value, "");

   const char *id = (strcmp(value, "1")? BT_STACK_MODULE_ID : BT_STACK_TEST_MODULE_ID);

   err = hw_get_module(id, (hw_module_t const**)&module);//只是加載對應的庫文件

   if (err == 0) {
       hw_device_t* abstraction;
   	//實際上調的是Bluetooth.c中的open_bluetooth_stack
       err = module->methods->open(module, id, &abstraction);
       if (err == 0) {
           bluetooth_module_t* btStack = (bluetooth_module_t *)abstraction;
           sBluetoothInterface = btStack->get_bluetooth_interface();
       } else {
          ALOGE("Error while opening Bluetooth library");
       }
   } else {
       ALOGE("No Bluetooth Library found");
   }
···
}

具體的是根據什麼去加載的細節問題這裏就不在詳細介紹了,就是根據name進行加載的。
那麼sBluetoothInterface對應的實現文件是bluetooth.c
繼續分析discovery流程

bluetooth.c
static int start_discovery(void)
{
    /* sanity check */
    if (interface_ready() == FALSE)
        return BT_STATUS_NOT_READY;

    return btif_dm_start_discovery();
}
bt_status_t btif_dm_start_discovery(void){

···
BTA_DmSearch(&inq_params, services, bte_search_devices_evt);
···
}
void BTA_DmSearch(tBTA_DM_INQ *p_dm_inq, tBTA_SERVICE_MASK services, tBTA_DM_SEARCH_CBACK *p_cback)
{

    tBTA_DM_API_SEARCH    *p_msg;

    if ((p_msg = (tBTA_DM_API_SEARCH *) GKI_getbuf(sizeof(tBTA_DM_API_SEARCH))) != NULL)
    {
        memset(p_msg, 0, sizeof(tBTA_DM_API_SEARCH));

        p_msg->hdr.event = BTA_DM_API_SEARCH_EVT;
        memcpy(&p_msg->inq_params, p_dm_inq, sizeof(tBTA_DM_INQ));
        p_msg->services = services;
        p_msg->p_cback = p_cback;
        p_msg->rs_res  = BTA_DM_RS_NONE;
        bta_sys_sendmsg(p_msg);
    }

}

看到就是填充一個信息相關的機構,表明事件類型爲BTA_DM_API_SEARCH_EVT,之後發送這個消息。
這裏面就涉及到了藍牙中的消息是如何循環處理的問題了。這部分還是有點複雜,後面有時間再寫出來分析。
總之會根據調用到bta_dm_act.c中的bta_dm_search_start函數。

void bta_dm_search_start (tBTA_DM_MSG *p_data){
···
    result.status = BTM_StartInquiry(   (tBTM_INQ_PARMS*)&p_data->search.inq_params,
                        bta_dm_inq_results_cb,
                        (tBTM_CMPL_CB*) bta_dm_inq_cmpl_cb);
···
}
btm_inq.c
tBTM_STATUS BTM_StartInquiry (tBTM_INQ_PARMS *p_inqparms, tBTM_INQ_RESULTS_CB *p_results_cb,
                              tBTM_CMPL_CB *p_cmpl_cb){
···
        else if ((status = btm_ble_start_inquiry((UINT8)(p_inqparms->mode & BTM_BLE_INQUIRY_MASK),
                                            p_inqparms->duration)) != BTM_CMD_STARTED)
        {
···
}
btm_ble_gap.c
tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8   duration){
···
	if (btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE))
···
}
hciblecmds.c
BOOLEAN btsnd_hcic_ble_set_scan_enable (UINT8 scan_enable, UINT8 duplicate)
{
    BT_HDR *p;
    UINT8 *pp;

    if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE)) == NULL)
        return (FALSE);

    pp = (UINT8 *)(p + 1);

    p->len    = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE;
    p->offset = 0;

    UINT16_TO_STREAM (pp, HCI_BLE_WRITE_SCAN_ENABLE);
    UINT8_TO_STREAM  (pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE);

    UINT8_TO_STREAM (pp, scan_enable);
    UINT8_TO_STREAM (pp, duplicate);

    btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID,  p);
    return (TRUE);
}
void btu_hcif_send_cmd (UINT8 controller_id, BT_HDR *p_buf){
···
HCI_CMD_TO_LOWER(p_buf);
···
}

這個宏定義爲:

#define HCI_CMD_TO_LOWER(p)         bte_main_hci_send((BT_HDR *)(p), BT_EVT_TO_LM_HCI_CMD)
bte_main.c
void bte_main_hci_send (BT_HDR *p_msg, UINT16 event){
···
        if (bt_hc_if)
            bt_hc_if->transmit_buf((TRANSAC)p_msg, \
                                       (char *) (p_msg + 1), \
                                        p_msg->len);
···
}

transmit_buf這個函數實際上就是動用了uart的寫入函數,將數據寫入到節點中。至此discover命令的發送就結束了。

歡迎使用 {小書匠}(xiaoshujiang)編輯器,您可以通過 小書匠主按鈕>模板 裏的模板管理來改變新建文章的內容。

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