前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android4.4.2源码分析之WiFi模块(二)

Android4.4.2源码分析之WiFi模块(二)

作者头像
fanfan
发布2022-05-07 14:47:27
1.1K0
发布2022-05-07 14:47:27
举报
文章被收录于专栏:编程思想之路编程思想之路

接着上一篇继续对WiFi源码的分析

Android4.4.2源码分析之WiFi模块(一)

onResume方法中

6>,首先是调用WiFiEnabler的resume方法对switch进行管理

接下来注册广播

代码语言:javascript
复制
getActivity().registerReceiver(mReceiver, mFilter);

广播监听的action如下

代码语言:javascript
复制
       //wifi状态改变的action
        mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
       
       //WiFi扫描到附近可用WiFi时的广播
        mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
      
       //
        mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
        mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
        mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
        mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
        mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);

查看WiFiManager发现各action定义如下,以及在监听到对应广播后各处理如下

i>

代码语言:javascript
复制
 /**
     * Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
     * enabling, disabling, or unknown. One extra provides this state as an int.
     * Another extra provides the previous state, if available.
     *
     * @see #EXTRA_WIFI_STATE
     * @see #EXTRA_PREVIOUS_WIFI_STATE
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String WIFI_STATE_CHANGED_ACTION =
        "android.net.wifi.WIFI_STATE_CHANGED";

WIFI_STATE_CHANGED_ACTION:当WiFi被打开、关闭、正在打开、正在关闭或者位置状态即wifi状态发生改变时系统会自动发送该广播,该广播会附带有两个值,一个是int型表示改变后的state,可通过字段EXTRA_WIFI_STATE获取,还有一个是int型的改变前的state(如果有的话)可通过字段EXTRA_PREVIOUS_WIFI_STATE获取

当监听到该广播后会进行如下处理:更新WiFi状态(在WiFiEnbabler中也监听了该广播,用于当WiFi状态改变时对switch进行更新)

代码语言:javascript
复制
 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
            updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                    WifiManager.WIFI_STATE_UNKNOWN));

updateWifiState方法如下

代码语言:javascript
复制
private void updateWifiState(int state) {
        Activity activity = getActivity();
        if (activity != null) {
        //重新加载菜单  ,该方法会调用activity中的onCreateOptionsMenu加载actionbar
         activity.invalidateOptionsMenu();
        }

        switch (state) {
            case WifiManager.WIFI_STATE_ENABLED://打开WiFi
              mScanner.resume();//从下面的方法中可以看到,该方法是用于开启WiFi的扫描,并记录扫描次数
                return; // not break, to avoid the call to pause() below

            case WifiManager.WIFI_STATE_ENABLING://正在打开WiFi
                addMessagePreference(R.string.wifi_starting);
                break;

            case WifiManager.WIFI_STATE_DISABLED://关闭WiFi
              //用户可以在wlan-->高级选项中去设置时是否随时都可以扫描(关闭WiFi后也可以扫描),根据用户的选择,
               //设置在关闭WLAN后显示界面上的文本
              setOffMessage();
                break;
        }

        mLastInfo = null;
        mLastState = null;
        mScanner.pause();//移除message通知
    }

因为更新的方法中涉及到了Scanner,这里的Scanner是自定义的内部类,继承自handler,代码如下

代码语言:javascript
复制
private class Scanner extends Handler {
        private int mRetry = 0;

        void resume() {
            if (!hasMessages(0)) {
                sendEmptyMessage(0);
            }
        }

        void forceScan() {
            removeMessages(0);
            sendEmptyMessage(0);
        }

        void pause() {
            mRetry = 0;
            removeMessages(0);
        }

        @Override
        public void handleMessage(Message message) {
            if (mWifiManager.startScan()) {
                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, 10*1000);//10s后再次发送message
        }
    }

可以看到,扫描附近可用WiFi的方法为mWifiManager.startScan()该方法对用户可见,可直接调用

ii>

代码语言:javascript
复制
/**
     * An access point scan has completed, and results are available from the supplicant.
     * Call {@link #getScanResults()} to obtain the results.
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";

SCAN_RESULT_AVAILABLE_ACTION:WiFi扫描结束时系统会发送该广播,用户可以监听该广播通过调用WifiManager的getScanResults方法来获取到扫描结果

代码语言:javascript
复制
else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
                WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
                WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
                updateAccessPoints();

当用户扫描到WiFi或者WiFi信息发生改变时都需要去更新WiFi列表,更新WiFi列表的方法为updateAccessPoints(),扫描加载列表这块感觉相当复杂,慢慢分析

首先在扫描结束后可以通过getScanResult()获取到扫描后的WiFi列表,返回值为List<ScanResult>,所返回的每个WiFi会携带如下信息

各字段分别代表什么含义呢?

BSSID:The address of the access point,接入点地址(String值)

SSID:The network name,WiFi名字(String值)

capabilities:Describes the authentication,key management,and encryption schemes supported by the access point,描述接入点的身份验证,密钥管理和加密方案(String值)

wifiSsid:ASCII encode SSID,This will replace SSID when we deprecate it ,SSID的ASCII码编码,当不支持它时我们可以用它来代替ssid(WifiSsid值)

timestamp:timestamp in microseconds (since boot)when this result was last seen,距离上一次的更改的微秒数

level:对于level的定义从源码中可以看到,表示信号的强度,属于int型数值,

代码语言:javascript
复制
 /**
     * The detected signal level in dBm, also known as the RSSI.
     *
     * <p>Use {@link android.net.wifi.WifiManager#calculateSignalLevel} to convert this number into
     * an absolute signal level which can be displayed to a user.
     */
    public int level;

