安卓藍牙掃描流程

目錄

系統廣播

流程圖

源碼跟蹤


系統廣播

掃描開啟廣播: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 currentfilter.The setting of the filter is done upon completion of clearing of thepreviousfilter.*/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);
#endifp_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 callbackcopy 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// nowBluetoothDevice 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()});}}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/63942.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/63942.shtml
英文地址,請注明出處:http://en.pswp.cn/web/63942.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

shell 編程(三)

條件測試命令 條件測試&#xff1a;判斷某需求是否滿足&#xff0c;需要有測試機制來實現 專用的測試表達式需要由測試命令輔助完成測試過程&#xff0c;評估布爾生命&#xff0c;以便用在條件性執行中 若真,則狀態碼變量$? 返回0 // echo $? 打印0 反之返回1 t…

八股(One Day one)

最近老是看到一些面試的視頻&#xff0c;對于視頻內部面試所提到的八股文&#xff0c;感覺是知道是什么&#xff0c;但是要說的話&#xff0c;卻又不知道該怎么說&#xff08;要不咋稱之為八股文呢&#xff09;&#xff0c;所以就想到寫一篇八股文總結的博客&#xff0c;以便進…

Rust 在前端基建中的使用

摘要 隨著前端技術的不斷發展&#xff0c;前端基礎設施&#xff08;前端基建&#xff09;的建設已成為提升開發效率、保障產品質量的關鍵環節。然而&#xff0c;在應對復雜業務場景與高性能需求時&#xff0c;傳統的前端技術棧逐漸暴露出諸多不足。近年來&#xff0c;Rust語言…

豆包MarsCode:a替換函數

問題描述 思路分析 在這個問題中&#xff0c;我們的目標是將字符串中的所有小寫字母 a 替換為 "%100"。為了實現這一點&#xff0c;我們需要分析問題的核心需求和合理的解決方案。以下是分析和思路的詳細步驟&#xff1a; 1. 理解問題 給定一個字符串 s&#xff0…

人臉生成3d模型 Era3D

從單視圖圖像進行3D重建是計算機視覺和圖形學中的一項基本任務&#xff0c;因為它在游戲設計、虛擬現實和機器人技術中具有潛在的應用價值。早期的研究主要依賴于直接在體素上進行3D回歸&#xff0c;這往往會導致過于平滑的結果&#xff0c;并且由于3D訓練數據的限制&#xff0…

【點估計】之Python實現

點估計是一種統計推斷方法,它利用樣本數據來估計總體的未知參數。在概率論和數理統計的框架下,點估計將總體的未知參數視為一個確定的值或一個具體的點,并試圖通過樣本數據來找到這個值的最佳估計。以下是對點估計的詳細解釋: 一、定義與原理 定義:點估計是根據樣本數據估…

rust與python互通

互通三件套 rust側與python互通的三個庫&#xff1a; pyo3 pythonize serde pyo3 pyo3跟用Python C API寫python擴展有點類似&#xff0c;核心是&#xff1a; #[pymodule] #[pyfunction]兩個注解。前者對應Py_InitModule&#xff0c;后者對應PyMethodDef。 下面是其它博…

Ubuntu系統下 npm install -g tauri 報錯問題處理

處理在安裝 Tauri 時遇到的問題&#xff0c;可以按照以下步驟進行操作 npm install -g taurinpm warn deprecated inflight1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async …

信貸域——互聯網金融理論基礎

摘要 互聯網金融這種新興的金融業態近幾年飛速發展&#xff0c;規模不斷擴大&#xff0c;互聯網金融在對我國金融體系和經濟發展影響中所占的分量越來越重&#xff0c;一定程度上也推動了互聯網金融理論的發展。 互聯網金融與傳統金融都是金融&#xff0c;有著相近的理論基礎。…

C++軟件設計模式之享元模式(FlyWeight)

享元&#xff08;Flyweight&#xff09;模式的動機與意圖 動機 享元模式的主要動機是通過共享對象來減少內存使用&#xff0c;從而提高系統的性能。在某些情況下&#xff0c;系統中可能有大量細粒度的對象&#xff0c;這些對象具有共同的部分狀態&#xff0c;而這些狀態可以共…

LightGBM分類算法在醫療數據挖掘中的深度探索與應用創新(上)

一、引言 1.1 醫療數據挖掘的重要性與挑戰 在當今數字化醫療時代,醫療數據呈爆炸式增長,這些數據蘊含著豐富的信息,對醫療決策具有極為重要的意義。通過對醫療數據的深入挖掘,可以發現潛在的疾病模式、治療效果關聯以及患者的健康風險因素,從而為精準醫療、個性化治療方…

|-牛式-|

題目描述 下面是一個乘法豎式&#xff0c;如果用我們給定的那幾個數字來取代 * &#xff0c;可以使式子成立的話&#xff0c;我們就叫這個式子牛式。 * * * x * * ------- * * * * * * ------- * * * * 數字只能取代 * &#xff0c;當然第一位不能為 0 。 寫一個程序找…

es 3期 第18節-分頁查詢使用避坑的一些事

#### 1.Elasticsearch是數據庫&#xff0c;不是普通的Java應用程序&#xff0c;傳統數據庫需要的硬件資源同樣需要&#xff0c;提升性能最有效的就是升級硬件。 #### 2.Elasticsearch是文檔型數據庫&#xff0c;不是關系型數據庫&#xff0c;不具備嚴格的ACID事務特性&#xff…

STM32串口第一次接收數據時第一個字節丟失的問題

解決方法&#xff1a;開啟中斷之前&#xff0c;先清除標志位【1】。 串口清除標志位&#xff1a; __HAL_UART_CLEAR_PEFLAG(&huart1); HAL_UART_Receive_IT(&huart1,&RxUart, 1); 定時器清除標志位&#xff1a; __HAL_TIM_CLEAR_FLAG(&htim3,TIM_FLAG_UPDATE);…

深度學習中的殘差網絡、加權殘差連接(WRC)與跨階段部分連接(CSP)詳解

隨著深度學習技術的不斷發展&#xff0c;神經網絡架構變得越來越復雜&#xff0c;而這些復雜網絡在訓練時常常遇到梯度消失、梯度爆炸以及計算效率低等問題。為了克服這些問題&#xff0c;研究者們提出了多種網絡架構&#xff0c;包括 殘差網絡&#xff08;ResNet&#xff09;、…

Pytorch | 從零構建EfficientNet對CIFAR10進行分類

Pytorch | 從零構建EfficientNet對CIFAR10進行分類 CIFAR10數據集EfficientNet設計理念網絡結構性能特點應用領域發展和改進 EfficientNet結構代碼詳解結構代碼代碼詳解MBConv 類初始化方法前向傳播 forward 方法 EfficientNet 類初始化方法前向傳播 forward 方法 訓練過程和測…

Vue 2 中實現雙擊事件的幾種方法

在 Vue 2 中處理用戶交互&#xff0c;特別是雙擊事件&#xff0c;是一個常見的需求。Vue 提供了一種簡潔的方式來綁定事件&#xff0c;包括雙擊事件。本文將介紹幾種在 Vue 2 中實現雙擊事件的方法。 1. 使用 dblclick 指令 Vue 允許你直接在模板中使用 dblclick 指令來監聽雙…

音視頻入門基礎:MPEG2-TS專題(20)——ES流簡介

《T-REC-H.222.0-202106-S!!PDF-E.pdf》第27頁對ES進行了定義。ES流是PES packets&#xff08;PES包&#xff09;中編碼的視頻、編碼的音頻或其他編碼的比特流。一個ES流&#xff08;elementary stream&#xff09;在具有且只有一個stream_id的PES packets序列中攜帶&#xff1…

天水月亮圈圈:舌尖上的歷史與傳承

在天水甘谷縣&#xff0c;有一種美食如同夜空中的明月&#xff0c;散發著獨特的魅力&#xff0c;它就是有著百年歷史的月亮圈圈。月亮圈圈原名甘谷酥圈圈&#xff0c;據傳&#xff0c;由大像山鎮蔣家莊一姓李的廚師創制而成&#xff0c;后經王明玖等廚師的光大傳承&#xff0c;…

YOLOv11融合[CVPR2023]FFTformer中的FSAS模塊

YOLOv11v10v8使用教程&#xff1a; YOLOv11入門到入土使用教程 YOLOv11改進匯總貼&#xff1a;YOLOv11及自研模型更新匯總 《Efficient Frequency Domain-based Transformers for High-Quality Image Deblurring》 一、 模塊介紹 論文鏈接&#xff1a;https://arxiv.org/abs…