LeScan探検隊 - ReDo

2014年3月10日

LeScan探検隊

AndroidのBLEスキャンはみんなで同時にon/offしたらどうなるの?

○最初に結論

大丈夫だ、問題ない。

○確認手順

1. onPauseでscan止めないアプリAをつくる
2. アプリAを起動してscanしてHOMEおしてscanが続いてることを確認
3. scanしてconnectしてCharacteristicのreadとかwriteとかするアプリBをつくる
4. アプリAを起動してscanしてHOMEおしてアプリBを起動してscanして色々動かす
5. アプリAのscan止まってなければ勝ち

別になんでもいいのですがこれこれで試してみたところ...

なんてことのなかった様に動きました@Nexus5。 「どこかscan中の子が1つでも居たらstopLeScanしても止まらない」ということのようです。

以下、余談。

○潜る前に

ダンジョンマップを予め見ておきます。

source.android.com:Bluetooth
https://source.android.com/devices/bluetooth.html

○探検開始

http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html
boolean startLeScan(BluetoothAdapter.LeScanCallback callback)
boolean startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
void stopLeScan(BluetoothAdapter.LeScanCallback callback)
http://tools.oesf.biz/android-4.4.2_r1.0/xref/frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java
 
   1477     /**
   1478      * Starts a scan for Bluetooth LE devices, looking for devices that
   1479      * advertise given services.
   1480      *
   1481      * <p>Devices which advertise all specified services are reported using the
   1482      * {@link LeScanCallback#onLeScan} callback.
   1483      *
   1484      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
   1485      *
   1486      * @param serviceUuids Array of services to look for
   1487      * @param callback the callback LE scan results are delivered
   1488      * @return true, if the scan was started successfully
   1489      */
   1490     public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) {
 
同ファイル中のGattCallbackWrapperというclassに続きが実装されています。
   1543     /**
   1544      * Bluetooth GATT interface callbacks
   1545      */
   1546     private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub {
 
ちょっと長いですが、だいたい、以下のあたりがスキャン開始のあたりの模様です。
   1609         /**
   1610          * Application interface registered - app is ready to go
   1611          */
   1612         public void onClientRegistered(int status, int clientIf) {
 
   1620                 if (status == BluetoothGatt.GATT_SUCCESS) {
   1621                     mLeHandle = clientIf;
   1622                     IBluetoothGatt iGatt = null;
   1623                     try {
   1624                         BluetoothAdapter adapter = mBluetoothAdapter.get();
   1625                         if (adapter != null) {
   1626                             iGatt = adapter.getBluetoothManager().getBluetoothGatt();
   1627                             if (mScanFilter == null) {
   1628                                 iGatt.startScan(mLeHandle, false);
   1629                             } else {
   1630                                 ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length];
   1631                                 for(int i = 0; i != uuids.length; ++i) {
   1632                                     uuids[i] = new ParcelUuid(mScanFilter[i]);
   1633                                 }
   1634                                 iGatt.startScanWithUuids(mLeHandle, false, uuids);
   1635                             }
IBluetoothGattを公開している先を探しに行きます。Bluetoothアプリ(なんかあると時々クラッシュするあれですね)が該当します。 http://tools.oesf.biz/android-4.4.2_r1.0/xref/packages/apps/Bluetooth/AndroidManifest.xml
 
    239         <service
    240             android:process="@string/process"
    241             android:name = ".gatt.GattService"
    242             android:enabled="@bool/profile_supported_gatt">
    243             <intent-filter>
    244                 <action android:name="android.bluetooth.IBluetoothGatt" />
    245             </intent-filter>
    246         </service>
本体が見つかりました。ここからはNativeに潜ります。 http://tools.oesf.biz/android-4.4.2_r1.0/xref/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java
 
    897     void startScan(int appIf, boolean isServer) {
    898         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    899 
    900         if (DBG) Log.d(TAG, "startScan() - queue=" + mScanQueue.size());
    901 
    902         if (getScanClient(appIf, isServer) == null) {
    903             if (DBG) Log.d(TAG, "startScan() - adding client=" + appIf);
    904             mScanQueue.add(new ScanClient(appIf, isServer));
    905         }
    906 
    907         gattClientScanNative(appIf, true);
    908     }
btgatt_interface_t構造体にclientがあってscanを発行してるらしい。 http://tools.oesf.biz/android-4.4.2_r1.0/xref/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
 
    184 static const btgatt_interface_t *sGattIf = NULL;
 
    771 static void gattClientScanNative(JNIEnv* env, jobject object, jint clientIf, jboolean start)
    772 {
    773     if (!sGattIf) return;
    774     sGattIf->client->scan(clientIf, start);
    775 }
http://tools.oesf.biz/android-4.4.2_r1.0/xref/hardware/libhardware/include/hardware/bt_gatt.h#btgatt_interface_t
     52     /** Pointer to the GATT client interface methods.*/
     53     const btgatt_client_interface_t* client;
http://tools.oesf.biz/android-4.4.2_r1.0/xref/hardware/libhardware/include/hardware/bt_gatt_client.h#btgatt_client_interface_t
 
    189     /** Start or stop LE device scanning */
    190     bt_status_t (*scan)( int client_if, bool start );
BlueDroidに到着。 http://tools.oesf.biz/android-4.4.2_r1.0/xref/external/bluetooth/bluedroid/btif/src/btif_gatt_client.c
 
    864 static bt_status_t btif_gattc_scan( int client_if, bool start )
    865 {
    866     CHECK_BTGATT_INIT();
    867     btif_gattc_cb_t btif_cb;
    868     btif_cb.client_if = (uint8_t) client_if;
    869     return btif_transfer_context(btgattc_handle_event, start ? BTIF_GATTC_SCAN_START : BTIF_GATTC_SCAN_STOP,
    870                                  (char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);
    871 }
 
    590         case BTIF_GATTC_SCAN_START:
    591             btif_gattc_init_dev_cb();
    592             BTA_DmBleObserve(TRUE, 0, bta_scan_results_cb);
    593             break;
http://tools.oesf.biz/android-4.4.2_r1.0/xref/external/bluetooth/bluedroid/bta/dm/bta_dm_api.c#BTA_DmBleObserve
 
   1760 /*******************************************************************************
   1761 **
   1762 ** Function         BTA_DmBleObserve
   1763 **
   1764 ** Description      This procedure keep the device listening for advertising
   1765 **                  events from a broadcast device.
   1766 **
   1767 ** Parameters       start: start or stop observe.
   1768 **
   1769 ** Returns          void
   1770 
   1771 **
   1772 ** Returns          void.
   1773 **
   1774 *******************************************************************************/
   1775 BTA_API extern void BTA_DmBleObserve(BOOLEAN start, UINT8 duration,
   1776                                      tBTA_DM_SEARCH_CBACK *p_results_cb)
   1777 {
   1778 #if BLE_INCLUDED == TRUE
   1779 
   1780     tBTA_DM_API_BLE_OBSERVE   *p_msg;
   1781 
   1782     APPL_TRACE_API1("BTA_DmBleObserve:start = %d ", start);
   1783 
   1784     if ((p_msg = (tBTA_DM_API_BLE_OBSERVE *) GKI_getbuf(sizeof(tBTA_DM_API_BLE_OBSERVE))) != NULL)
   1785     {
   1786         memset(p_msg, 0, sizeof(tBTA_DM_API_BLE_OBSERVE));
   1787 
   1788         p_msg->hdr.event = BTA_DM_API_BLE_OBSERVE_EVT;
   1789         p_msg->start = start;
   1790         p_msg->duration = duration;
   1791         p_msg->p_cback = p_results_cb;
   1792 
   1793         bta_sys_sendmsg(p_msg);
   1794     }
   1795 #endif
   1796 }
http://tools.oesf.biz/android-4.4.2_r1.0/xref/external/bluetooth/bluedroid/bta/sys/bta_sys_main.c#bta_sys_sendmsg
 
    595 void bta_sys_sendmsg(void *p_msg)
    596 {
    597     GKI_send_msg(bta_sys_cb.task_id, p_bta_sys_cfg->mbox, p_msg);
    598 }

L2CAPの最下層、HCIまで下りてきた気がしますがここよりは下のもようです。ここまででリタイア。リレミト。

コメントする