GATT scan的流程


BLE scan 在bluedroid的實現中,有兩個接口:一個是discovery,一個是ble observe,這兩者有什么區別呢?

 這里追了一下代碼發現,inquiry 是上層調用search 相關的接口的實現函數,ble observe 是調用GATT scan的接口的實現函數,這篇文章分析一下,在調用GATT 接口實現scan的流程。

GATT的服務代碼邏輯在哪里實現的呢?其核心代碼的實現是bluedroid里面,但是,上層的應用是不可能直接調用協議棧的代碼的,其實在bluedroid上面還會進行封裝一個GATT的服務,其實現在package/app/Bluetooth下面的GattService.java,這里面實現了關於GATT相關的各種接口,應用層的代碼通過binder 調用綁定到這些接口,並完成一系列的調用。

看一下GattService.java的實現:

    /**
     * Handlers for incoming service calls
     */
    private static class BluetoothGattBinder extends IBluetoothGatt.Stub implements IProfileServiceBinder {
        private GattService mService;

        public BluetoothGattBinder(GattService svc) {
            mService = svc;
        }

        public boolean cleanup()  {
            mService = null;
            return true;
        }
...
        @Override
        public void startScan(int appIf, boolean isServer, ScanSettings settings,
                List<ScanFilter> filters, List storages, String callingPackage) {
            GattService service = getService();
            if (service == null) return;
            service.startScan(appIf, isServer, settings, filters, storages, callingPackage);
        }

        public void stopScan(int appIf, boolean isServer) {
            GattService service = getService();
            if (service == null) return;
            service.stopScan(new ScanClient(appIf, isServer));
        }

 ...
    }

可以看到其代碼中實現了一個BluetoothGattBinder,這個上層應用程序在綁定完成的時候,會得到這個binder接口。我們也可以看到,這個binder實現的也是GATT相關的基本的接口。

分析一下startScan接口,發現其是調用到另一個service 的startScan的接口,那這個service 是哪里來的呢?其實這個service就是GATTService本身,在initBinder的時候,將this 指針傳入。

    protected IProfileServiceBinder initBinder() {
        return new BluetoothGattBinder(this);
    }

 我們的重點是分析startScan 這個接口的流程,現在我們看GATTService是如何實現這個接口的

    void startScan(int appIf, boolean isServer, ScanSettings settings,
            List<ScanFilter> filters, List<List<ResultStorageDescriptor>> storages,
            String callingPackage) {
...
        final ScanClient scanClient = new ScanClient(appIf, isServer, settings, filters, storages);
... mScanManager.startScan(scanClient); }

新建了一個scanClient 類,並將此類傳入到mScanManager.startScan中:

    void startScan(ScanClient client) {
        sendMessage(MSG_START_BLE_SCAN, client);
    }

此時的代碼走到了ScanManager.java里面,不管上層的代碼如何流轉,我們知道,最后肯定還是調用到JNI 的接口,然后到達bluedroid里面,接着看:

    // Handler class that handles BLE scan operations.
    private class ClientHandler extends Handler {
...
        @Override
        public void handleMessage(Message msg) {
            ScanClient client = (ScanClient) msg.obj;
            switch (msg.what) {
                case MSG_START_BLE_SCAN://處理事件
                    handleStartScan(client);
                    break;
                case MSG_STOP_BLE_SCAN:
                    handleStopScan(client);
                    break;
...
            }
        }

        void handleStartScan(ScanClient client) {//處理scan的實現函數
            Utils.enforceAdminPermission(mService);...
            // Begin scan operations.
            if (isBatchClient(client)) {
                mBatchClients.add(client);
                mScanNative.startBatchScan(client);
            } else {
                mRegularScanClients.add(client);
                mScanNative.startRegularScan(client);
                if (!mScanNative.isOpportunisticScanClient(client)) {
                    mScanNative.configureRegularScanParams();
                }
            }
        }

...
    }

