安卓蓝牙扫描流程

目录

系统广播

流程图

源码跟踪


系统广播

扫描开启广播:BluetoothAdapter.ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";

扫描关闭广播:BluetoothAdapter.ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";

扫描结果广播:BluetoothAdapter.ACTION_FOUND = "android.bluetooth.device.action.FOUND";

流程图

安卓系统中应用Application通过蓝牙适配器接口BluetoothAdapter.startDiscovery()的调用触发搜索流程的开始,从这里开始流程分析。

源码跟踪

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(getOpPackageName(), getAttributionTag());
            }
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
        } finally {
            mServiceLock.readLock().unlock();
        }
        return false;
    }

这里的Service.startDiscovery属于aidl跨进程通信,通过IBluetooth.aidl调用远程服务中packages\apps\Bluetooth\src\com\android\bluetooth\btservice\AdapterService.java中的startDiscovery

    boolean startDiscovery(String callingPackage, @Nullable String callingFeatureId) {
        UserHandle callingUser = UserHandle.of(UserHandle.getCallingUserId());
        debugLog("startDiscovery");
        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
        boolean isQApp = Utils.isQApp(this, callingPackage);
        String permission = null;
        if (Utils.checkCallerHasNetworkSettingsPermission(this)) {
            permission = android.Manifest.permission.NETWORK_SETTINGS;
        } else if (Utils.checkCallerHasNetworkSetupWizardPermission(this)) {
            permission = android.Manifest.permission.NETWORK_SETUP_WIZARD;
        } else if (isQApp) {
            if (!Utils.checkCallerHasFineLocation(this, mAppOps, callingPackage, callingFeatureId,
                    callingUser)) {
                return false;
            }
            permission = android.Manifest.permission.ACCESS_FINE_LOCATION;
        } else {
            if (!Utils.checkCallerHasCoarseLocation(this, mAppOps, callingPackage, callingFeatureId,
                    callingUser)) {
                return false;
            }
            permission = android.Manifest.permission.ACCESS_COARSE_LOCATION;
        }

        synchronized (mDiscoveringPackages) {
            mDiscoveringPackages.add(new DiscoveringPackage(callingPackage, permission));
        }
        return startDiscoveryNative();
    }

调用packages\apps\Bluetooth\jni\com_android_bluetooth_btservice_AdapterService.cpp中的startDiscoveryNative

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

调到system\bt\btif\src\bluetooth.cc中的start_discovery

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用于设备管理相关功能

/*******************************************************************************
 *
 * Function         btif_dm_start_discovery
 *
 * Description      Start device discovery/inquiry
 *
 * Returns          bt_status_t
 *
 ******************************************************************************/
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_main_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_main_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;
}

再调到system\bt\bta\dm\bta_dm_api.cc中的BTA_DmSearch

/*******************************************************************************
 *
 * Function         BTA_DmSearch
 *
 * Description      This function searches for peer Bluetooth devices. It
 *                  performs an inquiry and gets the remote name for devices.
 *                  Service discovery is done if services is non zero
 *
 *
 * Returns          void
 *
 ******************************************************************************/
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中bta_dm_rs_cback

/*******************************************************************************
 *
 * Function         bta_dm_rs_cback
 *
 * Description      Receives the role switch complete event
 *
 * Returns
 *
 ******************************************************************************/
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方法

