我是靠谱客的博主 鳗鱼画笔,最近开发中收集的这篇文章主要介绍WIFI操作流程源码分析—扫描,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

WIFI操作流程源码分析—扫描

在处理该消息的代码中做真正的使能工作:首先装载 WIFI 内核模块(该模块的位置硬编码为

"/system/lib/modules/wlan.ko" ), 然后启动 wpa_supplicant ( 配置文件硬编码为

"/data/misc/wifi/wpa_supplicant.conf") 再通过 WifiStateTracker 来启动 WifiMonitor 中的监视线程。

当使能成功后,会广播发送 WIFI_STATE_CHANGED_ACTION通知外界 WIFI已经成功使能了。

应用一侧扫描

WifiSettings创建的时候就会向 Android 注册接收WIFI_STATE_CHANGED_ACTION,因此它会收到该 Intent,从而开始扫描。

mReceiver = new BroadcastReceiver() {

            @Override

            public void onReceive(Context context, Intent intent) {

                handleEvent(context, intent);

            }

        };

private void handleEvent(Context context, Intent intent) {

        String action = intent.getAction();

        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {

            updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,

                    WifiManager.WIFI_STATE_UNKNOWN));

        } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||

                WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||

                WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {

                ……

        }

}

private void updateWifiState(int state) {

        Activity activity = getActivity();

        if (activity != null) {

            activity.invalidateOptionsMenu();

        }

        switch (state) {

            case WifiManager.WIFI_STATE_ENABLED:

                mScanner.resume();

                /// M: update priority

                // if wifi has connectd, not need to updatePriority

                WifiInfo mCurrentConnecdInfo = mWifiManager.getConnectionInfo();

                if (mCurrentConnecdInfo != null && mCurrentConnecdInfo.getSSID() != null

                                   && mCurrentConnecdInfo.getSSID().length() > 0) {                   

                    mExt.updatePriority();                

                }

                return; // not break, to avoid the call to pause() below           

        }

        mLastInfo = null;

        mLastState = null;

        mScanner.pause();

    }

Scanner是一个内部类,实现如下:

private class Scanner extends Handler {

        private int mRetry = 0;

        void resume() {

            if (!hasMessages(0)) {

                sendEmptyMessage(0);//首先发送消息

            }

        }

        @Override

        public void handleMessage(Message message) {

            if (mWifiManager.startScanActive()) {

                mRetry = 0;

            } else if (++mRetry >= 3) {

                mRetry = 0;

                Activity activity = getActivity();

                if (activity != null) {

                    Toast.makeText(activity, R.string.wifi_fail_to_scan,

                        Toast.LENGTH_LONG).show();

                }

                return;

            }

            sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);

        }

    }

WifiManager中函数startScanActive进行处理

public boolean startScanActive() {

        try {

            mService.startScan(true);

            return true;

        } catch (RemoteException e) {

            return false;

        }

    }

服务一侧扫描

通过 AIDL,实际调用的是 WifiService startScan函数,

public void startScan(boolean forceActive) {       

        enforceChangePermission();//首先判断是否有权限

        mWifiStateMachine.startScan(forceActive);

        noteScanStart();

    }

WifiStateMachine中函数startScan会发送消息CMD_START_SCAN

public void startScan(boolean forceActive) {

        sendMessage(obtainMessage(CMD_START_SCAN, forceActive ?

                SCAN_ACTIVE : SCAN_PASSIVE, 0));

    }

WifiStateMachine内部类DriverStartedState会处理该消息

class DriverStartedState extends State {

   @Override

        public boolean processMessage(Message message) {

            case CMD_START_SCAN:

                    boolean forceActive = (message.arg1 == SCAN_ACTIVE);

                    if (forceActive && !mSetScanActive) {

                        mWifiNative.setScanMode(forceActive);

                    }

                    mWifiNative.scan();

                    if (forceActive && !mSetScanActive) {

                        mWifiNative.setScanMode(mSetScanActive);

                    }

                    mScanResultIsPending = true;

                    break;

 

            }

}

WifiNative.java中函数如下:

public boolean scan() {

       return doBooleanCommand("SCAN");

    }

 

private boolean doBooleanCommand(String command) {

        if (DBG) Log.d(mTAG, "doBoolean: " + command);

        return doBooleanCommand(mInterface, command);

    }