這邊分析一下startBatchScan是vendor command 相關,那么一般都是調用到mScanNative.startRegularScan,這邊已經調用到了native層面,具體看看其實現:

        void startRegularScan(ScanClient client) {
            if (isFilteringSupported() && mFilterIndexStack.isEmpty()
                    && mClientFilterIndexMap.isEmpty()) {
                initFilterIndexStack();
            }
            if (isFilteringSupported()) {
                configureScanFilters(client);
            }
            // Start scan native only for the first client.
            if (numRegularScanClients() == 1) {
                gattClientScanNative(true);
            }
        }

這邊繼續往下調用到gattClientScanNative(true) :這里調用到JNI 層,其實現在文件com_android_bluetooth_gatt.cpp

static void gattClientScanNative(JNIEnv* env, jobject object, jboolean start)
{
    if (!sGattIf) return;
    sGattIf->client->scan(start);
}

到這里就很明確了,其最終調用的是sGattIf中client 的scan的接口,那其接口是怎么樣的呢?

其是在bluetooth.c里面通過get_profile_interface 來獲取GATT的interface的,

static const btgatt_interface_t btgattInterface = {
    sizeof(btgattInterface),
    btif_gatt_init,
    btif_gatt_cleanup,
    &btgattClientInterface,
    &btgattServerInterface,
};

中的client 的接口如下:

const btgatt_client_interface_t btgattClientInterface = {
    btif_gattc_register_app,
    btif_gattc_unregister_app,
    btif_gattc_scan,
    btif_gattc_open,
    btif_gattc_close,
    btif_gattc_listen,
...
}

那其實調用的就是:btif_gattc_scan 

static bt_status_t btif_gattc_scan( bool start )
{
    CHECK_BTGATT_INIT();
    btif_gattc_cb_t btif_cb;
    return btif_transfer_context(btgattc_handle_event, start ? BTIF_GATTC_SCAN_START : BTIF_GATTC_SCAN_STOP,
                                 (char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);
}

這里將處理的流程transfer到bt_jni_workqueue_thread線程了,從這個線程的名字來看,主要是處理從JNI 下來的事件。看看具體做了什么:

static void btgattc_handle_event(uint16_t event, char* p_param)
{
...
    btif_gattc_cb_t* p_cb = (btif_gattc_cb_t*) p_param;
    if (!p_cb) return;
    switch (event)
    {
...
        case BTIF_GATTC_SCAN_START:
            btif_gattc_init_dev_cb();
            BTA_DmBleObserve(TRUE, 0, bta_scan_results_cb);//調用的是這個函數
            break;
...

繼續看BTA_DmBleObserve,注意第一個參數是true,表示開始scan,第二個參數是持續時間,0表示一直持續:

extern void BTA_DmBleObserve(BOOLEAN start, UINT8 duration,
                             tBTA_DM_SEARCH_CBACK *p_results_cb)
{
    tBTA_DM_API_BLE_OBSERVE   *p_msg;
    APPL_TRACE_API("BTA_DmBleObserve:start = %d ", start);
    if ((p_msg = (tBTA_DM_API_BLE_OBSERVE *) GKI_getbuf(sizeof(tBTA_DM_API_BLE_OBSERVE))) != NULL)
    {
        memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_OBSERVE));
        p_msg->hdr.event = BTA_DM_API_BLE_OBSERVE_EVT;//向bt_workqueue_thread發送BTA_DM_API_BLE_OBSERVE_EVT
     p_msg->start = start;
     p_msg->duration = duration; 
p_msg
->p_cback = p_results_cb;
bta_sys_sendmsg(p_msg);
}
}

 看了一下代碼發現bt_workqueue_thread 是處理事件的主線程,bta_sys_sendmsg(p_msg); 這個函數是將消息發送到btu_bta_msg_queue,而這個queue是和bt_workqueue_thread綁定的,隊列里面的消息都會在這個線程里面處理:

void bta_sys_sendmsg(void *p_msg)
{
    if (btu_bta_msg_queue)
        fixed_queue_enqueue(btu_bta_msg_queue, p_msg);
}

那現在 關於scan的event 的處理已經來到了另一個線程:bt_workqueue_thread,那么該隊列里面有了數據線程如何處理?

  fixed_queue_register_dequeue(btu_bta_msg_queue,
      thread_get_reactor(bt_workqueue_thread),
      btu_bta_msg_ready,
      NULL);

根據上面的代碼,我們知道會調用到btu_bta_msg_ready:

void btu_bta_msg_ready(fixed_queue_t *queue, UNUSED_ATTR void *context) {
    BT_HDR *p_msg = (BT_HDR *)fixed_queue_dequeue(queue);//消息出列
    bta_sys_event(p_msg);
}

也就是先讓消息處理,然后再調用bta_sys_event來處理:那至此我們知道,凡是調用到bta_sys_sendmsg,那最終處理的函數都是bta_sys_event來處理,而這個函數的處理方式也是一種dispatch的機制:

void bta_sys_event(BT_HDR *p_msg)
{
...
    /* get subsystem id from event */
    id = (UINT8) (p_msg->event >> 8);
    /* verify id and call subsystem event handler */
    if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL))
    {
        freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg);
    }
