android5.0(Lollipop) BLE Peripheral深入理解系統篇之初體驗

http://blog.csdn.net/lansefeiyang08/article/details/46505921

上週花了兩天時間簡單試了一下android L BLE  Peripheral和Central角色的應用開發。基本功能也差不多可以實現,那接下來我們就要來看一下系統裏,android L是如何加入Peripheral角色,工作原理到底是什麼樣子的,SDK裏的接口怎麼用才能實現我們想要的功能,android協議裏對一些BLE參數設定是什麼,下面我們來一下看一下。

關於Peripheral角色的實現,我會按照應用開發的流程來講解,簡單講就是從啓動到工作,系統裏的GATT是如何完成Peripheral的。此篇文章可能不是應用開發工程師關注的,但是對於和真正的BLE外設聯調,有些功能瞭解一下還是很有幫助的。當然後面我會繼續更新Central角色的實現,對所有人都會有幫助,因爲BLE外設的流行,手機Central角色遇到的問題也會越多,想了解原理的讓你也會越多,敬請期待。好了,我們繼續Peripheral的工作。

可能有人會有疑惑,爲什麼我開始寫博客是從Peripheral開始而不是應用更廣泛的Central。其實我的想法很簡單,知己知彼,百戰不殆。想深入瞭解Central,最好還是先了解它的搭檔Peripheral,雖然android系統很少用到,但是既然Android L更新了,那就肯定有他亮點和用處。好了我們正式開始瞭解整個流程。還是和以前一樣,代碼+解析架構。由於牽扯的東西比較多,爲了減少每篇篇幅,我計劃分兩次來完成。

對於藍牙標準的東西,比如檢查是否支持藍牙、是否支持BLE我就不講了,因爲不管是傳統藍牙還是BLE,其實都是一樣的。那我們就從獲得BluetoothAdapter開始。

如果你開發過SPP,那麼你應該會熟悉如下代碼:

<span style="font-size:14px;">        // Get local Bluetooth adapter
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();</span>
那現在就有一點改變了,BLE獲得的BluetoothAdapter並不用BluetoothAdapter裏的方法,而是用瞭如下代碼:
<span style="font-size:14px;">		final BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
		mBluetoothAdapter = mBluetoothManager.getAdapter();</span>

你可能會有疑惑,爲什麼會有如此差別呢?實際上BluetoothManager這個類是專門爲GATT和GATT SERVER服務的,最終通過BluetoothManager獲得的mBluetoothAdapter最終也是調用了BluetoothAdapter裏的getDefaultAdapter。好吧,你應該發現了,實際上這兩個類是一樣的,但是BluetoothManager類裏方法還是比較好用,一個是getConnectionState(BluetoothDevice device, int profile)用來獲得遠端BLE設備的連接狀態;另外一種是getConnectedDevices(int profile)用來獲得某種profile的已連接設備;還有一個可能會用到的是openGattServer(Context context,BluetoothGattServerCallback callback),此方法在android L之前就已經存在,且更新了Advertiser接口也並沒有棄用,說明此方法肯定有它的作用(Nordic開發的BLE應用,是用OpenGattServer啓動這個BLE應用的,此做法的好處暫時不詳,不過我猜是爲了使用Find Me功能,如果有人知道,可以告訴我。

獲得了關鍵的bluetoothadapter,剩下來就是要實例化BluetoothLeAdvertiser:

<span style="font-size:14px;">mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();</span>
讓我們看看Adapter裏getBluetoothLeAdvertiser到底做了什麼:
<span style="font-size:14px;">    public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
        if (getState() != STATE_ON) {
            return null;
        }
        if (!isMultipleAdvertisementSupported()) {
            return null;
        }
        synchronized(mLock) {
            if (sBluetoothLeAdvertiser == null) {
                sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
            }
        }
        return sBluetoothLeAdvertiser;
    }</span>
你可以看到,實際上就做了兩件事,一件事是判斷藍牙有沒有打開,判斷支持不支持BLE  Advertise模式,這兩種情況失敗都會返回null,所以如果你在開發應用時,一定要先把藍牙是否打開判斷了,如果再執行這段代碼返回null,那就是你本地設備不支持BLE Advertise了;第二件事是實例化了BluetoothLeAdvertiser,最後返回實例化的對象。接下來我們繼續往下看:
<span style="font-size:14px;">    public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) {
        mBluetoothManager = bluetoothManager;
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        mHandler = new Handler(Looper.getMainLooper());
    }</span>
這裏是實例化代碼,你會看到爲了拿到BluetoothAdapter,代碼裏又執行了一遍此類。可能有些人會有疑問,這樣執行多遍,不會拿到的BluetoothAdapter會不會不是同一個呀,實際上是這樣的,google默認只支持一個BlueoothAdapter,所以你執行多遍獲得也是你本地唯一的Adapter。

實例化結束了,發現這個都沒有跑到系統裏去。那我們繼續往下看應用開發流程。

接下來是要實例化AdvertiseSettings喝AdvertiseData兩個類,這兩個類都是在準備最後一步廣播所需要的參數,也沒有真正的走進藍牙協議裏,所以我們就直接看最後一步廣播:

<span style="font-size:14px;">mBluetoothLeAdvertiser.startAdvertising(createAdvSettings(true, 0), createAdvertiseData(), mAdvertiseCallback);</span>
上一篇的文章已經把應用代碼貼出來了,所以我們還是沿用之前的代碼,我們來看看startAdvertising到底幹了什麼:
<span style="font-size:14px;">    public void startAdvertising(AdvertiseSettings settings,
            AdvertiseData advertiseData, final AdvertiseCallback callback) {
        startAdvertising(settings, advertiseData, null, callback);
    }</span>
發現它調用了自己類的另一個startAdvertising方法,並且多了一個參數scanResponse:
<span style="font-size:14px;">    public void startAdvertising(AdvertiseSettings settings,
            AdvertiseData advertiseData, AdvertiseData scanResponse,
            final AdvertiseCallback callback) {
        synchronized (mLeAdvertisers) {
            BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
            if (callback == null) {
                throw new IllegalArgumentException("callback cannot be null");
            }
            if (!mBluetoothAdapter.isMultipleAdvertisementSupported()) {
                postStartFailure(callback,
                        AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED);
                return;
            }
            if (totalBytes(advertiseData) > MAX_ADVERTISING_DATA_BYTES ||
                    totalBytes(scanResponse) > MAX_ADVERTISING_DATA_BYTES) {
                postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);
                return;
            }
            if (mLeAdvertisers.containsKey(callback)) {
                postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
                return;
            }

            IBluetoothGatt gatt;
            try {
                gatt = mBluetoothManager.getBluetoothGatt();
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
                postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
                return;
            }
            AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,
                    scanResponse, settings, gatt);
            wrapper.startRegisteration();
        }
    }</span>
這裏需要注意幾個點:

(1)你必須實例化一個回調函數

(2)廣播最大數據字節數:當前廣播的最大數據字節數是31Bytes

(3)獲得了gatt的接口

(4)實例化AdvertiseCallbackWraper,調用startRegisteration方法。

那我們現在依次看這幾個關鍵點:

(1)回調函數AdvertiseCallback,此回調類會收到兩個回調方法onStartSuccess(AdvertiseSettings settingsInEffect)和onStartFailure(int errorCode),由於上一次應用篇已經講了,這裏就不過多的講述了。

(2)最大字節數沒有什麼好講的,大家注意這些細節就好

(3)gatt接口,這裏我們需要注意一下mBluetoothManager的定義:

<span style="font-size:14px;">   private final IBluetoothManager mBluetoothManager;</span>
那我們看看IBluetoothManager的實現:
<span style="font-size:14px;">/**
 * System private API for talking with the Bluetooth service.
 *
 * {@hide}
 */
interface IBluetoothManager
{
    ……
    IBluetoothGatt getBluetoothGatt();
    ……
}
</span>
原來它關聯了IBluetoothGatt,那我們繼續去追蹤IBluetoothGatt,裏面的接口都是Gatt的隱藏接口,他會在(4)中被用到:
<span style="font-size:14px;">/**
 * API for interacting with BLE / GATT
 * @hide
 */
interface IBluetoothGatt {
    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);

    void startScan(in int appIf, in boolean isServer, in ScanSettings settings, in List<ScanFilter> filters, in List scanStorages);
    void stopScan(in int appIf, in boolean isServer);
    void flushPendingBatchResults(in int appIf, in boolean isServer);
    void startMultiAdvertising(in int appIf, in AdvertiseData advertiseData, in AdvertiseData scanResponse,
                           in AdvertiseSettings settings);
    void stopMultiAdvertising(in int appIf);
    void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback);
    void unregisterClient(in int clientIf);
    void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport);
    void clientDisconnect(in int clientIf, in String address);
    void refreshDevice(in int clientIf, in String address);
    void discoverServices(in int clientIf, in String address);
    void readCharacteristic(in int clientIf, in String address, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId,
                            in int charInstanceId, in ParcelUuid charId, in int authReq);
    void writeCharacteristic(in int clientIf, in String address, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId,
                            in int charInstanceId, in ParcelUuid charId, in int writeType, in int authReq, in byte[] value);
    void readDescriptor(in int clientIf, in String address, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId,
                            in int charInstanceId, in ParcelUuid charId, in int descrInstanceId, in ParcelUuid descrUuid, in int authReq);
    void writeDescriptor(in int clientIf, in String address, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId,
                            in int charInstanceId, in ParcelUuid charId, in int descrInstanceId, in ParcelUuid descrId,
                            in int writeType, in int authReq, in byte[] value);
    void registerForNotification(in int clientIf, in String address, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId,
                            in int charInstanceId, in ParcelUuid charId, in boolean enable);
    void beginReliableWrite(in int clientIf, in String address);
    void endReliableWrite(in int clientIf, in String address, in boolean execute);
    void readRemoteRssi(in int clientIf, in String address);
    void configureMTU(in int clientIf, in String address, in int mtu);
    void connectionParameterUpdate(in int clientIf, in String address, in int connectionPriority);

    void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback);
    void unregisterServer(in int serverIf);
    void serverConnect(in int servertIf, in String address, in boolean isDirect, in int transport);
    void serverDisconnect(in int serverIf, in String address);
    void beginServiceDeclaration(in int serverIf, in int srvcType, in int srvcInstanceId, in int minHandles,
                            in ParcelUuid srvcId, boolean advertisePreferred);
    void addIncludedService(in int serverIf, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId);
    void addCharacteristic(in int serverIf, in ParcelUuid charId, in int properties, in int permissions);
    void addDescriptor(in int serverIf, in ParcelUuid descId, in int permissions);
    void endServiceDeclaration(in int serverIf);
    void removeService(in int serverIf, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId);
    void clearServices(in int serverIf);
    void sendResponse(in int serverIf, in String address, in int requestId, in int status, in int offset, in byte[] value);
    void sendNotification(in int serverIf, in String address, in int srvcType, in int srvcInstanceId, in ParcelUuid srvcId,
                            in int charInstanceId, in ParcelUuid charId, in boolean confirm, in byte[] value);
}
</span>
(4)那我們就看看最後一步到底是做了什麼,設備就開始廣播了呢?實例化了AdvertiseCallbackWrapper:
<span style="font-size:14px;">        public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
                AdvertiseData advertiseData, AdvertiseData scanResponse,
                AdvertiseSettings settings,
                IBluetoothGatt bluetoothGatt) {
            mAdvertiseCallback = advertiseCallback;
            mAdvertisement = advertiseData;
            mScanResponse = scanResponse;
            mSettings = settings;
            mBluetoothGatt = bluetoothGatt;
            mClientIf = 0;
        }</span>
這裏注意兩點,第一點是mClientIf,這裏的值大家在調試的時候要了解:      

        // mClientIf 0: not registered
        // -1: scan stopped
        // >0: registered and scan started

第二個是mBluetoothGatt,這裏的接口來源實際就是調用了IBluetoothGatt的所有接口。

實例化完了之後就是調用startRegisteration(),實現代碼如下:

<span style="font-size:14px;"> public void startRegisteration() {
            synchronized (this) {
                if (mClientIf == -1) return;

                try {
                    UUID uuid = UUID.randomUUID();
                    mBluetoothGatt.registerClient(new ParcelUuid(uuid), this);
                    wait(LE_CALLBACK_TIMEOUT_MILLIS);
                } catch (InterruptedException | RemoteException e) {
                    Log.e(TAG, "Failed to start registeration", e);
                }
                if (mClientIf > 0 && mIsAdvertising) {
                    mLeAdvertisers.put(mAdvertiseCallback, this);
                } else if (mClientIf <= 0) {
                    // Post internal error if registration failed.
                    postStartFailure(mAdvertiseCallback,
                            AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
                } else {
                    // Unregister application if it's already registered but advertise failed.
                    try {
                        mBluetoothGatt.unregisterClient(mClientIf);
                        mClientIf = -1;
                    } catch (RemoteException e) {
                        Log.e(TAG, "remote exception when unregistering", e);
                    }
                }
            }
        }</span>
這裏實際就是真正的去啓動BLE工作了。首先去調用了registerClient:
<span style="font-size:14px;">        public void registerClient(ParcelUuid uuid, IBluetoothGattCallback callback) {
            GattService service = getService();
            if (service == null) return;
            service.registerClient(uuid.getUuid(), callback);
        }</span>
然後又去調用了自己內部的registerClient函數:
<span style="font-size:14px;">    void registerClient(UUID uuid, IBluetoothGattCallback callback) {
        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");

        if (DBG) Log.d(TAG, "registerClient() - UUID=" + uuid);
        mClientMap.add(uuid, callback);
        gattClientRegisterAppNative(uuid.getLeastSignificantBits(),
                                    uuid.getMostSignificantBits());
    }</span>
看到這裏的函數,你應該就瞭解馬上就要進入JNI了, gattClientRegisterAppNative對應的就是JNI文件裏的   {"gattClientRegisterAppNative", "(JJ)V", (void *) gattClientRegisterAppNative},下面我們來看一下
gattClientRegisterAppNative的實現是怎樣的:
<span style="font-size:14px;">static void gattClientRegisterAppNative(JNIEnv* env, jobject object,
                                        jlong app_uuid_lsb, jlong app_uuid_msb )
{
    bt_uuid_t uuid;

    if (!sGattIf) return;
    set_uuid(uuid.uu, app_uuid_msb, app_uuid_lsb);
    sGattIf->client->register_client(&uuid);
}</span>
看到這裏的C++代碼,也就意味着馬上就要進入協議棧。要找到register_client函數,那我們必須先去找sGattIf。

sGattIf定義如下:

<span style="font-size:14px;">static const btgatt_interface_t *sGattIf = NULL;</span>
btgatt_interface_t的定義如下:
<span style="font-size:14px;">/** Represents the standard Bluetooth GATT interface. */
typedef struct {
    /** Set to sizeof(btgatt_interface_t) */
    size_t          size;

    /**
     * Initializes the interface and provides callback routines
     */
    bt_status_t (*init)( const btgatt_callbacks_t* callbacks );

    /** Closes the interface */
    void (*cleanup)( void );

    /** Pointer to the GATT client interface methods.*/
    const btgatt_client_interface_t* client;

    /** Pointer to the GATT server interface methods.*/
    const btgatt_server_interface_t* server;
} btgatt_interface_t;</span>
看到這裏,第二個成員變量我們也知道是啥了,但是我們還需要繼續追蹤,去繼續看btgatt_client_interface_t是如何定義的,
<span style="font-size:14px;">typedef struct {
    /** Registers a GATT client application with the stack */
    bt_status_t (*register_client)( bt_uuid_t *uuid );

    /** Unregister a client application from the stack */
    bt_status_t (*unregister_client)(int client_if );

    /** Start or stop LE device scanning */
    bt_status_t (*scan)( bool start );

    /** Create a connection to a remote LE or dual-mode device */
    bt_status_t (*connect)( int client_if, const bt_bdaddr_t *bd_addr,
                         bool is_direct, int transport );

    /** Disconnect a remote device or cancel a pending connection */
    bt_status_t (*disconnect)( int client_if, const bt_bdaddr_t *bd_addr,
                    int conn_id);

    /** Start or stop advertisements to listen for incoming connections */
    bt_status_t (*listen)(int client_if, bool start);

    /** Clear the attribute cache for a given device */
    bt_status_t (*refresh)( int client_if, const bt_bdaddr_t *bd_addr );

    /**
     * Enumerate all GATT services on a connected device.
     * Optionally, the results can be filtered for a given UUID.
     */
    bt_status_t (*search_service)(int conn_id, bt_uuid_t *filter_uuid );

    /**
     * Enumerate included services for a given service.
     * Set start_incl_srvc_id to NULL to get the first included service.
     */
    bt_status_t (*get_included_service)( int conn_id, btgatt_srvc_id_t *srvc_id,
                                         btgatt_srvc_id_t *start_incl_srvc_id);

    /**
     * Enumerate characteristics for a given service.
     * Set start_char_id to NULL to get the first characteristic.
     */
    bt_status_t (*get_characteristic)( int conn_id,
                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *start_char_id);

    /**
     * Enumerate descriptors for a given characteristic.
     * Set start_descr_id to NULL to get the first descriptor.
     */
    bt_status_t (*get_descriptor)( int conn_id,
                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
                    btgatt_gatt_id_t *start_descr_id);

    /** Read a characteristic on a remote device */
    bt_status_t (*read_characteristic)( int conn_id,
                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
                    int auth_req );

    /** Write a remote characteristic */
    bt_status_t (*write_characteristic)(int conn_id,
                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
                    int write_type, int len, int auth_req,
                    char* p_value);

    /** Read the descriptor for a given characteristic */
    bt_status_t (*read_descriptor)(int conn_id,
                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
                    btgatt_gatt_id_t *descr_id, int auth_req);

    /** Write a remote descriptor for a given characteristic */
    bt_status_t (*write_descriptor)( int conn_id,
                    btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id,
                    btgatt_gatt_id_t *descr_id, int write_type, int len,
                    int auth_req, char* p_value);

    /** Execute a prepared write operation */
    bt_status_t (*execute_write)(int conn_id, int execute);

    /**
     * Register to receive notifications or indications for a given
     * characteristic
     */
    bt_status_t (*register_for_notification)( int client_if,
                    const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id,
                    btgatt_gatt_id_t *char_id);

    /** Deregister a previous request for notifications/indications */
    bt_status_t (*deregister_for_notification)( int client_if,
                    const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id,
                    btgatt_gatt_id_t *char_id);

    /** Request RSSI for a given remote device */
    bt_status_t (*read_remote_rssi)( int client_if, const bt_bdaddr_t *bd_addr);

    /** Setup scan filter params */
    bt_status_t (*scan_filter_param_setup)(int client_if, int action, int filt_index, int feat_seln,
                                      int list_logic_type, int filt_logic_type, int rssi_high_thres,
                                      int rssi_low_thres, int dely_mode, int found_timeout,
                                      int lost_timeout, int found_timeout_cnt);


    /** Configure a scan filter condition  */
    bt_status_t (*scan_filter_add_remove)(int client_if, int action, int filt_type,
                                   int filt_index, int company_id,
                                   int company_id_mask, const bt_uuid_t *p_uuid,
                                   const bt_uuid_t *p_uuid_mask, const bt_bdaddr_t *bd_addr,
                                   char addr_type, int data_len, char* p_data, int mask_len,
                                   char* p_mask);

    /** Clear all scan filter conditions for specific filter index*/
    bt_status_t (*scan_filter_clear)(int client_if, int filt_index);

    /** Enable / disable scan filter feature*/
    bt_status_t (*scan_filter_enable)(int client_if, bool enable);

    /** Determine the type of the remote device (LE, BR/EDR, Dual-mode) */
    int (*get_device_type)( const bt_bdaddr_t *bd_addr );

    /** Set the advertising data or scan response data */
    bt_status_t (*set_adv_data)(int client_if, bool set_scan_rsp, bool include_name,
                    bool include_txpower, int min_interval, int max_interval, int appearance,
                    uint16_t manufacturer_len, char* manufacturer_data,
                    uint16_t service_data_len, char* service_data,
                    uint16_t service_uuid_len, char* service_uuid);

    /** Configure the MTU for a given connection */
    bt_status_t (*configure_mtu)(int conn_id, int mtu);

    /** Request a connection parameter update */
    bt_status_t (*conn_parameter_update)(const bt_bdaddr_t *bd_addr, int min_interval,
                    int max_interval, int latency, int timeout);

    /** Sets the LE scan interval and window in units of N*0.625 msec */
    bt_status_t (*set_scan_parameters)(int scan_interval, int scan_window);

    /* Setup the parameters as per spec, user manual specified values and enable multi ADV */
    bt_status_t (*multi_adv_enable)(int client_if, int min_interval,int max_interval,int adv_type,
                 int chnl_map, int tx_power, int timeout_s);

    /* Update the parameters as per spec, user manual specified values and restart multi ADV */
    bt_status_t (*multi_adv_update)(int client_if, int min_interval,int max_interval,int adv_type,
                 int chnl_map, int tx_power, int timeout_s);

    /* Setup the data for the specified instance */
    bt_status_t (*multi_adv_set_inst_data)(int client_if, bool set_scan_rsp, bool include_name,
                    bool incl_txpower, int appearance, int manufacturer_len,
                    char* manufacturer_data, int service_data_len,
                    char* service_data, int service_uuid_len, char* service_uuid);

    /* Disable the multi adv instance */
    bt_status_t (*multi_adv_disable)(int client_if);

    /* Configure the batchscan storage */
    bt_status_t (*batchscan_cfg_storage)(int client_if, int batch_scan_full_max,
        int batch_scan_trunc_max, int batch_scan_notify_threshold);

    /* Enable batchscan */
    bt_status_t (*batchscan_enb_batch_scan)(int client_if, int scan_mode,
        int scan_interval, int scan_window, int addr_type, int discard_rule);

    /* Disable batchscan */
    bt_status_t (*batchscan_dis_batch_scan)(int client_if);

    /* Read out batchscan reports */
    bt_status_t (*batchscan_read_reports)(int client_if, int scan_mode);

    /** Test mode interface */
    bt_status_t (*test_command)( int command, btgatt_test_params_t* params);

} btgatt_client_interface_t;</span>
我們終於找到我們要調用的函數了,但是我們發現並不是直接實現了此函數,而是做了一下映射:
<span style="font-size:14px;">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_refresh,
    btif_gattc_search_service,
    btif_gattc_get_included_service,
    btif_gattc_get_characteristic,
    btif_gattc_get_descriptor,
    btif_gattc_read_char,
    btif_gattc_write_char,
    btif_gattc_read_char_descr,
    btif_gattc_write_char_descr,
    btif_gattc_execute_write,
    btif_gattc_reg_for_notification,
    btif_gattc_dereg_for_notification,
    btif_gattc_read_remote_rssi,
    btif_gattc_scan_filter_param_setup,
    btif_gattc_scan_filter_add_remove,
    btif_gattc_scan_filter_clear,
    btif_gattc_scan_filter_enable,
    btif_gattc_get_device_type,
    btif_gattc_set_adv_data,
    btif_gattc_configure_mtu,
    btif_gattc_conn_parameter_update,
    btif_gattc_set_scan_parameters,
    btif_gattc_multi_adv_enable,
    btif_gattc_multi_adv_update,
    btif_gattc_multi_adv_setdata,
    btif_gattc_multi_adv_disable,
    btif_gattc_cfg_storage,
    btif_gattc_enb_batch_scan,
    btif_gattc_dis_batch_scan,
    btif_gattc_read_batch_scan_reports,
    btif_gattc_test_command
};</span>
ok,終於發現在協議棧裏,已經不是原來的名字,換了一個馬甲,那我們看看它到底做了什麼:
<span style="font-size:14px;">static bt_status_t btif_gattc_register_app(bt_uuid_t *uuid)
{
    CHECK_BTGATT_INIT();
    btif_gattc_cb_t btif_cb;
    memcpy(&btif_cb.uuid, uuid, sizeof(bt_uuid_t));
    return btif_transfer_context(btgattc_handle_event, BTIF_GATTC_REGISTER_APP,
                                 (char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);
}</span>
看到這裏你肯定會覺得似曾相識了,因爲前面有一篇帖子已經講過了一些。主要還是btgattc_handle_event的實現和BTIF_GATTC_REGISTER_APP:
<span style="font-size:14px;">static void btgattc_handle_event(uint16_t event, char* p_param)
{
     ……

    switch (event)
    {
        case BTIF_GATTC_REGISTER_APP:
            btif_to_bta_uuid(&uuid, &p_cb->uuid);
            btif_gattc_incr_app_count();
            BTA_GATTC_AppRegister(&uuid, bta_gattc_cback);
            break;
            ……
</span>
接下來就有點梳理代碼了,大家不要失去耐心,看這些並不是沒用,對你肯定有好處。好了,我們繼續,後面主要就是BTA_GATTC_AppRegister和 bta_gattc_cback了:
<span style="font-size:14px;">void BTA_GATTC_AppRegister(tBT_UUID *p_app_uuid, tBTA_GATTC_CBACK *p_client_cb)
{
    tBTA_GATTC_API_REG  *p_buf;

    if (bta_sys_is_register(BTA_ID_GATTC) == FALSE)
    {
        bta_sys_register(BTA_ID_GATTC, &bta_gattc_reg);
    }

    if ((p_buf = (tBTA_GATTC_API_REG *) GKI_getbuf(sizeof(tBTA_GATTC_API_REG))) != NULL)
    {
        p_buf->hdr.event    = BTA_GATTC_API_REG_EVT;
        if (p_app_uuid != NULL)
            memcpy(&p_buf->app_uuid, p_app_uuid, sizeof(tBT_UUID));
        p_buf->p_cback      = p_client_cb;

        bta_sys_sendmsg(p_buf);
    }
    return;
}
</span>

 通過bta_sys_register函數註冊了bta_gatt_reg結構體中定義的客戶端主事件處理函數bta_gattc_hdl_event;然後設置eventBTA_GATTC_API_REG_EVT,觸發bta_gattc_hdl_event函數:

<span style="font-size:14px;">static const tBTA_SYS_REG bta_gattc_reg =
{
    bta_gattc_hdl_event,
    BTA_GATTC_Disable
};</span>
<span style="font-size:14px;">BOOLEAN bta_gattc_hdl_event(BT_HDR *p_msg)
{
     ……
    switch (p_msg->event)
    {
        ……
        case BTA_GATTC_API_REG_EVT:
            bta_gattc_register(p_cb, (tBTA_GATTC_DATA *) p_msg);
            break;
        …… 
</span>
接下來就是bta對它的處理實現了:
<span style="font-size:14px;">void bta_gattc_register(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_data)
{
    tBTA_GATTC               cb_data;
    UINT8                    i;
    tBT_UUID                 *p_app_uuid = &p_data->api_reg.app_uuid;
    tBTA_GATTC_INT_START_IF  *p_buf;
    tBTA_GATT_STATUS         status = BTA_GATT_NO_RESOURCES;


    APPL_TRACE_DEBUG("bta_gattc_register state %d",p_cb->state);
    memset(&cb_data, 0, sizeof(cb_data));
    cb_data.reg_oper.status = BTA_GATT_NO_RESOURCES;

     /* check if  GATTC module is already enabled . Else enable */
     if (p_cb->state == BTA_GATTC_STATE_DISABLED)
     {
         bta_gattc_enable (p_cb);
     }
    /* todo need to check duplicate uuid */
    for (i = 0; i < BTA_GATTC_CL_MAX; i ++)
    {
        if (!p_cb->cl_rcb[i].in_use)
        {
            if ((p_app_uuid == NULL) || (p_cb->cl_rcb[i].client_if = GATT_Register(p_app_uuid, &bta_gattc_cl_cback)) == 0)
            {
                APPL_TRACE_ERROR("Register with GATT stack failed.");
                status = BTA_GATT_ERROR;
            }
            else
            {
                p_cb->cl_rcb[i].in_use = TRUE;
                p_cb->cl_rcb[i].p_cback = p_data->api_reg.p_cback;
                memcpy(&p_cb->cl_rcb[i].app_uuid, p_app_uuid, sizeof(tBT_UUID));

                /* BTA use the same client interface as BTE GATT statck */
                cb_data.reg_oper.client_if = p_cb->cl_rcb[i].client_if;

                if ((p_buf = (tBTA_GATTC_INT_START_IF *) GKI_getbuf(sizeof(tBTA_GATTC_INT_START_IF))) != NULL)
                {
                    p_buf->hdr.event    = BTA_GATTC_INT_START_IF_EVT;
                    p_buf->client_if    = p_cb->cl_rcb[i].client_if;

                    bta_sys_sendmsg(p_buf);
                    status = BTA_GATT_OK;
                }
                else
                {
                    GATT_Deregister(p_cb->cl_rcb[i].client_if);

                    status = BTA_GATT_NO_RESOURCES;
                    memset( &p_cb->cl_rcb[i], 0 , sizeof(tBTA_GATTC_RCB));
                }
                break;
            }
        }
    }

    /* callback with register event */
    if (p_data->api_reg.p_cback)
    {
        if (p_app_uuid != NULL)
            memcpy(&(cb_data.reg_oper.app_uuid),p_app_uuid,sizeof(tBT_UUID));

        cb_data.reg_oper.status = status;
        (*p_data->api_reg.p_cback)(BTA_GATTC_REG_EVT,  (tBTA_GATTC *)&cb_data);
    }
}</span>
到這裏了,基本上已經到了GKI庫了,那裏面的東西我就不寫了,不過那裏面和協議關係不是很大了。這裏面最後還有一個event的回調BTA_GATTC_REG_EVT了。那麼接下來就是要講回調了。

那我們就重點來解析一下bta_gattc_cback這個回調,先來看看它的實現:

<span style="font-size:14px;">static void bta_gattc_cback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
{
    bt_status_t status = btif_transfer_context(btif_gattc_upstreams_evt,
                    (uint16_t) event, (void*) p_data, sizeof(tBTA_GATTC), btapp_gattc_req_data);
    ASSERTC(status == BT_STATUS_SUCCESS, "Context transfer failed!", status);
}</span>
這裏我們主要是關注兩個地方,一個是btif_gattc_upstreams_evt另一個就是btapp_gattc_req_data.

btapp_gattc_req_data的實現很簡單:

<span style="font-size:14px;">static void btapp_gattc_req_data(UINT16 event, char *p_dest, char *p_src)
{
    tBTA_GATTC *p_dest_data = (tBTA_GATTC*) p_dest;
    tBTA_GATTC *p_src_data = (tBTA_GATTC*) p_src;

    if (!p_src_data || !p_dest_data)
       return;

    // Copy basic structure first
    memcpy(p_dest_data, p_src_data, sizeof(tBTA_GATTC));

    // Allocate buffer for request data if necessary
    switch (event)
    {
        case BTA_GATTC_READ_CHAR_EVT:
        case BTA_GATTC_READ_DESCR_EVT:

            if (p_src_data->read.p_value != NULL)
            {
                p_dest_data->read.p_value = GKI_getbuf(sizeof(tBTA_GATT_READ_VAL));

                if (p_dest_data->read.p_value != NULL)
                {
                    memcpy(p_dest_data->read.p_value, p_src_data->read.p_value,
                        sizeof(tBTA_GATT_READ_VAL));

                    // Allocate buffer for att value if necessary
                    if (get_uuid16(&p_src_data->read.descr_type.uuid) != GATT_UUID_CHAR_AGG_FORMAT
                      && p_src_data->read.p_value->unformat.len > 0
                      && p_src_data->read.p_value->unformat.p_value != NULL)
                    {
                        p_dest_data->read.p_value->unformat.p_value =
                                       GKI_getbuf(p_src_data->read.p_value->unformat.len);
                        if (p_dest_data->read.p_value->unformat.p_value != NULL)
                        {
                            memcpy(p_dest_data->read.p_value->unformat.p_value,
                                   p_src_data->read.p_value->unformat.p_value,
                                   p_src_data->read.p_value->unformat.len);
                        }
                    }
                }
            }
            else
            {
                BTIF_TRACE_WARNING("%s :Src read.p_value ptr is NULL for event  0x%x",
                                    __FUNCTION__, event);
                p_dest_data->read.p_value = NULL;

            }
            break;

        default:
            break;
    }
}
</span>
下面來看看btif_gattc_upstreams_evt的實現:

<span style="font-size:14px;">static void btif_gattc_upstreams_evt(uint16_t event, char* p_param)
{
    BTIF_TRACE_EVENT("%s: Event %d", __FUNCTION__, event);

    tBTA_GATTC *p_data = (tBTA_GATTC*) p_param;
    switch (event)
    {
        case BTA_GATTC_REG_EVT:
        {
            bt_uuid_t app_uuid;
            bta_to_btif_uuid(&app_uuid, &p_data->reg_oper.app_uuid);
            HAL_CBACK(bt_gatt_callbacks, client->register_client_cb
                , p_data->reg_oper.status
                , p_data->reg_oper.client_if
                , &app_uuid
            );
            break;
          ……
 }</span>
那你肯定會問,爲什麼是BTA_GATTC_REG_EVT這個事件呢,因爲我們在bta_gattc_register做了伏筆,參數是它傳過去的。在這裏又做了一個很重要的事情註冊了client callback.

我們先來看一下bt_gatt_callbacks:

<span style="font-size:14px;">const btgatt_callbacks_t *bt_gatt_callbacks = NULL;</span>
看來我們真正要找的是btgatt_callbacks_t,那我們繼續追它:

<span style="font-size:14px;">/** BT-GATT callbacks */
typedef struct {
    /** Set to sizeof(btgatt_callbacks_t) */
    size_t size;

    /** GATT Client callbacks */
    const btgatt_client_callbacks_t* client;

    /** GATT Server callbacks */
    const btgatt_server_callbacks_t* server;
} btgatt_callbacks_t;</span>

接下來就要去解析register_client_cb了,我們知道client指向的是btgatt_client_callbacks_t結構體,它對應的是:

typedef struct {
    register_client_callback            register_client_cb;
    scan_result_callback                scan_result_cb;
    connect_callback                    open_cb;
    disconnect_callback                 close_cb;
     ……
到這裏既有點繞了,因爲需要回到JNI去尋找,但是由於前面我已經講過(android4.4.2 bluetooth解析(二)),爲了減少篇幅我就不重複了,直接找到對應的函數:

static const btgatt_client_callbacks_t sGattClientCallbacks = {
    btgattc_register_app_cb,
    btgattc_scan_result_cb,
    btgattc_open_cb,
    btgattc_close_cb,
    ……
void btgattc_register_app_cb(int status, int clientIf, bt_uuid_t *app_uuid)
{
    CHECK_CALLBACK_ENV
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientRegistered, status,
        clientIf, UUID_PARAMS(app_uuid));
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}
到這裏我們已經看到method_onClientRegistered了:

static jmethodID method_onClientRegistered;
下一步就會進入Java層,到達回調service:

    void onClientRegistered(int status, int clientIf, long uuidLsb, long uuidMsb)
            throws RemoteException {
        UUID uuid = new UUID(uuidMsb, uuidLsb);
        if (DBG) Log.d(TAG, "onClientRegistered() - UUID=" + uuid + ", clientIf=" + clientIf);
        ClientMap.App app = mClientMap.getByUuid(uuid);
        if (app != null) {
            if (status == 0) {
                app.id = clientIf;
                app.linkToDeath(new ClientDeathRecipient(clientIf));
            } else {
                mClientMap.remove(uuid);
            }
            app.callback.onClientRegistered(status, clientIf);
        }
    }
這終於回到了BluetoothLeAdvertiser類了:

        /**
         * Application interface registered - app is ready to go
         */
        @Override
        public void onClientRegistered(int status, int clientIf) {
            Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf);
            synchronized (this) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    mClientIf = clientIf;
                    try {
                        mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement,
                                mScanResponse, mSettings);
                        return;
                    } catch (RemoteException e) {
                        Log.e(TAG, "failed to start advertising", e);
                    }
                }
                // Registration failed.
                mClientIf = -1;
                notifyAll();
            }
        }
到這裏你可以看到已經走了一個來回了,當然這是在應用第一次啓動,或者之前並沒有註冊過ble應用設備時纔會這樣,不然他會跳過register_app這一步動作的。看到這裏你會感覺好複雜呀,實際上,這一直是在準備工作,真正的廣播還沒開始呢。不過爲了文章篇幅我會在第二篇文章繼續看看mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement, mScanResponse, mSettings);是怎麼工作的。

下一篇文章即將更新,敬請期待。















 

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