底层扫描

通过JNI进入函数android_net_wifi_doBooleanCommand,在android_net_wifi_Wifi.cpp

static jboolean android_net_wifi_doBooleanCommand(JNIEnv* env, jobject, jstring jIface,

        jstring jCommand)

{

    ScopedUtfChars ifname(env, jIface);

    ScopedUtfChars command(env, jCommand);

    if (command.c_str() == NULL) {

        return JNI_FALSE;

    }

    if (DBG) ALOGD("doBoolean: %s", command.c_str());

    return doBooleanCommand(ifname.c_str(), "OK", "%s", command.c_str());

}

 

static jboolean doBooleanCommand(const char *ifname, const char* expect, const char* fmt, ...)

{

    char buf[BUF_SIZE] = {0};

    va_list args;

    va_start(args, fmt);

    int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);

    va_end(args);

    if (byteCount < 0 || byteCount >= BUF_SIZE) {

        return JNI_FALSE;

    }

    char reply[BUF_SIZE] = {0};

    if (doCommand(ifname, buf, reply, sizeof(reply)) != 0) {

        return JNI_FALSE;

    }

    return (strcmp(reply, expect) == 0);

}

 

static int doCommand(const char *cmd, char *replybuf, int replybuflen)

         {

             size_t reply_len = replybuflen - 1;

          

             if (::wifi_command(cmd, replybuf, &reply_len) != 0)

                 return -1;

             else {

                 // Strip off trailing newline

                 if (reply_len > 0 && replybuf[reply_len-1] == 'n')

                     replybuf[reply_len-1] = '';

                 else

                     replybuf[reply_len] = '';

                 return 0;

             }

         }

 

wifi_commandwifi.c中实现

         int wifi_command(const char *command, char *reply, size_t *reply_len)

         {

             return wifi_send_command(ctrl_conn, command, reply, reply_len);

         }

接下来看看wifi_send_command函数,在wifi.c里面

         int wifi_send_command(struct wpa_ctrl *ctrl, const char *cmd, char *reply, size_t *reply_len)

         {

             int ret;

          

             if (ctrl_conn == NULL) {

                 LOGV("Not connected to wpa_supplicant - "%s" command dropped.n", cmd);

                 return -1;

             }

             ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), reply, reply_len, NULL);

             if (ret == -2) {

                 LOGD("'%s' command timed out.n", cmd);

                 return -2;

             } else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) {

                 return -1;

             }

             if (strncmp(cmd, "PING", 4) == 0) {

                 reply[*reply_len] = '';

             }

             return 0;

         }

这里面有个struct,很重要

         struct wpa_ctrl {

         #ifdef CONFIG_CTRL_IFACE_UDP

             int s;

             struct sockaddr_in local;

             struct sockaddr_in dest;

             char *cookie;

         #endif /* CONFIG_CTRL_IFACE_UDP */

         #ifdef CONFIG_CTRL_IFACE_UNIX

             int s;

             struct sockaddr_un local;

             struct sockaddr_un dest;

         #endif /* CONFIG_CTRL_IFACE_UNIX */

         #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE

             HANDLE pipe;

         #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */

         };

这个类中声明了两个Socket套接口,一个是本地一个是要连接的套接口,wpa_ctrlwpa_supplicant的通信就需要socket来帮忙了,而wpa_supplicant就是通过调用 wpa_ctrl.h中定义的函数和wpa_supplicant进行通讯的,wpa_ctrl类(其实是其中的两个socket)就是他们之间的桥梁。

wpa_ctrl_request,这个函数存放在wpa_ctrl.c(external/wpa_supplicant)

         int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,

                      char *reply, size_t *reply_len,

                      void (*msg_cb)(char *msg, size_t len))

         {           

            ……

                 res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);

                 if (FD_ISSET(ctrl->s, &rfds)) {

                     res = recv(ctrl->s, reply, *reply_len, 0);

                     if (res < 0)

                         return res;

                     if (res > 0 && reply[0] == '<') {

                         /* This is an unsolicited message from

                          * wpa_supplicant, not the reply to the

                          * request. Use msg_cb to report this to the

                          * caller. */

                         if (msg_cb) {

                             /* Make sure the message is nul

                              * terminated. */

                             if ((size_t) res == *reply_len)

                                 res = (*reply_len) - 1;

                             reply[res] = '';

                             msg_cb(reply, res);

                         }

                         continue;

                     }

                     *reply_len = res;

                     break;

                 } else {

                     return -2;

                 }       

         }

