Android wifi 扫描流程 分析

不飞则已,一飞冲天;不鸣则已,一鸣惊人。这篇文章主要讲述Android wifi 扫描流程 分析相关的知识,希望能为你提供帮助。

void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) { int res; if (wpa_s-> p2p_mgmt) { wpa_dbg(wpa_s, MSG_DEBUG, " Ignore scan request (%d.%06d sec) on p2p_mgmt interface" , sec, usec); return; }res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); if (res == 1) { wpa_dbg(wpa_s, MSG_DEBUG, " Rescheduling scan request: %d.%06d sec" , sec, usec); } else if (res == 0) { wpa_dbg(wpa_s, MSG_DEBUG, " Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner" , sec, usec); } else { wpa_dbg(wpa_s, MSG_DEBUG, " Setting scan request: %d.%06d sec" , sec, usec); eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); } }static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_ssid *ssid; int ret, p2p_in_prog; struct wpabuf *extra_ie = NULL; struct wpa_driver_scan_params params; struct wpa_driver_scan_params *scan_params; size_t max_ssids; int connect_without_scan = 0; wpa_s-> ignore_post_flush_scan_res = 0; // 接口未使能 if (wpa_s-> wpa_state == WPA_INTERFACE_DISABLED) { wpa_dbg(wpa_s, MSG_DEBUG, " Skip scan - interface disabled" ); return; }if (wpa_s-> disconnected & & wpa_s-> scan_req == NORMAL_SCAN_REQ) { wpa_dbg(wpa_s, MSG_DEBUG, " Disconnected - do not scan" ); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); return; } // 如果正在扫描, 推迟本次扫描 if (wpa_s-> scanning) { /* * If we are already in scanning state, we shall reschedule the * the incoming scan request. */ wpa_dbg(wpa_s, MSG_DEBUG, " Already scanning - Reschedule the incoming scan req" ); wpa_supplicant_req_scan(wpa_s, 1, 0); return; } // 查看是否有使能的ssid if (!wpa_supplicant_enabled_networks(wpa_s) & & wpa_s-> scan_req == NORMAL_SCAN_REQ) { wpa_dbg(wpa_s, MSG_DEBUG, " No enabled networks - do not scan" ); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); return; }if (wpa_s-> conf-> ap_scan != 0 & & (wpa_s-> drv_flags & WPA_DRIVER_FLAGS_WIRED)) { wpa_dbg(wpa_s, MSG_DEBUG, " Using wired authentication - " " overriding ap_scan configuration" ); wpa_s-> conf-> ap_scan = 0; wpas_notify_ap_scan_changed(wpa_s); }if (wpa_s-> conf-> ap_scan == 0) { wpa_supplicant_gen_assoc_event(wpa_s); return; }ssid = NULL; if (wpa_s-> scan_req != MANUAL_SCAN_REQ & & wpa_s-> connect_without_scan) { connect_without_scan = 1; for (ssid = wpa_s-> conf-> ssid; ssid; ssid = ssid-> next) { if (ssid == wpa_s-> connect_without_scan) break; } }p2p_in_prog = wpas_p2p_in_progress(wpa_s); if (p2p_in_prog & & p2p_in_prog != 2 & & (!ssid || (ssid-> mode != WPAS_MODE_AP & & ssid-> mode != WPAS_MODE_P2P_GO))) { wpa_dbg(wpa_s, MSG_DEBUG, " Delay station mode scan while P2P operation is in progress" ); wpa_supplicant_req_scan(wpa_s, 5, 0); return; }/* * Don' t cancel the scan based on ongoing PNO; defer it. Some scans are * used for changing modes inside wpa_supplicant (roaming, * auto-reconnect, etc). Discarding the scan might hurt these processes. * The normal use case for PNO is to suspend the host immediately after * starting PNO, so the periodic 100 ms attempts to run the scan do not * normally happen in practice multiple times, i.e., this is simply * restarting scanning once the host is woken up and PNO stopped. */ if (wpa_s-> pno || wpa_s-> pno_sched_pending) { wpa_dbg(wpa_s, MSG_DEBUG, " Defer scan - PNO is in progress" ); wpa_supplicant_req_scan(wpa_s, 0, 100000); return; }if (wpa_s-> conf-> ap_scan == 2) max_ssids = 1; else { max_ssids = wpa_s-> max_scan_ssids; if (max_ssids > WPAS_MAX_SCAN_SSIDS) max_ssids = WPAS_MAX_SCAN_SSIDS; }wpa_s-> last_scan_req = wpa_s-> scan_req; wpa_s-> scan_req = NORMAL_SCAN_REQ; // 如果设置了connect_without_scan, 连接之前选择的网络,不进行扫描,直接关联 if (connect_without_scan) { wpa_s-> connect_without_scan = NULL; if (ssid) { wpa_printf(MSG_DEBUG, " Start a pre-selected network " " without scan step" ); wpa_supplicant_associate(wpa_s, NULL, ssid); return; } }os_memset(& params, 0, sizeof(params)); // 设置状态为扫描 wpa_s-> scan_prev_wpa_state = wpa_s-> wpa_state; if (wpa_s-> wpa_state == WPA_DISCONNECTED || wpa_s-> wpa_state == WPA_INACTIVE) wpa_supplicant_set_state(wpa_s, WPA_SCANNING); /* * If autoscan has set its own scanning parameters */ if (wpa_s-> autoscan_params != NULL) { scan_params = wpa_s-> autoscan_params; goto scan; }if (wpa_s-> last_scan_req == MANUAL_SCAN_REQ & & wpa_set_ssids_from_scan_req(wpa_s, & params, max_ssids)) { wpa_printf(MSG_DEBUG, " Use specific SSIDs from SCAN command" ); goto ssid_list_set; }....../* Find the starting point from which to continue scanning */ // 查找开始扫描的ssid ssid = wpa_s-> conf-> ssid; // 从这里看,应该是链表里面下一个ssid if (wpa_s-> prev_scan_ssid != WILDCARD_SSID_SCAN) { while (ssid) { if (ssid == wpa_s-> prev_scan_ssid) { ssid = ssid-> next; break; } ssid = ssid-> next; } }if (wpa_s-> last_scan_req != MANUAL_SCAN_REQ & & #ifdef CONFIG_AP !wpa_s-> ap_iface & & #endif /* CONFIG_AP */ wpa_s-> conf-> ap_scan == 2) { wpa_s-> connect_without_scan = NULL; wpa_s-> prev_scan_wildcard = 0; wpa_supplicant_assoc_try(wpa_s, ssid); return; } else if (wpa_s-> conf-> ap_scan == 2) { /* * User-initiated scan request in ap_scan == 2; scan with * wildcard SSID. */ ssid = NULL; } else if (wpa_s-> reattach & & wpa_s-> current_ssid != NULL) { // 实行单信道,单个ssid扫描。难道是漫游的操作? // reassoicate重关联 /* * Perform single-channel single-SSID scan for * reassociate-to-same-BSS operation. */ /* Setup SSID */ ssid = wpa_s-> current_ssid; wpa_hexdump_ascii(MSG_DEBUG, " Scan SSID" , ssid-> ssid, ssid-> ssid_len); params.ssids[0].ssid = ssid-> ssid; params.ssids[0].ssid_len = ssid-> ssid_len; params.num_ssids = 1; /* * Allocate memory for frequency array, allocate one extra * slot for the zero-terminator. */ params.freqs = os_malloc(sizeof(int) * 2); if (params.freqs) { params.freqs[0] = wpa_s-> assoc_freq; params.freqs[1] = 0; }/* * Reset the reattach flag so that we fall back to full scan if * this scan fails. */ wpa_s-> reattach = 0; } else { struct wpa_ssid *start = ssid, *tssid; int freqs_set = 0; if (ssid == NULL & & max_ssids > 1) ssid = wpa_s-> conf-> ssid; // 获取配置文件中的ssid while (ssid) { if (!wpas_network_disabled(wpa_s, ssid) & & ssid-> scan_ssid) { wpa_hexdump_ascii(MSG_DEBUG, " Scan SSID" , ssid-> ssid, ssid-> ssid_len); params.ssids[params.num_ssids].ssid = ssid-> ssid; params.ssids[params.num_ssids].ssid_len = ssid-> ssid_len; params.num_ssids++; // 达到所能支持的最大ssid个数,退出循环 if (params.num_ssids + 1 > = max_ssids) break; } ssid = ssid-> next; if (ssid == start) break; if (ssid == NULL & & max_ssids > 1 & & start != wpa_s-> conf-> ssid) ssid = wpa_s-> conf-> ssid; }if (wpa_s-> scan_id_count & & wpa_s-> last_scan_req == MANUAL_SCAN_REQ) wpa_set_scan_ssids(wpa_s, & params, max_ssids); for (tssid = wpa_s-> conf-> ssid; wpa_s-> last_scan_req != MANUAL_SCAN_REQ & & tssid; tssid = tssid-> next) { // 网络关闭,不进行扫描 if (wpas_network_disabled(wpa_s, tssid)) continue; if ((params.freqs || !freqs_set) & & tssid-> scan_freq) { int_array_concat(& params.freqs, tssid-> scan_freq); } else { os_free(params.freqs); params.freqs = NULL; } freqs_set = 1; } // 对频率进行排序 int_array_sort_unique(params.freqs); }if (ssid & & max_ssids == 1) { /* * If the driver is limited to 1 SSID at a time interleave * wildcard SSID scans with specific SSID scans to avoid * waiting a long time for a wildcard scan. */ if (!wpa_s-> prev_scan_wildcard) { params.ssids[0].ssid = NULL; params.ssids[0].ssid_len = 0; wpa_s-> prev_scan_wildcard = 1; wpa_dbg(wpa_s, MSG_DEBUG, " Starting AP scan for " " wildcard SSID (Interleave with specific)" ); } else { wpa_s-> prev_scan_ssid = ssid; wpa_s-> prev_scan_wildcard = 0; wpa_dbg(wpa_s, MSG_DEBUG, " Starting AP scan for specific SSID: %s" , wpa_ssid_txt(ssid-> ssid, ssid-> ssid_len)); } } else if (ssid) { /* max_ssids > 1 */wpa_s-> prev_scan_ssid = ssid; wpa_dbg(wpa_s, MSG_DEBUG, " Include wildcard SSID in " " the scan request" ); params.num_ssids++; } else if (wpa_s-> last_scan_req == MANUAL_SCAN_REQ & & wpa_s-> manual_scan_passive & & params.num_ssids == 0) { wpa_dbg(wpa_s, MSG_DEBUG, " Use passive scan based on manual request" ); } else if (wpa_s-> conf-> passive_scan) { wpa_dbg(wpa_s, MSG_DEBUG, " Use passive scan based on configuration" ); } else { wpa_s-> prev_scan_ssid = WILDCARD_SSID_SCAN; params.num_ssids++; wpa_dbg(wpa_s, MSG_DEBUG, " Starting AP scan for wildcard " " SSID" ); }ssid_list_set: wpa_supplicant_optimize_freqs(wpa_s, & params); extra_ie = wpa_supplicant_extra_ies(wpa_s); // 手动发起的扫描 if (wpa_s-> last_scan_req == MANUAL_SCAN_REQ & & wpa_s-> manual_scan_only_new) { wpa_printf(MSG_DEBUG, " Request driver to clear scan cache due to manual only_new=1 scan" ); params.only_new_results = 1; }if (wpa_s-> last_scan_req == MANUAL_SCAN_REQ & & params.freqs == NULL & & wpa_s-> manual_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, " Limit manual scan to specified channels" ); params.freqs = wpa_s-> manual_scan_freqs; wpa_s-> manual_scan_freqs = NULL; }if (params.freqs == NULL & & wpa_s-> select_network_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, " Limit select_network scan to specified channels" ); params.freqs = wpa_s-> select_network_scan_freqs; wpa_s-> select_network_scan_freqs = NULL; } // 参数里面的频率为空 // 优化扫描,基于的频率列表 if (params.freqs == NULL & & wpa_s-> next_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, " Optimize scan based on previously " " generated frequency list" ); params.freqs = wpa_s-> next_scan_freqs; } else os_free(wpa_s-> next_scan_freqs); wpa_s-> next_scan_freqs = NULL; wpa_setband_scan_freqs(wpa_s, & params); /* See if user specified frequencies. If so, scan only those. */ if (wpa_s-> conf-> freq_list & & !params.freqs) { wpa_dbg(wpa_s, MSG_DEBUG, " Optimize scan based on conf-> freq_list" ); int_array_concat(& params.freqs, wpa_s-> conf-> freq_list); }/* Use current associated channel? */ // 是否只扫描当前信道 if (wpa_s-> conf-> scan_cur_freq & & !params.freqs) { unsigned int num = wpa_s-> num_multichan_concurrent; params.freqs = os_calloc(num + 1, sizeof(int)); if (params.freqs) { num = get_shared_radio_freqs(wpa_s, params.freqs, num); if (num > 0) { wpa_dbg(wpa_s, MSG_DEBUG, " Scan only the " " current operating channels since " " scan_cur_freq is enabled" ); } else { os_free(params.freqs); params.freqs = NULL; } } }params.filter_ssids = wpa_supplicant_build_filter_ssids( wpa_s-> conf, & params.num_filter_ssids); if (extra_ie) { params.extra_ies = wpabuf_head(extra_ie); params.extra_ies_len = wpabuf_len(extra_ie); }...if ((wpa_s-> mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) & & wpa_s-> wpa_state < = WPA_SCANNING) { params.mac_addr_rand = 1; if (wpa_s-> mac_addr_scan) { params.mac_addr = wpa_s-> mac_addr_scan; params.mac_addr_mask = wpa_s-> mac_addr_scan + ETH_ALEN; } }if (!is_zero_ether_addr(wpa_s-> next_scan_bssid)) { struct wpa_bss *bss; params.bssid = wpa_s-> next_scan_bssid; bss = wpa_bss_get_bssid_latest(wpa_s, params.bssid); if (bss & & bss-> ssid_len & & params.num_ssids == 1 & & params.ssids[0].ssid_len == 0) { params.ssids[0].ssid = bss-> ssid; params.ssids[0].ssid_len = bss-> ssid_len; wpa_dbg(wpa_s, MSG_DEBUG, " Scan a previously specified BSSID " MACSTR " and SSID %s" , MAC2STR(params.bssid), wpa_ssid_txt(bss-> ssid, bss-> ssid_len)); } else { wpa_dbg(wpa_s, MSG_DEBUG, " Scan a previously specified BSSID " MACSTR, MAC2STR(params.bssid)); } }scan_params = & params; scan: ... ret = wpa_supplicant_trigger_scan(wpa_s, scan_params); if (ret & & wpa_s-> last_scan_req == MANUAL_SCAN_REQ & & params.freqs & & !wpa_s-> manual_scan_freqs) { /* Restore manual_scan_freqs for the next attempt */ wpa_s-> manual_scan_freqs = params.freqs; params.freqs = NULL; }wpabuf_free(extra_ie); os_free(params.freqs); os_free(params.filter_ssids); if (ret) { wpa_msg(wpa_s, MSG_WARNING, " Failed to initiate AP scan" ); if (wpa_s-> scan_prev_wpa_state != wpa_s-> wpa_state) wpa_supplicant_set_state(wpa_s, wpa_s-> scan_prev_wpa_state); /* Restore scan_req since we will try to scan again */ wpa_s-> scan_req = wpa_s-> last_scan_req; wpa_supplicant_req_scan(wpa_s, 1, 0); } else { wpa_s-> scan_for_connection = 0; #ifdef CONFIG_INTERWORKING wpa_s-> interworking_fast_assoc_tried = 0; #endif /* CONFIG_INTERWORKING */ if (params.bssid) os_memset(wpa_s-> next_scan_bssid, 0, ETH_ALEN); } }wpa_supplicant_trigger_scan --> wpas_trigger_scan_cb --> wpa_drv_scan(wpa_s, params); --> wpa_s-> driver-> scan2(wpa_s-> drv_priv, params); externalwpa_supplicant_8srcdriversdriver_nl80211.c const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = " nl80211" , .desc = " Linux nl80211/cfg80211" , .get_bssid = wpa_driver_nl80211_get_bssid, .get_ssid = wpa_driver_nl80211_get_ssid, .set_key = driver_nl80211_set_key, .scan2 = driver_nl80211_scan2, ... }static int driver_nl80211_scan2(void *priv, struct wpa_driver_scan_params *params) { struct i802_bss *bss = priv; #ifdef CONFIG_DRIVER_NL80211_QCA struct wpa_driver_nl80211_data *drv = bss-> drv; /* * Do a vendor specific scan if possible. If only_new_results is * set, do a normal scan since a kernel (cfg80211) BSS cache flush * cannot be achieved through a vendor scan. The below condition may * need to be modified if new scan flags are added in the future whose * functionality can only be achieved through a normal scan. */ if (drv-> scan_vendor_cmd_avail & & !params-> only_new_results) return wpa_driver_nl80211_vendor_scan(bss, params); #endif /* CONFIG_DRIVER_NL80211_QCA */ return wpa_driver_nl80211_scan(bss, params); }externalwpa_supplicant_8hostapdsrcdriversdriver_nl80211_scan.c int wpa_driver_nl80211_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params) { struct wpa_driver_nl80211_data *drv = bss-> drv; int ret = -1, timeout; struct nl_msg *msg = NULL; wpa_dbg(drv-> ctx, MSG_DEBUG, " nl80211: scan request" ); drv-> scan_for_auth = 0; if (TEST_FAIL()) return -1; msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params); if (!msg) return -1; if (params-> p2p_probe) { struct nlattr *rates; wpa_printf(MSG_DEBUG, " nl80211: P2P probe - mask SuppRates" ); rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES); if (rates == NULL) goto fail; /* * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates * by masking out everything else apart from the OFDM rates 6, * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz * rates are left enabled. */ if (nla_put(msg, NL80211_BAND_2GHZ, 8, " x0cx12x18x24x30x48x60x6c" )) goto fail; nla_nest_end(msg, rates); if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) goto fail; }if (params-> bssid) { wpa_printf(MSG_DEBUG, " nl80211: Scan for a specific BSSID: " MACSTR, MAC2STR(params-> bssid)); if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params-> bssid)) goto fail; }ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, " nl80211: Scan trigger failed: ret=%d " " (%s)" , ret, strerror(-ret)); if (drv-> hostapd & & is_ap_interface(drv-> nlmode)) { enum nl80211_iftype old_mode = drv-> nlmode; /* * mac80211 does not allow scan requests in AP mode, so * try to do this in station mode. */ if (wpa_driver_nl80211_set_mode( bss, NL80211_IFTYPE_STATION)) goto fail; if (wpa_driver_nl80211_scan(bss, params)) { wpa_driver_nl80211_set_mode(bss, old_mode); goto fail; }/* Restore AP mode when processing scan results */ drv-> ap_scan_as_station = old_mode; ret = 0; } else goto fail; }drv-> scan_state = SCAN_REQUESTED; /* Not all drivers generate " scan completed" wireless event, so try to * read results after a timeout. */ // 扫描超时时间,超时之后读取结果。 timeout = 10; if (drv-> scan_complete_events) { /* * The driver seems to deliver events to notify when scan is * complete, so use longer timeout to avoid race conditions * with scanning and following association request. */ timeout = 30; } wpa_printf(MSG_DEBUG, " Scan requested (ret=%d) - scan timeout %d " " seconds" , ret, timeout); eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv-> ctx); eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, drv, drv-> ctx); drv-> last_scan_cmd = NL80211_CMD_TRIGGER_SCAN; fail: nlmsg_free(msg); return ret; }static struct nl_msg * nl80211_scan_common(struct i802_bss *bss, u8 cmd, struct wpa_driver_scan_params *params) { struct wpa_driver_nl80211_data *drv = bss-> drv; struct nl_msg *msg; size_t i; u32 scan_flags = 0; msg = nl80211_cmd_msg(bss, 0, cmd); if (!msg) return NULL; if (params-> num_ssids) { struct nlattr *ssids; ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS); if (ssids == NULL) goto fail; for (i = 0; i < params-> num_ssids; i++) { wpa_hexdump_ascii(MSG_MSGDUMP, " nl80211: Scan SSID" , params-> ssids[i].ssid, params-> ssids[i].ssid_len); if (nla_put(msg, i + 1, params-> ssids[i].ssid_len, params-> ssids[i].ssid)) goto fail; } nla_nest_end(msg, ssids); } else { wpa_printf(MSG_DEBUG, " nl80211: Passive scan requested" ); }if (params-> extra_ies) { wpa_hexdump(MSG_MSGDUMP, " nl80211: Scan extra IEs" , params-> extra_ies, params-> extra_ies_len); if (nla_put(msg, NL80211_ATTR_IE, params-> extra_ies_len, params-> extra_ies)) goto fail; } // 扫描频率 if (params-> freqs) { struct nlattr *freqs; freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); if (freqs == NULL) goto fail; for (i = 0; params-> freqs[i]; i++) { wpa_printf(MSG_MSGDUMP, " nl80211: Scan frequency %u " " MHz" , params-> freqs[i]); if (nla_put_u32(msg, i + 1, params-> freqs[i])) goto fail; } nla_nest_end(msg, freqs); }os_free(drv-> filter_ssids); drv-> filter_ssids = params-> filter_ssids; params-> filter_ssids = NULL; drv-> num_filter_ssids = params-> num_filter_ssids; if (params-> only_new_results) { wpa_printf(MSG_DEBUG, " nl80211: Add NL80211_SCAN_FLAG_FLUSH" ); scan_flags |= NL80211_SCAN_FLAG_FLUSH; }if (params-> low_priority & & drv-> have_low_prio_scan) { wpa_printf(MSG_DEBUG, " nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY" ); scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY; }if (params-> mac_addr_rand) { wpa_printf(MSG_DEBUG, " nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR" ); scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR; if (params-> mac_addr) { wpa_printf(MSG_DEBUG, " nl80211: MAC address: " MACSTR, MAC2STR(params-> mac_addr)); if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params-> mac_addr)) goto fail; }if (params-> mac_addr_mask) { wpa_printf(MSG_DEBUG, " nl80211: MAC address mask: " MACSTR, MAC2STR(params-> mac_addr_mask)); if (nla_put(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN, params-> mac_addr_mask)) goto fail; } }if (params-> duration) { if (!(drv-> capa.rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) || nla_put_u16(msg, NL80211_ATTR_MEASUREMENT_DURATION, params-> duration)) goto fail; if (params-> duration_mandatory & & nla_put_flag(msg, NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY)) goto fail; }if (scan_flags & & nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags)) goto fail; return msg; fail: nlmsg_free(msg); return NULL; }

【Android wifi 扫描流程 分析】2018-11-14

    推荐阅读