Android 9.0 藍牙掃描流程

昨天梳理了藍牙的開啓流程,今天梳理一遍掃描流程:

1、UI

/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();
}

在這裏如果沒有開始掃描就清除緩存中所有的設備,然後將緩存的設備中沒有配對的設備清除,最後調用了父類 DeviceListPreferenceFragment 的enableScanning()方法:

/packages/apps/Settings/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java

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

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

2、framework

/frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
LocalBluetoothAdapter的startScanning方法:

public void startScanning(boolean force) {
    // Only start if we're not already scanning
    if (!mAdapter.isDiscovering()) {
        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;
            }
            A2dpSinkProfile a2dpSink = mProfileManager.getA2dpSinkProfile();
            if ((a2dpSink != null) && (a2dpSink.isA2dpPlaying())){
                return;
            }
        }

        if (mAdapter.startDiscovery()) {
            mLastScan = System.currentTimeMillis();
        }
    }
}

/frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java
這裏最後調用frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java的startDiscovery方法,啓動掃描任務

public boolean startDiscovery() {
    if (getState() != STATE_ON) {
        return false;
    }
    try {
        mServiceLock.readLock().lock();
        if (mService != null) {
            return mService.startDiscovery();
        }
    } catch (RemoteException e) {
        Log.e(TAG, "", e);
    } finally {
        mServiceLock.readLock().unlock();
    }
    return false;
}

3、Bluetooth App

這裏的 service.startDiscovery 屬於 aidl 跨進程通信,通過 IBluetooth.aidl 調用遠程服務 /packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService 中的 startDiscovery 方法:
/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java

boolean setDiscoverableTimeout(int timeout) {
    enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");

    return mAdapterProperties.setDiscoverableTimeout(timeout);
}

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

private native boolean startDiscoveryNative();

int ret = sBluetoothInterface->start_discovery() 這行代碼調進了hal層的藍牙協議棧中。

/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

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

  if (!sBluetoothInterface) return JNI_FALSE;

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

4、藍牙協議棧\

/system/bt/btif/src/bluetooth.cc

static int start_discovery(void) {
  /* sanity check */
  if (!interface_ready()) return BT_STATUS_NOT_READY;

  return btif_dm_start_discovery();
}

start_discovery方法調用/system/bt/btif/src/btif_dm.cc的btif_dm_start_discovery方法,btif_dm.cc 用於設備管理相關的功能。

/system/bt/btif/src/btif_dm.cc

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

  BTIF_TRACE_EVENT("%s", __func__);

  /* 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;
  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方法中最終調用 /system/bt/bta/dm/bta_dm_api.cc中的 BTA_DmSearch(&inq_params, services, bte_search_devices_evt);

/system/bt/bta/dm/bta_dm_api.cc

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 =
      (tBTA_DM_API_SEARCH*)osi_calloc(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_sys_sendmsg向BTA發送掃描任務消息。bta_sys_sendmsg這是藍牙協議棧的進程間收發消息的機制。

通過搜索event標記BTA_DM_API_SEARCH_EVT可以找到/system/bt/bta/dm/bta_dm_act.cc裏面:
/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);
  }
}

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

/system/bt/bta/dm/bta_dm_act.cc

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);
  /* 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);

  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);
  }
}

5、掃描結果返回

之前調用了在/system/bt/bta/dm/bta_dm_act.cc裏面的bta_dm_search_start方法時,關鍵代碼是:

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

這裏可以看到在調用btm的開始掃描方法的時候會傳入三個參數,第一個就是啓動掃描所需要傳下去的參數,第二個bta_dm_inq_results_cb就是掃描結果的回調,第三個bta_dm_inq_cmpl_cb就是掃描完成的回調。也就是說btm啓動掃描之後,掃描到藍牙設備之後會通過bta_dm_inq_results_cb這個回調返給bta,而掃描結束之後會通過bta_dm_inq_cmpl_cb這個回調返給bta。
這裏我們拿bta_dm_inq_results_cb這個回調來分析:

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數據放入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發現在/system/bt/btif/src/btif_dm.cc裏面有一個bte_search_devices_evt方法:

/system/bt/btif/src/btif_dm.cc

static void bte_search_devices_evt(tBTA_DM_SEARCH_EVT event,
                                   tBTA_DM_SEARCH* p_data) {
  uint16_t 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 += p_data->inq_res.eir_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", __func__,
                   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_t)event, (char*)p_data, param_len,
      (param_len > sizeof(tBTA_DM_SEARCH)) ? search_devices_copy_cb : NULL);
}

這個bte_search_devices_evt方法就是掃描任務的回調函數,當掃描到設備時,回調這個方法,將上下文從BTE切換到BTIF
再調用btif_dm_search_devices_evt,將掃描到的設備通過HAL_CBACK方式返回。

/system/bt/btif/src/btif_dm.cc

static void btif_dm_search_devices_evt(uint16_t event, char* p_param) {
  tBTA_DM_SEARCH* p_search_data;
  BTIF_TRACE_EVENT("%s event=%s", __func__, 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_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);
        RawAddress& bdaddr = 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? */
    } break;

    case BTA_DM_INQ_RES_EVT: {
      /* inquiry result */
      bt_bdname_t bdname;
      uint8_t remote_name_len;
      tBTA_SERVICE_MASK services = 0;

      p_search_data = (tBTA_DM_SEARCH*)p_param;
      RawAddress& bdaddr = p_search_data->inq_res.bd_addr;

      BTIF_TRACE_DEBUG("%s() %s device_type = 0x%x\n", __func__,
                       bdaddr.ToString().c_str(),
                       p_search_data->inq_res.device_type);
      bdname.name[0] = 0;

      if (!check_eir_remote_name(p_search_data, bdname.name, &remote_name_len))
        check_cached_remote_name(p_search_data, bdname.name, &remote_name_len);

      /* Check EIR for remote name and services */
      if (p_search_data->inq_res.p_eir) {
        BTA_GetEirService(p_search_data->inq_res.p_eir,
                          p_search_data->inq_res.eir_len, &services);
        BTIF_TRACE_DEBUG("%s()EIR BTA services = %08X", __func__,
                         (uint32_t)services);
        /* TODO:  Get the service list and check to see which uuids we got and
         * send it back to the client. */
      }

      {
        bt_property_t properties[5];
        bt_device_type_t dev_type;
        uint32_t num_properties = 0;
        bt_status_t status;
        int addr_type = 0;

        memset(properties, 0, sizeof(properties));
        /* RawAddress */
        BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                   BT_PROPERTY_BDADDR, sizeof(bdaddr), &bdaddr);
        num_properties++;
        /* BD_NAME */
        /* Don't send BDNAME if it is empty */
        if (bdname.name[0]) {
          BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                     BT_PROPERTY_BDNAME,
                                     strlen((char*)bdname.name), &bdname);
          num_properties++;
        }

        /* DEV_CLASS */
        uint32_t cod = devclass2uint(p_search_data->inq_res.dev_class);
        BTIF_TRACE_DEBUG("%s cod is 0x%06x", __func__, cod);
        if (cod != 0) {
          BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                     BT_PROPERTY_CLASS_OF_DEVICE, sizeof(cod),
                                     &cod);
          num_properties++;
        }

        int stored_device_type = 0;
        if (btif_get_device_type(bdaddr, &stored_device_type) &&
            ((stored_device_type != BT_DEVICE_TYPE_BREDR &&
              p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BREDR) ||
             (stored_device_type != BT_DEVICE_TYPE_BLE &&
              p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE))) {
          dev_type = (bt_device_type_t)BT_DEVICE_TYPE_DUMO;
        } else {
          dev_type = (bt_device_type_t)p_search_data->inq_res.device_type;
        }

        if (p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE)
          addr_type = p_search_data->inq_res.ble_addr_type;
        BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                   BT_PROPERTY_TYPE_OF_DEVICE, sizeof(dev_type),
                                   &dev_type);
        num_properties++;
        /* RSSI */
        BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
                                   BT_PROPERTY_REMOTE_RSSI, sizeof(int8_t),
                                   &(p_search_data->inq_res.rssi));
        num_properties++;

        status =
            btif_storage_add_remote_device(&bdaddr, num_properties, properties);
        ASSERTC(status == BT_STATUS_SUCCESS,
                "failed to save remote device (inquiry)", status);
        status = btif_storage_set_remote_addr_type(&bdaddr, addr_type);
        ASSERTC(status == BT_STATUS_SUCCESS,
                "failed to save remote addr type (inquiry)", status);
        /* Callback to notify upper layer of device */
        HAL_CBACK(bt_hal_cbacks, device_found_cb, num_properties, properties);
      }
    } break;

    case BTA_DM_INQ_CMPL_EVT: {
      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)));
    } break;
    case BTA_DM_DISC_CMPL_EVT: {
      HAL_CBACK(bt_hal_cbacks, discovery_state_changed_cb,
                BT_DISCOVERY_STOPPED);
    } break;
    case BTA_DM_SEARCH_CANCEL_CMPL_EVT: {
   
      if (!btif_dm_inquiry_in_progress) {
        btgatt_filt_param_setup_t adv_filt_param;
        memset(&adv_filt_param, 0, sizeof(btgatt_filt_param_setup_t));
        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)));
        HAL_CBACK(bt_hal_cbacks, discovery_state_changed_cb,
                  BT_DISCOVERY_STOPPED);
      }
    } break;
  }
}

掃描到設備時,調用的是HAL_CBACK(bt_hal_cbacks, device_found_cb,num_properties, properties);
device_found_cb即是device_found_callback方法,在com_android_bluetooth_btservice_AdapterService.cpp中實現

/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

static void device_found_callback(int num_properties,
                                  bt_property_t* properties) {
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;

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

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

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

  sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceFoundCallback,
                               addr.get());
}

該方法對應JNI方法爲method_deviceFoundCallback,即

/packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp

method_deviceFoundCallback =
    env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");

對應JniCallbacks.java的deviceFoundCallback方法。

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/JniCallbacks.java

void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] val) {
    mRemoteDevices.devicePropertyChangedCallback(address, types, val);
}

deviceFoundCallback將掃描到的設備通過廣播發送出去

/packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java

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);

    sAdapterService.sendBroadcastMultiplePermissions(intent, new String[]{
            AdapterService.BLUETOOTH_PERM, android.Manifest.permission.ACCESS_COARSE_LOCATION
    });
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章