最终会将扫描到的数据保存在变量reply中。

应用一侧显示

下面需要更新UI,并将所扫描到的数据显示在界面上去。在WifiSettings.javaonResume最后调用updateAccessPoints更新AP扫描

public void onResume() {

        super.onResume();

        if (mWifiEnabler != null) {

            mWifiEnabler.resume();

        }

 

        getActivity().registerReceiver(mReceiver, mFilter);

        if (mKeyStoreNetworkId != INVALID_NETWORK_ID &&

                KeyStore.getInstance().state() == KeyStore.State.UNLOCKED) {

            mWifiManager.connect(mKeyStoreNetworkId, mConnectListener);

        }

        mKeyStoreNetworkId = INVALID_NETWORK_ID;

 

        updateAccessPoints();

}

updateAccessPoints中首先判断当前wifi的状态为WIFI_STATE_ENABLED

private void updateAccessPoints() {

        // Safeguard from some delayed event handling

        if (getActivity() == null) return;

 

        final int wifiState = mWifiManager.getWifiState();

 

        switch (wifiState) {

            case WifiManager.WIFI_STATE_ENABLED:

                // AccessPoints are automatically sorted with TreeSet.

                final Collection<AccessPoint> accessPoints = constructAccessPoints();

 

                if (accessPoints.size() == 0) {

                    addMessagePreference(R.string.wifi_empty_list_wifi_on);

                }

                /// M: add ap to screen @{

                if (!mExt.isCatogoryExist()) {

                    for (AccessPoint accessPoint : accessPoints) {

                        getPreferenceScreen().addPreference(accessPoint);

                    }

                }

                /// @}

                break;

        }

}

constructAccessPoints中通过WifiManager获取数据

private List<AccessPoint> constructAccessPoints() {

        ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>();

       

        final List<ScanResult> results = mWifiManager.getScanResults();      

 

    }

服务一侧显示

通过 AIDL,实际调用的是 WifiService getScanResults函数,

public List<ScanResult> getScanResults() {

        try {

            return mService.getScanResults();

        } catch (RemoteException e) {

            return null;

        }

    }

getScanResults中通过判断扫描ap用户id与当前请求用户id是否一致,这里假设是保持一致的。

public List<ScanResult> getScanResults() {

        enforceAccessPermission();

        int userId = UserHandle.getCallingUserId();

        long ident = Binder.clearCallingIdentity();

        try {

            int currentUser = ActivityManager.getCurrentUser();

            if (userId != currentUser) {

                return new ArrayList<ScanResult>();

            } else {

                return mWifiStateMachine.syncGetScanResultsList();

            }

        } finally {

            Binder.restoreCallingIdentity(ident);

        }

    }

最后在WifiStateMachine通过获取mScanResults得到扫描ap所获得的数据

public List<ScanResult> syncGetScanResultsList() {

        synchronized (mScanResultCache) {

            List<ScanResult> scanList = new ArrayList<ScanResult>();

            for (ScanResult result : mScanResults) {

                scanList.add(new ScanResult(result));

            }

            return scanList;

        }

    }

下面我们分析一下Android源码中如何将扫描AP后的值赋给mScanResults

而在WifiStateMachine一侧,当WifiMoniter.java监测并获取底层发送消息SCAN_RESULTS后,会发送广播SCAN_RESULTS_EVENT

内部类SupplicantStartedState会处理该广播:

class SupplicantStartedState extends State {

      //……

      @Override

        public boolean processMessage(Message message) {

 

              case WifiMonitor.SCAN_RESULTS_EVENT:

                    setScanResults(mWifiNative.scanResults());

                    sendScanResultsAvailableBroadcast();

                    mScanResultIsPending = false;

 

                    if (mWifiFwkExt.hasCustomizedAutoConnect()) {

                        Xlog.d(TAG, "SCAN_RESULTS_EVENT, mScanForWeakSignal:" + mScanForWeakSignal);

                        if (mScanForWeakSignal) {

                            showReselectionDialog();

                        }

                        mDisconnectNetworkId = INVALID_NETWORK_ID;

                    }

 

                    break;

                    //……

           }

}