...
}

其思想就是找到該事件對應的處理函數,這些event的高8bit 是屬於事件的類型,或者稱為主事件,而event的低8 bit是事件的子類,或者稱為子事件。處理的過程是先通過主事件找到事件的處理函數handler(當然肯定是事先注冊好的),然后在該處理函數中處理子事件。

那該事件的處理函數handler 是什么呢?

/*******************************************************************************
**
** Function         bta_sys_register
**
** Description      Called by other BTA subsystems to register their event
**                  handler.
**
**
** Returns          void
**
*******************************************************************************/
void bta_sys_register(UINT8 id, const tBTA_SYS_REG *p_reg)
{
    bta_sys_cb.reg[id] = (tBTA_SYS_REG *) p_reg;
    bta_sys_cb.is_reg[id] = TRUE;
}

這里是注冊的地方,根據函數的注釋,是BTA 的子系統注冊自己的event 的處理函數 時候所調用的。下圖很容易看出有哪些模塊調用這個注冊函數

 對於BTA_DM_API_BLE_OBSERVE_EVT 這個event 可知其主事件是BTA_ID_DM = 1 ,其注冊 的地方在BTA_EnableBluetooth:

bta_sys_register (BTA_ID_DM, &bta_dm_reg );

那現在我們知道,其處理的函數的入口就是bta_dm_reg:

static const tBTA_SYS_REG bta_dm_reg =
{
    bta_dm_sm_execute,
    bta_dm_sm_disable
};
BOOLEAN bta_dm_sm_execute(BT_HDR *p_msg)
{
    UINT16  event = p_msg->event & 0x00ff;//取出子事件
    /* execute action functions */
    if(event < BTA_DM_NUM_ACTIONS)
    {
        (*bta_dm_action[event])( (tBTA_DM_MSG*) p_msg);
    }
    return TRUE;
}

這里我們發現,其設計還是比較巧妙,每個event 對應的處理函數,是在一個大的數組中,用事件的子事件(低8bit)來尋址,這有點分頁的意味了。

這里該事件真正的處理函數是bta_dm_ble_observe:並調用如下代碼:

((status = BTM_BleObserve(TRUE, p_data->ble_observe.duration,
                            bta_dm_observe_results_cb, bta_dm_observe_cmpl_cb))!= BTM_CMD_STARTED)

這里我們發現,函數調用已經進入到stack里面了,BTM_BleObserve,看看其具體實現,這里我們應該還記得,這里的第二個參數傳進來的時候是0:

