<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                首先來看WifiStateMachine的構造函數,其內容較多,我們分兩段來介紹。 **1、WifiStateMachine構造函數分析之一** **WifiStateMachine.java::WifiStateMachine構造函數代碼段一** ~~~ public WifiStateMachine(Context context, String wlanInterface) { super(TAG); mContext = context; mInterfaceName = wlanInterface; // 創建一個NetworkInfo,它實際上代表一個網絡設備的狀態信息(status of a network interface) mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI,0, NETWORKTYPE, ""); // 和BatteryStatsService交互,BSS注冊的服務名叫“batteryinfo” mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); // 創建和NewtorkManagmentService交互的Binder客戶端 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); mNwService = INetworkManagementService.Stub.asInterface(b); /* 判斷系統是否支持Wi-Fi Display功能。本書不討論WFD,感興趣的讀者可閱讀筆者的一篇博文 http:// blog.csdn.net/innost/article/details/8474683 " Android Wi-Fi Display(Miracast)介紹"。 */ mP2pSupported = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT); /* WifiNative:用于和wpa_supplicant交互。它和4.2.3節中控制API知識相關。 WifiMonitor:內部將創建一個線程,并借助WifiNative去接收并處理來自WPAS的信息。 WifiConfigStore:它對應一個配置文件,位置為/data/misc/wifi/ifconfig.txt。 該文件用于存儲每個無線網絡的配置項。例如代理地址、靜態IP地址等。讀者可在Settings 中選擇某個無線網絡,長按以彈出修改對話框,然后選擇“高級選項”即可設置這些信息。 */ mWifiNative = new WifiNative(mInterfaceName); mWifiConfigStore = new WifiConfigStore(context, mWifiNative); mWifiMonitor = new WifiMonitor(this, mWifiNative); // 用于保存DHCP的一些信息 mDhcpInfoInternal = new DhcpInfoInternal(); // WifiInfo用于存儲手機當前連接上的無線網絡的一些信息,包括IP地址、ssid等內容 mWifiInfo = new WifiInfo(); // SupplicantStateTracker用于跟蹤WPAS的狀態,它也是一個StateMachine mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,getHandler()); // LinkProperties用于描述網絡鏈接(network link)的一些屬性,如IP地址、DNS地址和路由設置 mLinkProperties = new LinkProperties(); // WifiApConfigStore和Soft AP模式有關,用于存儲Soft AP模式中使用到的一些配置信息 // WifiApConfigStore是一個StateMachine。配置信息存儲于/data// misc/wifi/softap.conf中 WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(context, getHandler()); wifiApConfigStore.loadApConfiguration(); // mWifiApConfigChannel的類型是AsyncChannel,它將和wifiApConfigStore中的某個Handler通信 mWifiApConfigChannel.connectSync(mContext, getHandler(),wifiApConfigStore.getMessenger()); mNetworkInfo.setIsAvailable(false); mLinkProperties.clear(); ...... // 設置掃描間隔時間:當驅動不支持Background掃描時,Framework將定時開展掃描工作 // 默認值為300秒 mDefaultFrameworkScanIntervalMs = mContext.getResources() .getInteger(R.integer.config_wifi_framework_scan_interval); /* driver stop延遲,默認是120秒。該變量和emergency calls(緊急呼叫)有關。 處于這種模式下,即使用戶選擇關閉Wi-Fi,WifiStateMachine也不會立即執行它,而是要 等待一段時間才真正去關閉Wi-Fi。 */ mDriverStopDelayMs = mContext.getResources().getInteger(R.integer.config_wifi_driver_stop_delay); // 是否支持Background掃描 mBackgroundScanSupported = mContext.getResources() .getBoolean(R.bool.config_wifi_background_scan_support); // 和P2P有關。以后再介紹 mPrimaryDeviceType = mContext.getResources().getString(R.string.config_wifi_p2p_device_type); // WIFI_SUSPEND_OPTIMIZATIONS_ENABLED變量用于控制手機睡眠期間是否保持Wi-Fi開啟 mUserWantsSuspendOpt.set(Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1); ......// 處理ACTION_START_SCAN廣播事件 ......// 處理ACTION_SCREEN_ON/OFF廣播事件 ......// 處理ACTION_DELAYED_DRIVER_STOP廣播事件 ......// 監視ContentProvider中WIFI_SUSPEND_OPTIMIZATIONS_ENABLED設置的變化 // mScanResultCache用于保存掃描結果 mScanResultCache = new LruCache<String, ScanResult>(SCAN_RESULT_CACHE_SIZE); ......// 申請WakeLock } ~~~ 重點介紹其中的三個對象,分別是WifiNative、WifiMonitor以及SupplicantStateTracker。 **①、WifiNative** 根據上文描述,WifiNative用于和WPAS通信,其內部定義了較多的native方法(對應的JNI模塊是android_net_wifi_Wifi)。本節介紹其中最重要的兩個方法。 第一個方法是startSupplicant,用于啟動WPAS。startSupplicant是一個native函數,其JNI[^①]函數是**android_net_wifi_startSupplicant**,代碼如下所示。 **android_net_wifi_Wifi.c::android_net_wifi_startSupplicant** ~~~ static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject,jboolean p2pSupported) { return (jboolean)(::wifi_start_supplicant(p2pSupported) == 0); } ~~~ wifi_start_supplicant代碼如下所示。 **wifi.c::wifi_start_supplicant** ~~~ int wifi_start_supplicant(int p2p_supported) { char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; int count = 200; // 該宏在build/core/combo/include/arch/linux-arm/AndroidConfig.h中被定義為1 #ifdef HAVE_LIBC_SYSTEM_PROPERTIES const prop_info *pi; unsigned serial = 0, i; #endif // 和P2P有關 if (p2p_supported) {// P2P_SUPPLICANT_NAME值為“p2p_supplicant” strcpy(supplicant_name, P2P_SUPPLICANT_NAME); // P2P _PROP_NAME值為“init.svc.p2p_supplicant” strcpy(supplicant_prop_name, P2P_PROP_NAME); /* P2P_CONFIG_FILE的值為“/data/misc/wifi/p2p_supplicant.conf”。下面這個函數將把 /system/etc/wifi/wpa_supplicant.conf的內容復制到P2P_CONFIG_FILE中。 */ if (ensure_config_file_exists(P2P_CONFIG_FILE) < 0) return -1; } else { strcpy(supplicant_name, SUPPLICANT_NAME);// SUPPLICANT_NAME值為“wpa_supplicant” // SUPP_PROP_NAME值為“init.svc.wpa_supplicant” strcpy(supplicant_prop_name, SUPP_PROP_NAME); } // 如果WPAS已經啟動,則直接返回 if (property_get(supplicant_name, supp_status, NULL) && strcmp(supp_status, "running") == 0) return 0; // SUPP_CONFIG_FILE的值為“/data/misc/wifi/wpa_supplicant.conf” if (ensure_config_file_exists(SUPP_CONFIG_FILE) < 0) return -1; // entropy文件,用于增加隨機數生成的隨機性 if (ensure_entropy_file_exists() < 0) ALOGE("Wi-Fi entropy file was not created"); // 關閉之前創建的wpa_ctrl對象 wifi_wpa_ctrl_cleanup(); for (i=0; i&lt;MAX_CONNS; i++) exit_sockets[i][0] = exit_sockets[i][1] = -1; #ifdef HAVE_LIBC_SYSTEM_PROPERTIES // supplicant_prop_name值為“init.svc.wpa_supplicant” pi = __system_property_find(supplicant_prop_name); ...... #endif property_get("wifi.interface", primary_iface, WIFI_TEST_INTERFACE); /* 通過設置“ctrl.start”屬性來啟動wpa_supplicant服務。該屬性將觸發init fork一個子 進程用于運行wpa_supplicant。同時,init還會添加一個新的屬性 “init.svc.wpa_supplicant”用于跟蹤wpa_supplicant的狀態。 */ property_set("ctl.start", supplicant_name); sched_yield(); // 下面這個循環用于查詢“init.svc.wpa_supplicant”的屬性值 // 如果其值變成“running”,表示wpa_supplicant成功運行 while (count-- > 0) {// count初值為200。while循環最多等待20秒 #ifdef HAVE_LIBC_SYSTEM_PROPERTIES if (pi == NULL) { pi = __system_property_find(supplicant_prop_name); } if (pi != NULL) { __system_property_read(pi, NULL, supp_status); if (strcmp(supp_status, "running") == 0) return 0; else if (pi->serial != serial &&// 如果WPAS已經停止,則直接返回-1 strcmp(supp_status, "stopped") == 0) return -1; } #else ...... #endif usleep(100000);// 等待wpa_supplicant的狀態 } return -1; } ~~~ 圖5-3顯示了wpa_supplicant運行過程中及退出后"init.svc.wpa_supplicant"屬性值的變化。 :-: ![](https://box.kancloud.cn/bd1c4c272f9627aad27f765d2b7f5605_653x198.jpg) 圖5-3 init.svc.wpa_supplicant屬性 >[info] 提示 對Android屬性機制和init工作原理感興趣的讀者不妨閱讀《深入理解Android:卷Ⅰ》第3章。 第二個要介紹的函數是connectToSupplicant,它將通過WPAS控制API和WPAS建立交互關系。 **WifiNative.java::connectToSupplicant** ~~~ public boolean connectToSupplicant() { // mInterface的值為”wlan0”,由屬性“wifi.interface”決定 return connectToSupplicant(mInterface);// 調用native函數 } private native boolean connectToSupplicant(String iface); ~~~ 與connectToSupplicant對應的JNI函數是android_net_wifi_connectToSupplicant,其代碼如下所示。 **android_net_wifi_Wifi.cpp::android_net_wifi_connectToSupplicant** ~~~ static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject, jstring jIface) { ScopedUtfChars ifname(env, jIface); return (jboolean)(::wifi_connect_to_supplicant(ifname.c_str()) == 0); } ~~~ wifi_connect_to_supplicant的代碼如下所示。 **wifi.c::wifi_connect_to_supplicant** ~~~ int wifi_connect_to_supplicant(const char *ifname) { char path[256]; /* Android 4.2支持STA和P2P設備并發(concurrent)工作,STA用PRIMARY(值為0)來標示, 而P2P設備用SECONDARY(值為1)代表。is_primary_interface用于判斷ifname是否代表STA。 */ if (is_primary_interface(ifname)) { // IFACE_DIR的值為“/data/system/wpa_supplicant”。筆者測試的幾個手機中都沒有該文件夾 if (access(IFACE_DIR, F_OK) == 0) { snprintf(path, sizeof(path), "%s/%s", IFACE_DIR, primary_iface); } else { strlcpy(path, primary_iface, sizeof(path)); } return wifi_connect_on_socket_path(PRIMARY, path);// PRIMARY值為0 } else { sprintf(path, "%s/%s", CONTROL_IFACE_PATH, ifname); return wifi_connect_on_socket_path(SECONDARY, path);// SECONDARY值為1 } } ~~~ 來看wifi_connect_on_socket_path,其代碼如下所示。 **wifi.c::wifi_connect_on_socket_path** ~~~ int wifi_connect_on_socket_path(int index, const char *path) { char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; // 判斷wpa_supplicant進程是否已經啟動 if (!property_get(supplicant_prop_name, supp_status, NULL) || strcmp(supp_status, "running") != 0) return -1; // 創建第一個wpa_ctrl對象,用于發送命令 ctrl_conn[index] = wpa_ctrl_open(path); ...... // 創建第二個wpa_ctrl對象,用于接收unsolicited event monitor_conn[index] = wpa_ctrl_open(path); ...... // 必須調用wpa_ctrl_attach函數以啟用unsolicited event接收功能 if (wpa_ctrl_attach(monitor_conn[index]) != 0) {......} // 創建一個socketpair,它用于觸發WifiNative關閉和WPAS的連接 if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets[index]) == -1) {......} return 0; } ~~~ 由于Android 4.2支持兩個并發設備,所以每個并發設備各有兩個wpa_ctrl對象。 * ctrl_conn[PRIMARY]、monitor_conn[PRIMARY]:用于STA設備。ctrl_conn用于向WPAS發送命令并接收對應命令的回復,而monitor_conn用于接收來自WPAS的unsolicited event。 * ctrl_conn[SECONDARY]、monitor_conn[SECONDARY]:這兩個wpa_ctrl對象用于P2P設備。 另外,exit_sockets保存了socketpair創建的socket句柄,這些句柄用于WifiService通知WifiNative去關閉它和WPAS的連接。 >[info] 提示 wifi.c中,wifi_send_command會使用ctrl_conn中的wpa_ctrl對象向WPAS發送命令并接收回復,而wifi_recv函數將使用monitor_conn中的wpa_ctrl對象接收來自WPAS的消息。這兩個函數比較簡單,請讀者可自行閱讀它。 下面來看WifiMonitor,它將使用monitor_conn中的wpa_ctrl對象。 **②、WifiMonitor** WifiMonitor最重要的內容是其內部的WifiMonitor線程,該線程專門用于接收來自WPAS的消息。代碼如下所示。 **WifiMonitor.java::MonitorThread** ~~~ class MonitorThread extends Thread { public MonitorThread() { super("WifiMonitor"); } public void run() { if (connectToSupplicant()) {// 連接WPAS, mStateMachine指向WifiStateMachine // 連接成功后,將向WifiStateMachine發送SUP_CONNECTION_EVENT消息 mStateMachine.sendMessage(SUP_CONNECTION_EVENT); } else { mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT); return; } for (;;) { // waitForEvent內部會調用wifi.c中的wifi_wait_on_socket函數 String eventStr = mWifiNative.waitForEvent(); // 解析WPAS的消息格式。EVENT_PREFIX_STR的值為“CTRL-EVENT-” if (!eventStr.startsWith(EVENT_PREFIX_STR)) { ......// 非“CTRL-EVENT-”消息 continue; } // 處理“CTRL-EVENT-”消息 String eventName = eventStr.substring(EVENT_PREFIX_LEN_STR); int nameEnd = eventName.indexOf(' '); if (nameEnd != -1) eventName = eventName.substring(0, nameEnd); ...... int event; if (eventName.equals(CONNECTED_STR))// 對應為“CONNECTED”消息 event = CONNECTED; ...... else if (eventName.equals(STATE_CHANGE_STR))// 對應為“STATE-CHANGED” event = STATE_CHANGE; else if (eventName.equals(SCAN_RESULTS_STR))// 對應為“SCAN-RESULTS” event = SCAN_RESULTS; ...... else if (eventName.equals(DRIVER_STATE_STR))// 對應為“DRIVER-STATE” event = DRIVER_STATE; else if (eventName.equals(EAP_FAILURE_STR))// 對應為“EAP-FAILURE” event = EAP_FAILURE; else event = UNKNOWN; /* 提取消息中的其他信息,以CONNECTED消息為例,其消息全內容為: CTRL-EVENT-CONNECTED - Connection to xx:xx:xx:xx:xx:xx completed 其中,xx:xx:xx:xx:xx:xx代表目標AP的BSSID。 */ String eventData = eventStr; if (event == DRIVER_STATE || event == LINK_SPEED) eventData = eventData.split(" ")[1]; else if (event == STATE_CHANGE || event == EAP_FAILURE) { ...... } ...... if (event == STATE_CHANGE) {// WPAS狀態發生變化 handleSupplicantStateChange(eventData); } else if (event == DRIVER_STATE) { handleDriverEvent(eventData); }...... else {// 其他事件處理 handleEvent(event, eventData); } mRecvErrors = 0; } } ...... } ~~~ 上述代碼中: * handleSupplicantStateChange用于處理WPAS的狀態變化(見下文解釋),它將先把這些變化信息交給WifiStateMachine去處理。而WifiStateMachine將根據處理情況再決定是否需要由下一節介紹的SupplicantStateTracker來處理。handleSupplicant StateChange代碼比較簡單,讀者可自行閱讀它。 * handleDriverEvent用于處理來Driver的信息[^②]。 * handleEvent用于處理其他消息事件。詳情見下文。 >[info] 注意 WPAS的狀態指的是wpa_sm狀態機中的狀態,包括WPA_DISCONNECTED、WPA_INTERFACE_DISABLED、WPA_INACTIVE、WPA_SCANNING、WPA_AUTHENTICATING、WPA_ASSOCIATING、WPA_ASSOCIATED、WPA_4WAY_HANDSHAKE、WPA_GROUP_HANDSHAKE、WPA_COMPLETED共10個狀態。WifiService定義了SupplicantState類來描述WPAS的狀態,包括DISCONNECTED、INTERFACE_DISABLED、INACTIVE、SCANNING、AUTHENTICATING、ASSOCIATING、ASSOCIATED、FOUR_WAY_HANDSHAKE、GROUP_HANDSHAKE、COMPLETED、DORMANT、UNINITIALIZED、INVALID共13個狀態。其中最后三個狀態是WifiService定義的,但筆者在代碼中沒有找到使用它們的地方。 下面簡單介紹handleEvent,其代碼如下所示。 **WifiMonitor.java::handleEvent** ~~~ void handleEvent(int event, String remainder) { switch (event) { case DISCONNECTED: handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder); break; case CONNECTED:// 該事件表示WPAS成功加入一個無線網絡 handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder); break; case SCAN_RESULTS:// 該事件表示WPAS已經完成掃描,客戶端可以來查詢掃描結果 mStateMachine.sendMessage(SCAN_RESULTS_EVENT);// 處理掃描結果消息 break; case UNKNOWN: break; } } ~~~ 先介紹SupplicantStateTracker。后文再分析CONNECTED和SCAN_RESULTS消息的處理流程。 **③、SupplicantStateTracker** SupplicantStateTracker用于跟蹤和處理WPAS的狀態變化。根據前面對WPAS中的狀態以及WifiService中的狀態介紹可知。在WifiService中,WPAS的狀態由SupplicantState來表示,而和它相關的狀態管理模塊就是此處的SupplicantStateTracker。SupplicantStateTracker也從StateMachine派生,并且它還定義了8個狀態對象。相關代碼如下所示。 **SupplicantStateTracker.java::SupplicantStateTracker** ~~~ public SupplicantStateTracker(Context c, WifiStateMachine wsm,WifiConfigStore wcs, Handler t) { super(TAG, t.getLooper()); mContext = c; mWifiStateMachine = wsm; mWifiConfigStore = wcs; addState(mDefaultState); addState(mUninitializedState, mDefaultState); addState(mInactiveState, mDefaultState); addState(mDisconnectState, mDefaultState); addState(mScanState, mDefaultState); addState(mHandshakeState, mDefaultState); addState(mCompletedState, mDefaultState); addState(mDormantState, mDefaultState); setInitialState(mUninitializedState);// 初始狀態為mUninitializedState start();// 啟動狀態機 } ~~~ * SupplicantState中的AUTHENTICATING、ASSOCIATING、ASSOCIATED、FOUR_WAY_HANDSHAKE和GROUP_HANDSHAKE均對應于此處的mHandshakeState。 * SupplicantState中的UNINITIALIZED和INVALID對應于此處的mUnitializedState。SupplicantStateTracker比較簡單,而且它也不影響本章的分析流程。讀者可在閱讀完本章的基礎上,自行對其開展研究。 下面來看WifiStateMachine構造函數的最后一部分。 **2、WifiStateMachine構造函數分析之二** **WifiStateMachine.java::WifiStateMachine構造函數代碼段二** ~~~ ......// WifiStateMachine中的狀態。說實話,筆者還沒見過如此復雜的狀態機 addState(mDefaultState); addState(mInitialState, mDefaultState); addState(mDriverUnloadingState, mDefaultState); addState(mDriverUnloadedState, mDefaultState); addState(mDriverFailedState, mDriverUnloadedState); ......// WifiStateMachine一共定義了30個狀態 addState(mSoftApStoppingState, mDefaultState); setInitialState(mInitialState);// 設置初始狀態為mInitialState ......// 和StateMachine日志記錄相關設置 start();// 啟動狀態機 } ~~~ WifiStateMachine共定義30個狀態,其種類和層級關系如圖5-4所示。 :-: ![](https://box.kancloud.cn/f6aecfa81a54392f17c67917eba6ce38_1186x524.jpg) 圖5-4 WifiStateMachine中的狀態及層級關系 圖5-4中,箭頭所指的狀態為父狀態。本節先介紹和初始狀態的相關代碼,其他狀態的功能等碰到它們時再來分析。 >[info] 提示 如果算上SupplicantStateTracker中的8個狀態以及后續章節將要介紹的P2pStateMachine中的15個狀態,Java層中Wi-Fi相關的狀態機竟然多達63個狀態(還沒有計算Wi-Fi模塊其他代碼中定義的好些個狀態機所包含的狀態)。筆者很難理解為什么WifiService相關模塊會定義如此多的狀態。這些狀態使得WifiService的分析難度陡增。而且,在整個Wi-Fi模塊中,wpa_supplicant作為核心已經完成了絕大部分的工作,為什么WifiService還會如此復雜呢?歡迎讀者對此問題和筆者展開討論。 WifiStateMachine的初始狀態是mInitialState,其類型是InitialState。根據前文對HSM的介紹,其enter函數將被調用(由于InitialState的父狀態DefaultState并未實現enter函數,故此處略去)。 **WifiStateMachine.java::InitialState:enter** ~~~ class InitialState extends State { public void enter() { // 判斷Wlan Driver是否已經加載,其內部實現通過"wlan.driver.status"屬性的值來判斷 if (mWifiNative.isDriverLoaded()) transitionTo(mDriverLoadedState); else transitionTo(mDriverUnloadedState);// 假設此時驅動還沒有加載,故我們將轉入此狀態 // 獲取和WifiP2pService交互的對象 mWifiP2pManager = (WifiP2pManager) mContext.getSystemService( Context.WIFI_P2P_SERVICE); // mWifiP2pChannel用于和WifiP2pService中的某個Handler交互 mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger()); try { mNwService.disableIpv6(mInterfaceName); // 禁止Ipv6,NWService將和Netd交互 } ...... } } ~~~ 結合上述代碼,當WifiStateMachine開始運行后,其最終將進入DriverUnloadedState。由于DriverUnloadedState的enter函數沒有做什么有意義的工作,所以此處不再討論它。 至此,WifiService第一條分析路線就算結束。雖然WifiService創建工作涉及的流程并不長,但相信讀者也會感覺WifiService的代碼難度其實并不算小。從下一節開始,讀者還將進一步體會到這一點。 [^①]:可參考《深入理解Android:卷Ⅰ》第2章JNI相關的重要知識。 [^②]:筆者搜索了相關代碼,在wlan芯片廠商提供的一些供WPAS使用的動態庫中會發送DRIVER-EVENT。相關代碼可參考 hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib/driver_cmd_nl80211.c
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看