对于信号强度的显示可以通过如下代码

代码语言:javascript
复制
mWifiLevel.setImageLevel(WifiManager.calculateSignalLevel(mList.get(position).level,4));

frequency:The primary 20 MHz frequency (in MHz) of the channel over which the client is communicating with the access point.客户端与接入点通信的频率

我们一般显示WiFi列表用到的是SSID、level和capabilities

Android源码中扫描到WiFi后就需要去加载列表,在接收到SCAN_RESULT_AVAILABLE_ACTION广播后调用updateAccessPoints方法进行更新列表,在该方法中会根据WiFi的开关状态来对UI进行更新,只有在WiFi开启时 才会去更新列表,这里不再对其他情况进行赘述,在WiFi开启时会通过如下代码加载

代码语言:javascript
复制
 private void updateAccessPoints() {
        // Safeguard from some delayed event handling
        if (getActivity() == null) return;

        if (isRestrictedAndNotPinProtected()) {
            addMessagePreference(R.string.wifi_empty_list_user_restricted);
            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 (!getResources().getBoolean(R.bool.set_wifi_priority)) {
                    getPreferenceScreen().removeAll();
                }
                if(accessPoints.size() == 0) {
                    addMessagePreference(R.string.wifi_empty_list_wifi_on);
                }
                if (!getResources().getBoolean(R.bool.set_wifi_priority)) {
                   for (AccessPoint accessPoint : accessPoints) {
                       <pre name="code" class="java">                  
      //WiFisettings的xml文件的根节点为preferencescreen,所以通过如下方法添加
      preferencegetPreferenceScreen().addPreference(accessPoint); } } 
if (accessPoints.isEmpty()){ addMessagePreference(R.string.wifi_empty_list_wifi_on); } 
break; case WifiManager.WIFI_STATE_ENABLING://如果WiFi处于正在打开的状态,则清除列表 。。。。。。。。。 } }

那么接入点列表的获取是如何进行的呢?

代码语言:javascript
复制
 private List<AccessPoint> constructAccessPoints() {
        ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
        /** Lookup table to more quickly update AccessPoints by only considering objects with the
         * correct SSID.  Maps SSID -> List of AccessPoints with the given SSID.  */
          //key为ssid,value为接入点ScanResult
      Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();

        if (getResources().getBoolean(R.bool.set_wifi_priority)) {
            emptyCategory();
        }
      //getConfiguredNetwors可以返回一个配置列表,获取到配置好的网络连接,该列表存放了关于已经连接过的接入点WiFi的信息,
          //返回的列表中包括如下字段,当WiFi 关闭时会返回null
           <pre name="code" class="java">         /** <ul>
          * <li>networkId</li>
          * <li>SSID</li>
          * <li>BSSID</li>
          * <li>priority</li>
          * <li>allowedProtocols</li>
          * <li>allowedKeyManagement</li>
          * <li>allowedAuthAlgorithms</li>
          * <li>allowedPairwiseCiphers</li>
          * <li>allowedGroupCiphers</li>
          * </ul>
          */
final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
        if (configs != null) {
            for (WifiConfiguration config : configs) {
                if (config.SSID != null) {
                    AccessPoint accessPoint = new AccessPoint(getActivity(), config);
                    accessPoint.update(mLastInfo, mLastState);
                    accessPoints.add(accessPoint);
                    apMap.put(accessPoint.ssid, accessPoint);
                    if (getResources().getBoolean(R.bool.set_wifi_priority)) {
                        SetAPCategory(accessPoint, mConfigedAP);
                    }
                }
            }
            if (getResources().getBoolean(R.bool.set_wifi_priority)) {
                if (mConfigedAP != null && mConfigedAP.getPreferenceCount() == 0) {
                    getPreferenceScreen().removePreference(mConfigedAP);
                }
            }
        }

         //获取到WiFi扫描结果,返回附近可用WiFi,包括已经连接的或者已经保存的WiFi
        final List<ScanResult> results = mWifiManager.getScanResults();
        if (results != null) {
            for (ScanResult result : results) {
                // Ignore hidden and ad-hoc networks.
                if (result.SSID == null || result.SSID.length() == 0 ||
                        result.capabilities.contains("[IBSS]")) {
                    continue;
                }

                boolean found = false;
                for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
                    if (accessPoint.update(result))
                        found = true;
                }
                if (!found) {
                    AccessPoint accessPoint = new AccessPoint(getActivity(), result);
                    accessPoints.add(accessPoint);
                    apMap.put(accessPoint.ssid, accessPoint);
                    if (getResources().getBoolean(R.bool.set_wifi_priority)) {
                        SetAPCategory(accessPoint, mUnKnownAP);
                    }
                }
            }
            if (getResources().getBoolean(R.bool.set_wifi_priority)) {
                if(mUnKnownAP !=null && mUnKnownAP.getPreferenceCount() == 0){
                    getPreferenceScreen().removePreference(mUnKnownAP);
                }
            }
        }

        // Pre-sort accessPoints to speed preference insertion
        Collections.sort(accessPoints);
        return accessPoints;
    }

调用mWifiManager.getConfigureNetworks()方法获取到的是已经配置过连接过的WiFi列表,列表包含下列值

对于扫描到的WiFi的保护方式通过判断scanresult的capabilities字段是否包含对应的string来判断属于何种保护方式

代码语言:javascript
复制
 boolean wpa = result.capabilities.contains("WPA-PSK");
        boolean wpa2 = result.capabilities.contains("WPA2-PSK")

iii>

代码语言:javascript
复制
/**
     * The network IDs of the configured networks could have changed.
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";

NETWORK_IDS_CHANGED:所配置的网络的网络ID可能已经改变

iv>

代码语言:javascript
复制
  /**
     * Broadcast intent action indicating that the state of establishing a connection to
     * an access point has changed.One extra provides the new
     * {@link SupplicantState}. Note that the supplicant state is Wi-Fi specific, and
     * is not generally the most useful thing to look at if you are just interested in
     * the overall state of connectivity.
     * @see #EXTRA_NEW_STATE
     * @see #EXTRA_SUPPLICANT_ERROR
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String SUPPLICANT_STATE_CHANGED_ACTION =
        "android.net.wifi.supplicant.STATE_CHANGE";

SUPPLICANT_STATE_CHANGED_ACTION:正在建立的连接状态已经改变,该广播会携带两个值

v>

代码语言:javascript
复制
 /**
     * Broadcast intent action indicating that the configured networks changed.
     * This can be as a result of adding/updating/deleting a network. If
     * {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is set to true the new configuration
     * can be retreived with the {@link #EXTRA_WIFI_CONFIGURATION} extra. If multiple
     * Wi-Fi configurations changed, {@link #EXTRA_WIFI_CONFIGURATION} will not be present.
     * @hide
     */
    public static final String CONFIGURED_NETWORKS_CHANGED_ACTION =
        "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";

CONFIGURED_NETWORKS_CHANGED_ACTION:当WiFi列表中的网络添加、更新或者删除时系统会发送该广播,但是该广播对用户隐藏,无法调用

vi>

代码语言:javascript
复制
/**
     * Broadcast intent action indicating that the state of Wi-Fi connectivity
     * has changed. One extra provides the new state
     * in the form of a {@link android.net.NetworkInfo} object. If the new
     * state is CONNECTED, additional extras may provide the BSSID and WifiInfo of
     * the access point.
     * as a {@code String}.
     * @see #EXTRA_NETWORK_INFO
     * @see #EXTRA_BSSID
     * @see #EXTRA_WIFI_INFO
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";

NETWORK_STATE_CHANGED_ACTION:WiFi连接发生改变时系统会发送该广播,通过字段EXTRA_NETWORK_INFO可以获取到WiFi连接的状态,如果是已连接的状态,则会有额外的两个字段,字段EXTRA_BSSID可以获取到所连接的WiFi的bssid,字段EXTRA_WIFI_INFO可以获取到所连接的WiFi的信息获取到wifiinfo实例

vii>

代码语言:javascript
复制
 /**
     * The RSSI (signal strength) has changed.
     * @see #EXTRA_NEW_RSSI
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";

WIFI_RSSI_CHANGED:当WiFi信号强度发生改变时系统会发送该广播,通过字段EXTRA_NEW_RSSI可以获取到改变后的wifi信号强度,当然也需要去更新WiFi列表

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-05-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Android4.4.2源码分析之WiFi模块(一)
相关产品与服务
多因子身份认证
多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档