/*******************************************************************************
 *
 * Function         bta_dm_search_start
 *
 * Description      Starts an inquiry
 *
 *
 * Returns          void
 *
 ******************************************************************************/
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中之前保存的搜索数据
  /* 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);//调用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中之前的搜索数据,然后保存搜索的参数,调用btm中BTM_StartInquiry方法进行扫描。btm中的扫描在system\bt\stack\btm\btm_inq.cc里

/*******************************************************************************
 *
 * Function         BTM_StartInquiry
 *
 * Description      This function is called to start an inquiry.
 *
 * Parameters:      p_inqparms - pointer to the inquiry information
 *                      mode - GENERAL or LIMITED inquiry, BR/LE bit mask
 *                             seperately
 *                      duration - length in 1.28 sec intervals (If '0', the
 *                                 inquiry is CANCELLED)
 *                      max_resps - maximum amount of devices to search for
 *                                  before ending the inquiry
 *                      filter_cond_type - BTM_CLR_INQUIRY_FILTER,
 *                                         BTM_FILTER_COND_DEVICE_CLASS, or
 *                                         BTM_FILTER_COND_BD_ADDR
 *                      filter_cond - value for the filter (based on
 *                                                          filter_cond_type)
 *
 *                  p_results_cb   - Pointer to the callback routine which gets
 *                                called upon receipt of an inquiry result. If
 *                                this field is NULL, the application is not
 *                                notified.
 *
 *                  p_cmpl_cb   - Pointer to the callback routine which gets
 *                                called upon completion.  If this field is
 *                                NULL, the application is not notified when
 *                                completed.
 * Returns          tBTM_STATUS
 *                  BTM_CMD_STARTED if successfully initiated
 *                  BTM_BUSY if already in progress
 *                  BTM_ILLEGAL_VALUE if parameter(s) are out of range
 *                  BTM_NO_RESOURCES if could not allocate resources to start
 *                                   the command
 *                  BTM_WRONG_MODE if the device is not up.
 *
 ******************************************************************************/
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;

  if (bluetooth::shim::is_gd_shim_enabled()) {
    return bluetooth::shim::BTM_StartInquiry(p_inqparms, p_results_cb,
                                             p_cmpl_cb);
  }

  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) {
    LOG(ERROR) << __func__ << ": BTM_BUSY";
    return (BTM_BUSY);
  } else {
    p_inq->scan_type = INQ_GENERAL;
  }

  /*** Make sure the device is ready ***/
  if (!BTM_IsDeviceUp()) {
    LOG(ERROR) << __func__ << ": adapter is not up";
    return BTM_WRONG_MODE;
  }

  if ((p_inqparms->mode & BTM_BR_INQUIRY_MASK) != BTM_GENERAL_INQUIRY &&
      (p_inqparms->mode & BTM_BR_INQUIRY_MASK) != BTM_LIMITED_INQUIRY &&
      (p_inqparms->mode & BTM_BLE_INQUIRY_MASK) != BTM_BLE_GENERAL_INQUIRY &&
      (p_inqparms->mode & BTM_BLE_INQUIRY_MASK) != BTM_BLE_LIMITED_INQUIRY) {
    LOG(ERROR) << __func__ << ": illegal inquiry mode "
               << std::to_string(p_inqparms->mode);
    return (BTM_ILLEGAL_VALUE);
  }

  /* Save the inquiry parameters to be used upon the completion of
   * setting/clearing the inquiry filter */
  p_inq->inqparms = *p_inqparms;

  /* Initialize the inquiry variables */
  p_inq->state = BTM_INQ_ACTIVE_STATE;
  p_inq->p_inq_cmpl_cb = p_cmpl_cb;
  p_inq->p_inq_results_cb = p_results_cb;
  p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */
  p_inq->inq_active = p_inqparms->mode;

  BTM_TRACE_DEBUG("BTM_StartInquiry: p_inq->inq_active = 0x%02x",
                  p_inq->inq_active);

  tBTM_STATUS status = BTM_CMD_STARTED;
  /* start LE inquiry here if requested */
  if ((p_inqparms->mode & BTM_BLE_INQUIRY_MASK)) {
    if (!controller_get_interface()->supports_ble()) {
      LOG(ERROR) << __func__ << ": trying to do LE scan on a non-LE adapter";
      p_inq->inqparms.mode &= ~BTM_BLE_INQUIRY_MASK;
      status = BTM_ILLEGAL_VALUE;
    } else {
      /* BLE for now does not support filter condition for inquiry */
      status = btm_ble_start_inquiry(
          (uint8_t)(p_inqparms->mode & BTM_BLE_INQUIRY_MASK),
          p_inqparms->duration);
      if (status != BTM_CMD_STARTED) {
        LOG(ERROR) << __func__ << ": Error Starting LE Inquiry";
        p_inq->inqparms.mode &= ~BTM_BLE_INQUIRY_MASK;
      }
    }
    p_inqparms->mode &= ~BTM_BLE_INQUIRY_MASK;

    BTM_TRACE_DEBUG("BTM_StartInquiry: mode = %02x", p_inqparms->mode);
  }

  /* we're done with this routine if BR/EDR inquiry is not desired. */
  if ((p_inqparms->mode & BTM_BR_INQUIRY_MASK) == BTM_INQUIRY_NONE) {
    return status;
  }

  /* BR/EDR inquiry portion */
  /* If a filter is specified, then save it for later and clear the current
     filter.
     The setting of the filter is done upon completion of clearing of the
     previous
     filter.
  */
  switch (p_inqparms->filter_cond_type) {
    case BTM_CLR_INQUIRY_FILTER:
      p_inq->state = BTM_INQ_SET_FILT_STATE;
      break;

    case BTM_FILTER_COND_DEVICE_CLASS:
    case BTM_FILTER_COND_BD_ADDR:
      /* The filter is not being used so simply clear it;
          the inquiry can start after this operation */
      p_inq->state = BTM_INQ_CLR_FILT_STATE;
      p_inqparms->filter_cond_type = BTM_CLR_INQUIRY_FILTER;
      /* =============>>>> adding LE filtering here ????? */
      break;

    default:
      LOG(ERROR) << __func__ << ": invalid filter condition type "
                 << std::to_string(p_inqparms->filter_cond_type);
      return (BTM_ILLEGAL_VALUE);
    }

    /* Before beginning the inquiry the current filter must be cleared, so
     * initiate the command */
    status = btm_set_inq_event_filter(p_inqparms->filter_cond_type,
                                      &p_inqparms->filter_cond);
    if (status != BTM_CMD_STARTED) {
      LOG(ERROR) << __func__ << ": failed to set inquiry event filter";
      p_inq->state = BTM_INQ_INACTIVE_STATE;
    }

    return (status);
}