tBTM_STATUS BTM_BleObserve(BOOLEAN start, UINT8 duration,
                           tBTM_INQ_RESULTS_CB *p_results_cb, tBTM_CMPL_CB *p_cmpl_cb)
{
...
    UINT32 scan_interval = !p_inq->scan_interval ? BTM_BLE_GAP_DISC_SCAN_INT : p_inq->scan_interval;//發現參數是優先使用inquiry的參數
    UINT32 scan_window = !p_inq->scan_window ? BTM_BLE_GAP_DISC_SCAN_WIN : p_inq->scan_window;
...
    if (start)
    {
        /* shared inquiry database, do not allow observe if any inquiry is active */
        if (BTM_BLE_IS_OBS_ACTIVE(btm_cb.ble_ctr_cb.scan_activity))//如果有observe 直接返回
        {
            BTM_TRACE_ERROR("%s Observe Already Active", __func__);
            return status;
        }
        btm_cb.ble_ctr_cb.p_obs_results_cb = p_results_cb;
        btm_cb.ble_ctr_cb.p_obs_cmpl_cb = p_cmpl_cb;
        status = BTM_CMD_STARTED;
        /* scan is not started */
        if (!BTM_BLE_IS_SCAN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity))//沒有其他的scan 行為才繼續執行
        {
            /* allow config of scan type */
            p_inq->scan_type = (p_inq->scan_type == BTM_BLE_SCAN_MODE_NONE) ?
                                                    BTM_BLE_SCAN_MODE_ACTI: p_inq->scan_type;
...
            p_inq->scan_duplicate_filter = BTM_BLE_DUPLICATE_DISABLE;
            status = btm_ble_start_scan();//開始scan
        }
        if (status == BTM_CMD_STARTED)
        {
            btm_cb.ble_ctr_cb.scan_activity |= BTM_LE_OBSERVE_ACTIVE;
            if (duration != 0)
                /* start observer timer */
                btu_start_timer (&btm_cb.ble_ctr_cb.obs_timer_ent, BTU_TTYPE_BLE_OBSERVE, duration);//這里注意,如果duration設置了,那么經過一定時間就會超時,然后會停止scan,如果沒有設置這個值,就會一直scan
        }
    }

這里注意一下代碼中有這樣一句注釋:shared inquiry database, do not allow observe if any inquiry is active,說明oberve的優先級還是很低的。從代碼中也 可以看出只有當沒有其他的scan的行為,observe才會繼續進行。另外對於scan type 是active還是passive的問題,當p_inq->scan_interval 沒有設置的話,就使用active,否則就使用當前的設置值。從這也可以看出,active 是優先被使用的。

最后看看btm_ble_start_scan的實現,這個就很簡單了,直接通過HCI 來發送命令了:

 

tBTM_STATUS btm_ble_start_scan(void)
{
    tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var;
    tBTM_STATUS status = BTM_CMD_STARTED;

    /* start scan, disable duplicate filtering */
    if (!btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, p_inq->scan_duplicate_filter))//HCI command
    {
        status = BTM_NO_RESOURCES;
    }
    else
    {
        if (p_inq->scan_type == BTM_BLE_SCAN_MODE_ACTI)
            btm_ble_set_topology_mask(BTM_BLE_STATE_ACTIVE_SCAN_BIT);//更新拓撲
        else
            btm_ble_set_topology_mask(BTM_BLE_STATE_PASSIVE_SCAN_BIT);
    }
    return status;
}

 

scan結果的回報:


前面注冊的時候,我們看到

        case BTIF_GATTC_SCAN_START:
            btif_gattc_init_dev_cb();
            BTA_DmBleObserve(TRUE, 0, bta_scan_results_cb);
            break;

其回調函數是bta_scan_results_cb,當搜索結果上來的時候,該函數會被調用:

static void bta_scan_results_cb (tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)
{
...
    switch (event)
    {
        case BTA_DM_INQ_RES_EVT:
        {
...
        }
        break;

        case BTA_DM_INQ_CMPL_EVT:
        {
...
    }
    btif_transfer_context(btif_gattc_upstreams_evt, BTIF_GATT_OBSERVE_EVT,
                                 (char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);
}

代碼中針對BTA_DM_INQ_RES_EVT和BTA_DM_INQ_CMPL_EVT 都會有自己的一些處理,但是最后都要經過btif_gattc_upstreams_evt的處理,並且是event = BTIF_GATT_OBSERVE_EVT 

看具體的代碼實現:

        case BTIF_GATT_OBSERVE_EVT:
        {
            btif_gattc_cb_t *p_btif_cb = (btif_gattc_cb_t*) p_param;
...
             BTIF_STORAGE_FILL_PROPERTY(&properties,
                        BT_PROPERTY_TYPE_OF_DEVICE, sizeof(dev_type), &dev_type);
             btif_storage_set_remote_device_property(&(p_btif_cb->bd_addr), &properties);

            HAL_CBACK(bt_gatt_callbacks, client->scan_result_cb,
                      &p_btif_cb->bd_addr, p_btif_cb->rssi, p_btif_cb->value);
            break;
        }

上面做的主要就是保存設備的屬性,以及向上層匯報相關的設備信息:使用bt_gatt_callbacks中的 client->scan_result_cb,接口。

那這個接口是哪里來的呢?

static bt_status_t btif_gatt_init( const btgatt_callbacks_t* callbacks )
{
    bt_gatt_callbacks = callbacks;
    return BT_STATUS_SUCCESS;
}

發現是gatt 模塊init的時候賦值的,那么我們就知道其callback 來源於JNI層面:

static const btgatt_callbacks_t sGattCallbacks = {
    sizeof(btgatt_callbacks_t),
    &sGattClientCallbacks,
    &sGattServerCallbacks
};
static const btgatt_client_callbacks_t sGattClientCallbacks = {
    btgattc_register_app_cb,
    btgattc_scan_result_cb,//此函數
    btgattc_open_cb,
...

通過JNI方法的回調:

void btgattc_scan_result_cb(bt_bdaddr_t* bda, int rssi, uint8_t* adv_data)
{
...
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onScanResult
        , address, rssi, jb);//調用method_onScanResult
... checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); }

這個方法的實現是在java層,那到底對應於哪一個文件呢?

int register_com_android_bluetooth_gatt(JNIEnv* env)
{
    int register_success =
        jniRegisterNativeMethods(env, "com/android/bluetooth/gatt/ScanManager$ScanNative",
                sScanMethods, NELEM(sScanMethods));
    register_success &=
        jniRegisterNativeMethods(env, "com/android/bluetooth/gatt/AdvertiseManager$AdvertiseNative",
                sAdvertiseMethods, NELEM(sAdvertiseMethods));
    return register_success &
        jniRegisterNativeMethods(env, "com/android/bluetooth/gatt/GattService",
                sMethods, NELEM(sMethods));
}

發現sMethods對應於"com/android/bluetooth/gatt/GattService" ,那我們知道其實現是在GattService.java里面。看具體的實現:

    void onScanResult(String address, int rssi, byte[] adv_data) {
        if (VDBG) Log.d(TAG, "onScanResult() - address=" + address
                    + ", rssi=" + rssi);
        List<UUID> remoteUuids = parseUuids(adv_data);
        for (ScanClient client : mScanManager.getRegularScanQueue()) {
            if (client.uuids.length > 0) {
                int matches = 0;
                for (UUID search : client.uuids) {
                    for (UUID remote: remoteUuids) {
                        if (remote.equals(search)) {
                            ++matches;
                            break; // Only count 1st match in case of duplicates
                        }
                    }
                }

                if (matches < client.uuids.length) continue;
            }

            if (!client.isServer) {
                ClientMap.App app = mClientMap.getById(client.clientIf);
                if (app != null) {
                    BluetoothDevice device = BluetoothAdapter.getDefaultAdapter()
                            .getRemoteDevice(address);
                    ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(adv_data),
                            rssi, SystemClock.elapsedRealtimeNanos());
                    // Do no report if location mode is OFF or the client has no location permission
                    // PEERS_MAC_ADDRESS permission holders always get results
                    if (hasScanResultPermission(client) && matchesFilters(client, result)) {
                        try {
                            ScanSettings settings = client.settings;
                            if ((settings.getCallbackType() &
                                    ScanSettings.CALLBACK_TYPE_ALL_MATCHES) != 0) {
                                app.callback.onScanResult(result);
                            }
                        } catch (RemoteException e) {
                            Log.e(TAG, "Exception: " + e);
                            mClientMap.remove(client.clientIf);
                            mScanManager.stopScan(client);
                        }
                    }
                }
            } else {
                ServerMap.App app = mServerMap.getById(client.clientIf);
                if (app != null) {
                    try {
                        app.callback.onScanResult(address, rssi, adv_data);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Exception: " + e);
                        mServerMap.remove(client.clientIf);
                        mScanManager.stopScan(client);
                    }
                }
            }
        }
    }

到這里呢,協議棧就將關於設備的信息上傳到bluetooth.apk了,在這個函數里面,我們可以看到其最終調用到app.callback.onScanResult(address, rssi, adv_data);,這邊應該是回調到更上一層應用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM