Bluetooth源碼分析(二)藍牙掃描流程

時序圖:


藍牙掃描時序圖.png

1 UI

藍牙開始掃描位于setting的 /packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothPairingDetail.java 中。

      void enableScanning() {
          // Clear all device states before first scan
          if (!mInitialScanStarted) {
              if (mAvailableDevicesCategory != null) {
                  removeAllDevices();
              }
              mLocalManager.getCachedDeviceManager().clearNonBondedDevices();
              mInitialScanStarted = true;
          }
          super.enableScanning();
      }

在這里如果沒有開始掃描就清除緩存中所有的設(shè)備平斩,然后將緩存的設(shè)備中沒有配對的設(shè)備清除亚享,最后調(diào)用了父類 DeviceListPreferenceFragment 的enableScanning()方法:

     @VisibleForTesting
      void enableScanning() {
          // LocalBluetoothAdapter already handles repeated scan requests
          mLocalAdapter.startScanning(true);
          mScanEnabled = true;
      }

這里其實最終調(diào)用了/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter的startScanning方法開始掃描。


2 framework

LocalBluetoothAdapter的startScanning方法:

    public void startScanning(boolean force) {
        // Only start if we're not already scanning
        // 只會在沒有進行掃描的時候開始掃描
        if (!mAdapter.isDiscovering()) {
            // 如果不是強制掃描绘面,在SCAN_EXPIRATION_MS間隔內(nèi),只掃描一次
            if (!force) {
                // Don't scan more than frequently than SCAN_EXPIRATION_MS,
                // unless forced
                if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
                    return;
                }

                // If we are playing music, don't scan unless forced.
                // 播放藍牙音樂時,不進行掃描
                A2dpProfile a2dp = mProfileManager.getA2dpProfile();
                if (a2dp != null && a2dp.isA2dpPlaying()) {
                    return;
                }
            }
            // 開始掃描
            if (mAdapter.startDiscovery()) {
                mLastScan = System.currentTimeMillis();
            }
        }
    }

這里最后調(diào)用frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java的startDiscovery方法揭璃,啟動掃描任務(wù)

public boolean startDiscovery() {
    if (!Utils.checkCaller()) {
    Log.w(TAG, "startDiscovery() - Not allowed for non-active user");
    return false;
    }

    AdapterService service = getService();
    if (service == null) return false;
    return service.startDiscovery();
}


3 Bluetooth App

這里的 service.startDiscovery 屬于 aidl 跨進程通信晚凿,通過 IBluetooth.aidl 調(diào)用遠程服務(wù) /packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService 中的 startDiscovery 方法:

     boolean startDiscovery() {
         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                       "Need BLUETOOTH ADMIN permission");
          //do not allow new connections with active multicast
          A2dpService a2dpService = A2dpService.getA2dpService();
          if (a2dpService != null &&
              a2dpService.isMulticastFeatureEnabled() &&
              a2dpService.isMulticastOngoing(null)) {
              Log.i(TAG,"A2dp Multicast is Ongoing, ignore discovery");
              return false;
          }

         // 掃描中則終止操作
         if (mAdapterProperties.isDiscovering()) {
             Log.i(TAG,"discovery already active, ignore startDiscovery");
             return false;
           }
         return startDiscoveryNative();
    }

這里最終調(diào)用tartDiscoveryNative()方法,從 Java 層調(diào)到 JNI 層的com_android_bluetooth_btservice_AdapterService.cpp文件中的 startDiscoveryNative 方法:

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

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

    int ret = sBluetoothInterface->start_discovery();//該接口調(diào)用到hal層的配對函數(shù)
    result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    return result;
}

int ret = sBluetoothInterface->start_discovery() 這行代碼調(diào)進了hal層的藍牙協(xié)議棧中瘦馍。


4 藍牙協(xié)議棧

上面調(diào)用的start_discovery 方法位于 /system/bt/btif/src/bluetooth.cc:

static int start_discovery(void)
{
    /* sanity check */ //完整性檢查
    if (interface_ready() == FALSE)
        return BT_STATUS_NOT_READY;

    return btif_dm_start_discovery();
}

start_discovery方法調(diào)用/system/bt/btif/src/btif_dm.cc的btif_dm_start_discovery方法歼秽,btif_dm.cc 用于設(shè)備管理相關(guān)的功能。

  bt_status_t btif_dm_start_discovery(void) {
    tBTA_DM_INQ inq_params;
    tBTA_SERVICE_MASK services = 0;

    BTIF_TRACE_EVENT("%s : pairing_cb.state: 0x%x", __FUNCTION__, pairing_cb.state);

    /* We should not go for inquiry in BONDING STATE. */
    // 處于綁定狀態(tài)情组,則不進行掃描 
    if (pairing_cb.state == BT_BOND_STATE_BONDING)
        return BT_STATUS_BUSY;

    /* Cleanup anything remaining on index 0 */
    do_in_bta_thread(
        FROM_HERE,
        base::Bind(&BTM_BleAdvFilterParamSetup, BTM_BLE_SCAN_COND_DELETE, 0,
                   nullptr, base::Bind(&bte_scan_filt_param_cfg_evt, 0)));

    auto adv_filt_param = std::make_unique<btgatt_filt_param_setup_t>();
    /* Add an allow-all filter on index 0*/
    adv_filt_param->dely_mode = IMMEDIATE_DELY_MODE;
    adv_filt_param->feat_seln = ALLOW_ALL_FILTER;
    adv_filt_param->filt_logic_type = BTA_DM_BLE_PF_FILT_LOGIC_OR;
    adv_filt_param->list_logic_type = BTA_DM_BLE_PF_LIST_LOGIC_OR;
    adv_filt_param->rssi_low_thres = LOWEST_RSSI_VALUE;
    adv_filt_param->rssi_high_thres = LOWEST_RSSI_VALUE;
    do_in_bta_thread(
        FROM_HERE, base::Bind(&BTM_BleAdvFilterParamSetup, BTM_BLE_SCAN_COND_ADD,
                              0, base::Passed(&adv_filt_param),
                              base::Bind(&bte_scan_filt_param_cfg_evt, 0)));

    /* TODO: Do we need to handle multiple inquiries at the same time? */

    /* Set inquiry params and call API */
    inq_params.mode = BTA_DM_GENERAL_INQUIRY | BTA_BLE_GENERAL_INQUIRY;
  #if (BTA_HOST_INTERLEAVE_SEARCH == TRUE)
    inq_params.intl_duration[0] = BTIF_DM_INTERLEAVE_DURATION_BR_ONE;
    inq_params.intl_duration[1] = BTIF_DM_INTERLEAVE_DURATION_LE_ONE;
    inq_params.intl_duration[2] = BTIF_DM_INTERLEAVE_DURATION_BR_TWO;
    inq_params.intl_duration[3] = BTIF_DM_INTERLEAVE_DURATION_LE_TWO;
  #endif
    inq_params.duration = BTIF_DM_DEFAULT_INQ_MAX_DURATION;

    inq_params.max_resps = BTIF_DM_DEFAULT_INQ_MAX_RESULTS;
    inq_params.report_dup = true;

    inq_params.filter_type = BTA_DM_INQ_CLR;
    /* TODO: Filter device by BDA needs to be implemented here */

    /* Will be enabled to true once inquiry busy level has been received */
    btif_dm_inquiry_in_progress = false;
    /* find nearby devices */
    BTA_DmSearch(&inq_params, services, bte_search_devices_evt);

    return BT_STATUS_SUCCESS;
  }

btif_dm_start_discovery方法中最終調(diào)用 /system/bt/bta/dm/bta_dm_api.cc中的 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);//發(fā)送到另一個線程
    }
}

調(diào)用bta_sys_sendmsg向BTA發(fā)送掃描任務(wù)消息燥筷。bta_sys_sendmsg這是藍牙協(xié)議棧的進程間收發(fā)消息的機制,不是三言兩語能說清楚的院崇,若想了解肆氓,可以參考《android bluedroid協(xié)議棧里面的各個組件之間的消息處理機制》 這篇文章。
通過搜索event標記BTA_DM_API_SEARCH_EVT可以找到/system/bt/bta/dm/bta_dm_act.cc里面:

  static void bta_dm_rs_cback(UNUSED_ATTR void* p1) {
    APPL_TRACE_WARNING("bta_dm_rs_cback:%d", bta_dm_cb.rs_event);
    if (bta_dm_cb.rs_event == BTA_DM_API_SEARCH_EVT) {
      bta_dm_cb.search_msg.rs_res =
          BTA_DM_RS_OK; /* do not care about the result for now */
      bta_dm_cb.rs_event = 0;
      bta_dm_search_start((tBTA_DM_MSG*)&bta_dm_cb.search_msg);// 調(diào)用內(nèi)部的開啟掃描方法
    }
  }

這邊判斷了如果event是BTA_DM_API_SEARCH_EVT就調(diào)用bta本身的bta_dm_search_start方法:

  void bta_dm_search_start(tBTA_DM_MSG* p_data) {
    tBTM_INQUIRY_CMPL result = {};

    size_t len = sizeof(Uuid) * p_data->search.num_uuid;
    bta_dm_gattc_register();

    APPL_TRACE_DEBUG("%s avoid_scatter=%d", __func__,
                     p_bta_dm_cfg->avoid_scatter);

    if (p_bta_dm_cfg->avoid_scatter &&
        (p_data->search.rs_res == BTA_DM_RS_NONE) &&
        bta_dm_check_av(BTA_DM_API_SEARCH_EVT)) {
      LOG(INFO) << __func__ << ": delay search to avoid scatter";
      memcpy(&bta_dm_cb.search_msg, &p_data->search, sizeof(tBTA_DM_API_SEARCH));
      return;
    }

    BTM_ClearInqDb(nullptr);// 清除BTM中之前保存的搜索數(shù)據(jù)
    /* save search params */
    bta_dm_search_cb.p_search_cback = p_data->search.p_cback;
    bta_dm_search_cb.services = p_data->search.services;

    osi_free_and_reset((void**)&bta_dm_search_cb.p_srvc_uuid);

    if ((bta_dm_search_cb.num_uuid = p_data->search.num_uuid) != 0 &&
        p_data->search.p_uuid != nullptr) {
      bta_dm_search_cb.p_srvc_uuid = (Uuid*)osi_malloc(len);
      *bta_dm_search_cb.p_srvc_uuid = *p_data->search.p_uuid;
    }
    result.status = BTM_StartInquiry((tBTM_INQ_PARMS*)&p_data->search.inq_params,
                                     bta_dm_inq_results_cb, bta_dm_inq_cmpl_cb);// 調(diào)用btm中的啟動掃描

    APPL_TRACE_EVENT("%s status=%d", __func__, result.status);
    if (result.status != BTM_CMD_STARTED) {
      LOG(ERROR) << __func__ << ": BTM_StartInquiry returned "
                 << std::to_string(result.status);
      result.num_resp = 0;
      bta_dm_inq_cmpl_cb((void*)&result);
    }
  }

這里代碼很多底瓣,大概就是先清除BTM中之前的搜索數(shù)據(jù)谢揪,然后保存搜索的參數(shù),調(diào)用btm中的BTM_StartInquiry方法進行掃描捐凭。
btm的掃描操作在/system/bt/stack/btm/btm_inq.cc里面:

 tBTM_STATUS BTM_StartInquiry(tBTM_INQ_PARMS* p_inqparms,
                               tBTM_INQ_RESULTS_CB* p_results_cb,
                               tBTM_CMPL_CB* p_cmpl_cb) {
    tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;

    BTM_TRACE_API("BTM_StartInquiry: mode: %d, dur: %d, rsps: %d, flt: %d",
                  p_inqparms->mode, p_inqparms->duration, p_inqparms->max_resps,
                  p_inqparms->filter_cond_type);

    /* Only one active inquiry is allowed in this implementation.
       Also do not allow an inquiry if the inquiry filter is being updated */
    if (p_inq->inq_active || p_inq->inqfilt_active) {
      /*check if LE observe is already running*/
      if (p_inq->scan_type == INQ_LE_OBSERVE &&
          p_inq->p_inq_ble_results_cb != nullptr) {
        BTM_TRACE_API("BTM_StartInquiry: LE observe in progress");
        p_inq->scan_type = INQ_GENERAL;
        p_inq->inq_active = BTM_INQUIRY_INACTIVE;
        btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE;
        // 向hci發(fā)送開啟掃描消息
        btm_send_hci_scan_enable(BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE);
      } else {
        LOG(ERROR) << __func__ << ": BTM_BUSY";
        return (BTM_BUSY);
      }
    } else {
      p_inq->scan_type = INQ_GENERAL;
    }

     .............省略.................
  }

這邊就調(diào)用/system/bt/stack/btm/btm_ble_gap.cc里面的btm_send_hci_scan_enable方法向hci發(fā)送掃描的通知:

  void btm_send_hci_scan_enable(uint8_t enable, uint8_t filter_duplicates) {
    if (controller_get_interface()->supports_ble_extended_advertising()) {
      btsnd_hcic_ble_set_extended_scan_enable(enable, filter_duplicates, 0x0000,
                                              0x0000);
    } else {
      btsnd_hcic_ble_set_scan_enable(enable, filter_duplicates);//真正與hci打交道
    }
  }

在這里會判斷是否支持ble拓展拨扶,如果支持則調(diào)用另外一個掃描方法,這里不具體分析茁肠,這里只分析傳統(tǒng)的掃描流程患民,其實內(nèi)部邏輯是差不多的。
如果不支持ble拓展垦梆,則調(diào)用/system/bt/stack/hcic/hciblecmds.cc的btsnd_hcic_ble_set_scan_enable方法:

184  void btsnd_hcic_ble_set_scan_enable(uint8_t scan_enable, uint8_t duplicate) {
185    BT_HDR* p = (BT_HDR*)osi_malloc(HCI_CMD_BUF_SIZE);
186    uint8_t* pp = (uint8_t*)(p + 1);
187  
188    p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE;
189    p->offset = 0;
190  
191    UINT16_TO_STREAM(pp, HCI_BLE_WRITE_SCAN_ENABLE);
192    UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE);
193  
194    UINT8_TO_STREAM(pp, scan_enable);
195    UINT8_TO_STREAM(pp, duplicate);
196  
197    btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);//這里是向hci層發(fā)命令
198  }

可以看出來匹颤,這里是通過和hci層的通信,host告訴controlor藍牙地址奶赔、數(shù)據(jù)惋嚎、命令等,從而控制其底層硬件發(fā)起配對操作站刑。具體btu如何與hci通信另伍,過程也是很繁瑣,可以參考《Android BT STACK BTU 和 HCI之間的消息傳遞》這篇文章绞旅。
到此開啟掃描的流程就結(jié)束了摆尝。有一個遺留問題就是開啟掃描之后,掃描到的藍牙設(shè)備是怎么返給上層的呢因悲?


5 掃描結(jié)果返回

我們回頭看堕汞,之前調(diào)用了在/system/bt/bta/dm/bta_dm_act.cc里面的bta_dm_search_start方法時,關(guān)鍵代碼是:

    result.status = BTM_StartInquiry((tBTM_INQ_PARMS*)&p_data->search.inq_params,
                                     bta_dm_inq_results_cb, bta_dm_inq_cmpl_cb);

這里可以看到在調(diào)用btm的開始掃描方法的時候會傳入三個參數(shù)晃琳,第一個就是啟動掃描所需要傳下去的參數(shù)讯检,第二個bta_dm_inq_results_cb就是掃描結(jié)果的回調(diào)琐鲁,第三個bta_dm_inq_cmpl_cb就是掃描完成的回調(diào)。也就是說btm啟動掃描之后人灼,掃描到藍牙設(shè)備之后會通過bta_dm_inq_results_cb這個回調(diào)返給bta围段,而掃描結(jié)束之后會通過bta_dm_inq_cmpl_cb這個回調(diào)返給bta。
這里我們拿bta_dm_inq_results_cb這個回調(diào)來分析:

static void bta_dm_inq_results_cb(tBTM_INQ_RESULTS* p_inq, uint8_t* p_eir,
                                    uint16_t eir_len) {
    tBTA_DM_SEARCH result;
    tBTM_INQ_INFO* p_inq_info;
    uint16_t service_class;

    result.inq_res.bd_addr = p_inq->remote_bd_addr;
    memcpy(result.inq_res.dev_class, p_inq->dev_class, DEV_CLASS_LEN);
    BTM_COD_SERVICE_CLASS(service_class, p_inq->dev_class);
    result.inq_res.is_limited =
        (service_class & BTM_COD_SERVICE_LMTD_DISCOVER) ? true : false;
    result.inq_res.rssi = p_inq->rssi;

    result.inq_res.ble_addr_type = p_inq->ble_addr_type;
    result.inq_res.inq_result_type = p_inq->inq_result_type;
    result.inq_res.device_type = p_inq->device_type;
    result.inq_res.flag = p_inq->flag;

    /* application will parse EIR to find out remote device name */
    result.inq_res.p_eir = p_eir;
    result.inq_res.eir_len = eir_len;

    p_inq_info = BTM_InqDbRead(p_inq->remote_bd_addr);
    if (p_inq_info != NULL) {
      /* initialize remt_name_not_required to false so that we get the name by
       * default */
      result.inq_res.remt_name_not_required = false;
    }

    if (bta_dm_search_cb.p_search_cback)
      bta_dm_search_cb.p_search_cback(BTA_DM_INQ_RES_EVT, &result);

    if (p_inq_info) {
      /* application indicates if it knows the remote name, inside the callback
       copy that to the inquiry data base*/
      if (result.inq_res.remt_name_not_required)
        p_inq_info->appl_knows_rem_name = true;
    }
  }

這里面把掃描到的device數(shù)據(jù)放入result里面通過bta_dm_search_cb.p_search_cback(BTA_DM_INQ_RES_EVT, &result)回傳投放。event是BTA_DM_INQ_RES_EVT奈泪,通過搜索這個BTA_DM_INQ_RES_EVT發(fā)現(xiàn)在/system/bt/btif/src/btif_dm.cc里面有一個bte_search_devices_evt方法:

static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)
{
    UINT16 param_len = 0;

    if (p_data)
        param_len += sizeof(tBTA_DM_SEARCH);
    /* Allocate buffer to hold the pointers (deep copy). The pointers will point to the end of the tBTA_DM_SEARCH */
    switch (event)
    {
        case BTA_DM_INQ_RES_EVT:
        {
            if (p_data->inq_res.p_eir)
                param_len += HCI_EXT_INQ_RESPONSE_LEN;
        }
        break;

        case BTA_DM_DISC_RES_EVT:
        {
            if (p_data->disc_res.raw_data_size && p_data->disc_res.p_raw_data)
                param_len += p_data->disc_res.raw_data_size;
        }
        break;
    }
    BTIF_TRACE_DEBUG("%s event=%s param_len=%d", __FUNCTION__, dump_dm_search_event(event), param_len);

    /* if remote name is available in EIR, set teh flag so that stack doesnt trigger RNR */
    if (event == BTA_DM_INQ_RES_EVT)
        p_data->inq_res.remt_name_not_required = check_eir_remote_name(p_data, NULL, NULL);

    btif_transfer_context (btif_dm_search_devices_evt , (UINT16) event, (void *)p_data, param_len,
        (param_len > sizeof(tBTA_DM_SEARCH)) ? search_devices_copy_cb : NULL);
}

這個bte_search_devices_evt方法就是掃描任務(wù)的回調(diào)函數(shù),當掃描到設(shè)備時灸芳,回調(diào)這個方法涝桅,將上下文從BTE切換到BTIF
再調(diào)用btif_dm_search_devices_evt,將掃描到的設(shè)備通過HAL_CBACK方式返回烙样。

static void btif_dm_search_devices_evt (UINT16 event, char *p_param)
{
    tBTA_DM_SEARCH *p_search_data;
    BTIF_TRACE_EVENT("%s event=%s", __FUNCTION__, dump_dm_search_event(event));

    switch (event)
    {
        case BTA_DM_DISC_RES_EVT:
        {
            p_search_data = (tBTA_DM_SEARCH *)p_param;
            /* Remote name update */
            if (strlen((const char *) p_search_data->disc_res.bd_name))
            {
                bt_property_t properties[1];
                bt_bdaddr_t bdaddr;
                bt_status_t status;

                properties[0].type = BT_PROPERTY_BDNAME;
                properties[0].val = p_search_data->disc_res.bd_name;
                properties[0].len = strlen((char *)p_search_data->disc_res.bd_name);
                bdcpy(bdaddr.address, p_search_data->disc_res.bd_addr);

                status = btif_storage_set_remote_device_property(&bdaddr, &properties[0]);
                ASSERTC(status == BT_STATUS_SUCCESS, "failed to save remote device property", status);
                HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,
                                 status, &bdaddr, 1, properties);
            }
            /* TODO: Services? */
        }

        ..........................省略............................
  }
}

掃描到設(shè)備時冯遂,調(diào)用的是HAL_CBACK(bt_hal_cbacks, device_found_cb,num_properties, properties);
device_found_cb即是device_found_callback方法,在com_android_bluetooth_btservice_AdapterService.cpp中實現(xiàn)