调用system\bt\stack\btm\btm_ble_gap.cc 中的btm_ble_start_inquiry

/*******************************************************************************
 *
 * Function         btm_ble_start_inquiry
 *
 * Description      This function is called to start BLE inquiry procedure.
 *                  If the duration is zero, the periodic inquiry mode is
 *                  cancelled.
 *
 * Parameters:      mode - GENERAL or LIMITED inquiry
 *                  p_inq_params - pointer to the BLE inquiry parameter.
 *                  p_results_cb - callback returning pointer to results
 *                                 (tBTM_INQ_RESULTS)
 *                  p_cmpl_cb - callback indicating the end of an inquiry
 *
 *
 *
 * Returns          BTM_CMD_STARTED if successfully started
 *                  BTM_NO_RESOURCES if could not allocate a message buffer
 *                  BTM_BUSY - if an inquiry is already active
 *
 ******************************************************************************/
tBTM_STATUS btm_ble_start_inquiry(uint8_t mode, uint8_t duration) {
  tBTM_STATUS status = BTM_CMD_STARTED;
  tBTM_BLE_CB* p_ble_cb = &btm_cb.ble_ctr_cb;
  tBTM_INQUIRY_VAR_ST* p_inq = &btm_cb.btm_inq_vars;

  BTM_TRACE_DEBUG("btm_ble_start_inquiry: mode = %02x inq_active = 0x%02x",
                  mode, btm_cb.btm_inq_vars.inq_active);

  /* if selective connection is active, or inquiry is already active, reject it
   */
  if (BTM_BLE_IS_INQ_ACTIVE(p_ble_cb->scan_activity)) {
    BTM_TRACE_ERROR("LE Inquiry is active, can not start inquiry");
    return (BTM_BUSY);
  }

  if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) {
    btm_send_hci_set_scan_params(
        BTM_BLE_SCAN_MODE_ACTI, BTM_BLE_LOW_LATENCY_SCAN_INT,
        BTM_BLE_LOW_LATENCY_SCAN_WIN,
        btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, SP_ADV_ALL);
#if (BLE_PRIVACY_SPT == TRUE)
    /* enable IRK list */
    btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN);
#endif
    p_ble_cb->inq_var.scan_type = BTM_BLE_SCAN_MODE_ACTI;
    p_ble_cb->inq_var.scan_duplicate_filter = BTM_BLE_DUPLICATE_DISABLE;
    status = btm_ble_start_scan();
  } else if ((p_ble_cb->inq_var.scan_interval !=
              BTM_BLE_LOW_LATENCY_SCAN_INT) ||
             (p_ble_cb->inq_var.scan_window != BTM_BLE_LOW_LATENCY_SCAN_WIN)) {
    BTM_TRACE_DEBUG("%s, restart LE scan with low latency scan params",
                    __func__);
    btm_send_hci_scan_enable(BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE);
    btm_send_hci_set_scan_params(
        BTM_BLE_SCAN_MODE_ACTI, BTM_BLE_LOW_LATENCY_SCAN_INT,
        BTM_BLE_LOW_LATENCY_SCAN_WIN,
        btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, SP_ADV_ALL);
    btm_send_hci_scan_enable(BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE);
  }

  if (status == BTM_CMD_STARTED) {
    p_inq->inq_active |= mode;
    p_ble_cb->scan_activity |= mode;

    BTM_TRACE_DEBUG("btm_ble_start_inquiry inq_active = 0x%02x",
                    p_inq->inq_active);

    if (duration != 0) {
      /* start inquiry timer */
      uint64_t duration_ms = duration * 1000;
      alarm_set_on_mloop(p_ble_cb->inq_var.inquiry_timer, duration_ms,
                         btm_ble_inquiry_timer_timeout, NULL);
    }
  }

  return status;
}

再调用btm_send_hci_scan_enable向HCI发送扫描通知

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扩展,则调用system\bt\stack\hcic\hciblecmds.cc中的btsnd_hcic_ble_set_scan_enable

void btsnd_hcic_ble_set_scan_enable(uint8_t scan_enable, uint8_t duplicate) {
  BT_HDR* p = (BT_HDR*)osi_malloc(HCI_CMD_BUF_SIZE);
  uint8_t* pp = (uint8_t*)(p + 1);

  p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE;
  p->offset = 0;

  UINT16_TO_STREAM(pp, HCI_BLE_WRITE_SCAN_ENABLE);
  UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE);

  UINT8_TO_STREAM(pp, scan_enable);
  UINT8_TO_STREAM(pp, duplicate);

  btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);//这里向hci层发命令
}

这里通过和HCI层的通信,host告诉controlor蓝牙地址、数据、命令等,从而控制底层硬件发起配对操作。btu如何与hci通信也比较复杂...

扫描结果返回

主要看下扫描到的设备信息返回。开始扫描和结束扫描的流程见图

之前在调用了system\bt\bta\dm\bta_dm_act.cc中bta_dm_search_start时,传了三个参数,第一个是启动扫描所需传下去的参数,第二个bta_dm_inq_results_cb是扫描结果返回的回调,第三个是扫描完成的回调。

  result.status = BTM_StartInquiry((tBTM_INQ_PARMS*)&p_data->search.inq_params,
                                   bta_dm_inq_results_cb, bta_dm_inq_cmpl_cb);//调用btm中的启动扫描
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;
  }
}

在这里把扫描的devices数据放到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方法

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切换到BTIF再调用btif_dm_search_devices_evt,将扫描到的设备通过HAL_CBACK方式返回

/******************************************************************************
 *
 * Function         btif_dm_search_devices_evt
 *
 * Description      Executes search devices callback events in btif context
 *
 * Returns          void
 *
 *****************************************************************************/
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;
......省略
}

扫描到设备时,调用HAL_CBACK(bt_hal_cbacks, device_found_cb, num_properties, properties);

device_found_cb是device_found_callback方法,在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,即

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

对应JniCallbacks.java中的deviceFoundCallback方法

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

        final ArrayList<DiscoveringPackage> packages = sAdapterService.getDiscoveringPackages();
        synchronized (packages) {
            for (DiscoveringPackage pkg : packages) {
                intent.setPackage(pkg.getPackageName());
                sAdapterService.sendBroadcastMultiplePermissions(intent, new String[]{
                        AdapterService.BLUETOOTH_PERM, pkg.getPermission()
                });
            }
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/942171.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Pytorch | 利用BIM/I-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击

Pytorch | 利用BIM/I-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击 CIFAR数据集BIM介绍基本原理算法流程 BIM代码实现BIM算法实现攻击效果 代码汇总bim.pytrain.pyadvtest.py 之前已经针对CIFAR10训练了多种分类器&#xff1a; Pytorch | 从零构建AlexNet对CIFAR10进行分类 Py…

如何查看pad的console输出,以便我们更好的进行调试,查看并了解实际可能的问题。

1、以下是baidu AI回复&#xff1a; 2、说明&#xff1a; 1&#xff09;如果小伙伴们经常做android开发的话&#xff0c;这个不陌生&#xff0c;因为调试都是要开启这个开发者模式。并启用USB调试模式。 2&#xff09;需要连上USB线&#xff0c;有的时候会忘记&#xff0c;然…

外贸企业需要部署SD-WAN专线吗?

随着外贸行业对互联网和数字化技术依赖的加深&#xff0c;网络质量已成为影响企业运营效率和竞争力的重要因素。本文将深入探讨SD-WAN专线如何助力外贸企业优化业务运营。 外贸企业面临的网络挑战 1. 跨国访问速度缓慢 在访问海外服务器或目标网站时&#xff0c;外贸企业常常遭…

MySQL什么情况下会导致索引失效

MySQL什么情况下会导致索引失效 索引&#xff08;Index&#xff09;是数据库中一种用于快速查找和访问表中数据的结构&#xff0c;它类似于书的目录&#xff0c;通过索引可以快速定位到目标数据&#xff0c;而无需遍历整个表&#xff0c;索引的存在可以显著提高查询速度&#x…

两分钟解决:vscode卡在设置SSH主机,VS Code-正在本地初始化VSCode服务器

问题原因 remote-ssh还是有一些bug的&#xff0c;在跟新之后可能会一直加载初始化SSH主机解决方案 1.打开终端2.登录链接vscode的账号&#xff0c;到家目录下3.找到 .vscode-server文件,删掉这个文件4.重启 vscode 就没问题了

uniapp登录

第一步整登录 先整个appid APPID和APPSecret https://developers.weixin.qq.com/community/develop/article/doc/000ca4601b8f70e379febac985b413 一个账号只能整一个小程序 正确流程 调用uni.login https://juejin.cn/post/7126553599445827621 https://www.jb51.net/a…

I.MX6U 启动方式详解

一、启动方式选择 BOOT 的处理过程是发生在 I.MX6U 芯片上电以后,芯片会根据 BOOT_MODE[1:0]的设置 来选择 BOOT 方式。 BOOT_MODE[1:0]的值是可以改变的,有两种方式,一种是改写 eFUSE(熔 丝),一种是修改相应的 GPIO 高低电平。第一种修改 eFUSE 的方式只能修改一次,后面就…

项目代码第6讲:UpdownController.cs;理解 工艺/工序 流程、机台信息;前端的“历史 警报/工艺 记录”;每个机台各个管道的数据(温度、压力、气体)

一、UpdownController.cs 1、前端传入 当用户在下图的“记录查询”中的 两个界面选项 中,点击“导出”功能时,向后端发起请求,请求服务器下载文件的权限 【权限是在Program.cs中检测的,这个控制器里只需要进行“谁在哪个接口下载了文件”的日志记录】 【导出:是用户把…

WebRTC搭建与应用(五)-Coturn踩坑记

WebRTC搭建与应用(五)-Coturn踩坑记 近期由于项目需要在研究前端WebGL渲染转为云渲染&#xff0c;借此机会对WebRTC等有了初步了解&#xff0c;在此记录一下&#xff0c;以防遗忘。 第五章 WebRTC搭建与应用(五)-Coturn踩坑记 文章目录 WebRTC搭建与应用(五)-Coturn踩坑记前…

JVM简介—3.JVM的执行子系统

大纲 1.Class文件结构 2.Class文件格式概述 3.Class文件格式详解 4.字节码指令 5.类的生命周期和初始化 6.类加载的全过程 7.类加载器 8.双亲委派模型 9.栈桢详解 11.方法调用详解 12.基于栈的字节码解释执行引擎 1.Class文件结构 (1)Java跨平台的基础 字节码是各…

将自定义或第三方库的jar包引入项目中

文章目录 1.背景2.实现 1.背景 个人项目中引入了某个免费版框架有字数限制&#xff0c;我们业务需要生成字数很多&#xff0c;超过了限制&#xff0c;现在要引入自定义的jar解决问题。 2.实现 在resource文件夹下建lib文件夹 (属于是约定)&#xff0c;将自己的jar包放入 然后…

STL格式转换为FBX格式

STL格式与FBX格式简介 STL&#xff08;Stereo Lithography&#xff09;文件是一种用于3D打印的文件格式。它是由3D Systems公司开发的一种二进制文件格式&#xff0c;用于立体光刻技术。 FBX格式支持多边形游戏模型、曲线、表面、点组材质。FBX文件格式支持所有主要的三维数据…

VMware虚拟机三种网络工作模式

vmware为我们提供了三种网络工作模式,它们分别是:Bridged(桥接模式)、NAT(网络地址转换模式)、Host-Only(仅主机模式)。 打开vmware虚拟机,我们可以在选项栏的“编辑”下的“虚拟网络编辑器”中看到VMnet0(桥接模式)、VMnet1(仅主机模式)、VMnet8(NAT模式),那…

AI 技术,让洗护行业焕然「衣」新

根据最新的 Location 数据显示&#xff0c;国内目前有 20.79 万家与洗衣服务相关的企业。其中超过 80% 仍然是传统的夫妻店模式&#xff0c;即前店收衣后店洗衣的小型洗衣店。这种模式通常规模较小&#xff0c;服务范围有限&#xff0c;主要依赖于店主的个人经营。 另外 20% 企…

Hadoop集群(HDFS集群、YARN集群、MapReduce​计算框架)

一、 简介 Hadoop主要在分布式环境下集群机器&#xff0c;获取海量数据的处理能力&#xff0c;实现分布式集群下的大数据存储和计算。 其中三大核心组件: HDFS存储分布式文件存储、YARN分布式资源管理、MapReduce分布式计算。 二、工作原理 2.1 HDFS集群 Web访问地址&…

HDR视频技术之十:MPEG 及 VCEG 的 HDR 编码优化

与传统标准动态范围&#xff08; SDR&#xff09;视频相比&#xff0c;高动态范围&#xff08; HDR&#xff09;视频由于比特深度的增加提供了更加丰富的亮区细节和暗区细节。最新的显示技术通过清晰地再现 HDR 视频内容使得为用户提供身临其境的观看体验成为可能。面对目前日益…

精准提升:从94.5%到99.4%——目标检测调优全纪录

&#x1f680; 目标检测模型调优过程记录 在进行目标检测模型的训练过程中&#xff0c;我们面对了许多挑战与迭代。从初始模型的训练结果到最终的调优优化&#xff0c;每一步的实验和调整都有其独特的思路和收获。本文记录了我在优化目标检测模型的过程中进行的几次尝试&#…

Hadoop中MapReduce过程中Shuffle过程实现自定义排序

文章目录 Hadoop中MapReduce过程中Shuffle过程实现自定义排序一、引言二、实现WritableComparable接口1、自定义Key类 三、使用Job.setSortComparatorClass方法2、设置自定义排序器3、自定义排序器类 四、使用示例五、总结 Hadoop中MapReduce过程中Shuffle过程实现自定义排序 一…

论文《Vertical Federated Learning: Concepts, Advances, and Challenges》阅读

论文《Vertical Federated Learning: Concepts, Advances, and Challenges》阅读 论文概况纵向联邦VFL框架介绍问题定义VFL 训练协议 对通信效率的优化对性能的优化自监督方案&#xff08;Self-Supervised Approaches&#xff09;半监督方案&#xff08;Semi-Supervised Approa…

【Rust自学】4.5. 切片(Slice)

4.5.0. 写在正文之前 这是第四章的最后一篇文章了&#xff0c;在这里也顺便对这章做一个总结&#xff1a; 所有权、借用和切片的概念确保 Rust 程序在编译时的内存安全。 Rust语言让程序员能够以与其他系统编程语言相同的方式控制内存使用情况&#xff0c;但是当数据所有者超…