我们先分析一下mWifiNative.scanResults()如何获取扫描AP后的结果,再分析函数setScanResults(String scanResults)

public String scanResults() {

        return doStringCommand("BSS RANGE=ALL MASK=0x1986");

    }

private String doStringCommand(String command) {

        if (DBG) Log.d(mTAG, "doString: " + command);

        return doStringCommand(mInterface, command);

    }

底层处理

通过JNI进入android_net_wifi_Wifi.cppandroid_net_wifi_doStringCommand

static jstring android_net_wifi_doStringCommand(JNIEnv* env, jobject, jstring jIface,

        jstring jCommand)

{

    ScopedUtfChars ifname(env, jIface);

 

    ScopedUtfChars command(env, jCommand);

    if (command.c_str() == NULL) {

        return NULL;

    }

    if (DBG) ALOGD("doString: %s", command.c_str());

    return doStringCommand(env, ifname.c_str(), "%s", command.c_str());

}

 

static jstring doStringCommand(JNIEnv* env, const char *ifname, const char* fmt, ...) {   

 

    if (doCommand(ifname, buf, reply, sizeof(reply)) != 0) {

        return NULL;

    }       

}

 

static int doCommand(const char *ifname, const char *cmd, char *replybuf, int replybuflen)

{

    size_t reply_len = replybuflen - 1;

 

    if (::wifi_command(ifname, cmd, replybuf, &reply_len) != 0)

        return -1;

    else {

        // Strip off trailing newline

        if (reply_len > 0 && replybuf[reply_len-1] == 'n')

            replybuf[reply_len-1] = '';

        else

            replybuf[reply_len] = '';

        return 0;

    }

}

先前我们已经简单分析了扫描AP后的数据保存在replybuf

 

回头我们看setScanResults函数

private void setScanResults(String scanResults) {

 

        synchronized (mScanResultCache) {

            mScanResults = new ArrayList<ScanResult>();

            String[] lines = scanResults.split("n");

 

            for (String line : lines) {

                if (line.startsWith(BSSID_STR)) {

                    bssid = line.substring(BSSID_STR.length());

                } else if (line.startsWith(FREQ_STR)) {

                    try {

                        freq = Integer.parseInt(line.substring(FREQ_STR.length()));

                    } catch (NumberFormatException e) {

                        freq = 0;

                    }

                } else if (line.startsWith(LEVEL_STR)) {

                    try {

                        level = Integer.parseInt(line.substring(LEVEL_STR.length()));

                        /* some implementations avoid negative values by adding 256

                         * so we need to adjust for that here.

                         */

                        if (level > 0) level -= 256;

                    } catch (NumberFormatException e) {

                        level = 0;

                    }

                } else if (line.startsWith(TSF_STR)) {

                    try {

                        tsf = Long.parseLong(line.substring(TSF_STR.length()));

                    } catch (NumberFormatException e) {

                        tsf = 0;

                    }

                } else if (line.startsWith(FLAGS_STR)) {

                    flags = line.substring(FLAGS_STR.length());

                } else if (line.startsWith(SSID_STR)) {

                    wifiSsid = WifiSsid.createFromAsciiEncoded(

                            line.substring(SSID_STR.length()));

                } else if (line.startsWith(DELIMITER_STR)) {

                    if (bssid != null) {

                        String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;

                        String key = bssid + ssid;

                        ScanResult scanResult = mScanResultCache.get(key);

                        if (scanResult != null) {

                            scanResult.level = level;

                            scanResult.wifiSsid = wifiSsid;

                            // Keep existing API

                            scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :

                                    WifiSsid.NONE;

                            scanResult.capabilities = flags;

                            scanResult.frequency = freq;

                            scanResult.timestamp = tsf;

                        } else {

                            scanResult =

                                new ScanResult(

                                        wifiSsid, bssid, flags, level, freq, tsf);

                            mScanResultCache.put(key, scanResult);

                        }

  

                        mScanResults.add(scanResult);

                    }

 

                }

            }

        }

}

到此扫描AP后的结果就赋值给mScanResults

最后

以上就是鳗鱼画笔为你收集整理的WIFI操作流程源码分析—扫描的全部内容,希望文章能够帮你解决WIFI操作流程源码分析—扫描所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部