static void device_found_callback(int num_properties, bt_property_t *properties) {
    jbyteArray addr = NULL;
    int addr_index;

    for (int i = 0; i < num_properties; i++) {
        if (properties[i].type == BT_PROPERTY_BDADDR) {
            addr = callbackEnv->NewByteArray(properties[i].len);
            if (addr) {
                callbackEnv->SetByteArrayRegion(addr, 0, properties[i].len,
                                                (jbyte*)properties[i].val);
                addr_index = i;
            } else {
                ALOGE("Address is NULL (unable to allocate) in %s", __FUNCTION__);
                return;
            }
        }
    }
    if (addr == NULL) {
        ALOGE("Address is NULL in %s", __FUNCTION__);
        return;
    }

    ALOGV("%s: Properties: %d, Address: %s", __FUNCTION__, num_properties,
        (const char *)properties[addr_index].val);

    remote_device_properties_callback(BT_STATUS_SUCCESS, (bt_bdaddr_t *)properties[addr_index].val,
                                      num_properties, properties);

    callbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceFoundCallback, addr);
    checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
    callbackEnv->DeleteLocalRef(addr);
}

該方法對應(yīng)JNI方法為method_deviceFoundCallback误阻,即

jclass jniCallbackClass =
        env->FindClass("com/android/bluetooth/btservice/JniCallbacks");
        ..................
method_deviceFoundCallback = env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");

對應(yīng)JniCallbacks.java的deviceFoundCallback方法债蜜。

void deviceFoundCallback(byte[] address) {
        mRemoteDevices.deviceFoundCallback(address);
    }

deviceFoundCallback將掃描到的設(shè)備通過廣播發(fā)送出去

    void deviceFoundCallback(byte[] address) {
        // The device properties are already registered - we can send the intent
        // now
        BluetoothDevice device = getDevice(address);
        debugLog("deviceFoundCallback: Remote Address is:" + device);
        DeviceProperties deviceProp = getDeviceProperties(device);
        if (deviceProp == null) {
            errorLog("Device Properties is null for Device:" + device);
            return;
        }

        Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
        intent.putExtra(BluetoothDevice.EXTRA_CLASS,
                new BluetoothClass(deviceProp.mBluetoothClass));
        intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
        intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);

        mAdapterService.sendBroadcastMultiplePermissions(intent,
                new String[] {AdapterService.BLUETOOTH_PERM,
                        android.Manifest.permission.ACCESS_COARSE_LOCATION});
    }

只要是在上層app中注冊android.bluetooth.device.action.FOUND就都可以收到該廣播。

作者:貓疏
鏈接:http://www.reibang.com/p/4ce366089c1d

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末究反,一起剝皮案震驚了整個濱河市寻定,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌精耐,老刑警劉巖狼速,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卦停,居然都是意外死亡向胡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門惊完,熙熙樓的掌柜王于貴愁眉苦臉地迎上來僵芹,“玉大人蓖宦,你說我怎么就攤上這事晶密≈畛伲” “怎么了膜眠?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辣之。 經(jīng)常有香客問我战得,道長喷斋,這世上最難降的妖魔是什么控嗜? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任茧彤,我火速辦了婚禮,結(jié)果婚禮上疆栏,老公的妹妹穿的比我還像新娘曾掂。我一直安慰自己惫谤,他們只是感情好,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布遭殉。 她就那樣靜靜地躺著石挂,像睡著了一般博助。 火紅的嫁衣襯著肌膚如雪险污。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天富岳,我揣著相機與錄音蛔糯,去河邊找鬼。 笑死窖式,一個胖子當著我的面吹牛蚁飒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播萝喘,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼淮逻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了阁簸?” 一聲冷哼從身側(cè)響起爬早,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎启妹,沒想到半個月后筛严,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡饶米,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年桨啃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片檬输。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡照瘾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出丧慈,到底是詐尸還是另有隱情析命,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布伊滋,位于F島的核電站碳却,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏笑旺。R本人自食惡果不足惜昼浦,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望筒主。 院中可真熱鬧关噪,春花似錦鸟蟹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至虐沥,卻和暖如春熊经,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背欲险。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工镐依, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人天试。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓槐壳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親喜每。 傳聞我的和親對象是個殘疾皇子务唐,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355