【Settings开发】WiFi模块(一)

概要

    本篇主要介绍Settings应用的WiFi模块首页如何控制WiFi功能的开关,及如何扫描及连接一个WiFi热点。

使用到的类说明

    WiFiManager:提供基础的api来管理WiFi的联通性等操作。

    Scanner: 负责扫描WiFi。

    WiFiEnabler:内部包含SwitchUI控件,用户可点击此控件打开或者关闭WiFi。

    AccessPoint:继承自Preference,标识每个可连接的WiFi。

    WifiDialog:wifi信息弹框,可编辑。

WiFiSettings

    WiFiSettings是当用户在Settings页面点击WiFi按钮时填充到SettingsActivity的main_content中的PreferenceFragment。由顶部的WiFiEnabler开关和下部分的多个AccessPoint组成。可通过menu按键调出添加网络、保存网络、重新扫描等功能,当点击AccessPoint时弹出WiFiDialog,可编辑此AccessPoint的信息。

  •     构造方法
public WifiSettings() {
        super(DISALLOW_CONFIG_WIFI);
        mFilter = new IntentFilter();
        //WiFi状态变化,enable or disable 会携带wifi状态信息返回
        mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        //当我们调用startScan后返回的扫描结果,扫描到可用WiFi
        mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
        mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
        //声明正在建立连接的ap发生变化,在此处可判断WiFi是否建立连接
        mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
        //声明网络配置发生变化,作为添加、更新、删除一个网络的result的广播
        mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
        //声明连接的配置发生变化
        mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
        //网络状态发生变化,如果连接性可用,可能在广播中携带BSSID和NetWorkInfo信息返回
        mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        //wifi信号强度发生变化
        mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);

        mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                //处理广播信息
                handleEvent(intent);
            }
        };

        mScanner = new Scanner(this);
    }

    其构造方法添加了各种WiFi变化监听,同时初始化了WiFi扫描器Scanner。

  • 生命周期方法

        1.onActivityCreated:

        首先在其中通过Context.getSystemService方法获取WiFiManager。 然后监听了一些WiFi操作事件如保存WiFi、忘记WiFi、WiFi连接等事件的成功或失败。Activity的saveState现场恢复。

    //WiFi服务
    mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);

        WiFiSettings作为PreferenceFragment,在onActivityCreated方法中同时使用addPreferencesFromResource方法提供了根控件为PreferenceScreen的xml文件,后期的AccessPoint其实就是添加到此PreferenceScreen中。   

        2.onStart:

        在onStart中创建并显示了WiFi开关控制控件WiFiEnabler,在显示时会根据WiFiState把当前Switch置为合适状态。同时设置了WiFiEnabler监听WiFiState变换状态。

        3.onResume:

        在此生命周期中注册了构造方法中的广播接收者,并根据WiFiState更新下部分的UI,如果当前WiFi状态时打开的则需通过WiFiManager的getConfiguredNetworks方法和getScanResults方法获取到所有可用(getLevel()!=-1即可用)的AccessPoint添加到PreferenceScreen中。如果是其它状态则需根据状态显示不同的文字提示信息。关键的AccessPoint获取操作如下:

private static List<AccessPoint> constructAccessPoints(Context context,
            WifiManager wifiManager, WifiInfo lastInfo, DetailedState lastState) {
        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.  */
        Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();

        final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks();
        if (configs != null) {
            // Update "Saved Networks" menu option.
            if (savedNetworksExist != (configs.size() > 0)) {
                savedNetworksExist = !savedNetworksExist;
                if (context instanceof Activity) {
                    ((Activity) context).invalidateOptionsMenu();
                }
            }
            for (WifiConfiguration config : configs) {
                if (config.selfAdded && config.numAssociation == 0) {
                    continue;
                }
                AccessPoint accessPoint = new AccessPoint(context, config);
                if (lastInfo != null && lastState != null) {
                    accessPoint.update(lastInfo, lastState);
                }
                accessPoints.add(accessPoint);
                apMap.put(accessPoint.ssid, accessPoint);
            }
        }

        final List<ScanResult> results = wifiManager.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(context, result);
                    accessPoints.add(accessPoint);
                    apMap.put(accessPoint.ssid, accessPoint);
                }
            }
        }

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

        4.onPause:

        在可视但非前台时需要取消注册WiFi状态变化广播的接收,同时通知Scanner停止扫描。

@Override
    public void onPause() {
        super.onPause();
        if (mWifiEnabler != null) {
            mWifiEnabler.pause();
        }
        //停止WiFi状态监听
        getActivity().unregisterReceiver(mReceiver);
        //停止扫描
        mScanner.pause();
    }

        5.onDestroyView:

        在此生命周期中取消了WiFiEnabler控件对WiFi状态的监听。

  •       WiFi广播事件的处理HandleEvent
private void handleEvent(Intent intent) {
        String action = intent.getAction();
        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
            //更新WiFi状态,在extra中携带了state
            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)) {
            //更新各WiFi条目
            updateAccessPoints();
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                    WifiManager.EXTRA_NETWORK_INFO);
            mConnected.set(info.isConnected());
            changeNextButtonState(info.isConnected());
            //更新个WiFi条目
            updateAccessPoints();
            updateConnectionState(info.getDetailedState());
        } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
            //信号强度变化
            updateConnectionState(null);
        }
    }

        1.WiFi状态变化:

            WiFi状态在intent的extra即EXTRA_WIFI_STATE中,更新WiFi状态方法updateWifiState中根据wifi是否可用让Scanner扫描器启动扫描或停止扫描,并根据状态显示提示信息。

        2.扫描完成、新增or删除网络、已连接网络发生变化:

            当广播接收者接收到此广播时需要WiFiSettings需要更新页面上的AccessPoint个数。关于如何获取AccessPoint的数量参见上边的constructAccessPoints方法。

        3.WiFi信号强度发生改变:

            当某WiFi的信号强度发生变化时会调用各AccessPoint的update方法,在update内部会根据AccessPoint中的networkId(通过WifiInfo可获取)是否相同来确定是否是当前WiFi的信号强度发生改变。

  • 弹框(区分为1.点击AccessPoint,2.OptionsMenu,3.ContextMenu)
        1.WiFiSettings本身是一个PreferenceFragment,其点击事件通过重写其onPreferenceTreeClick方法实现。
@Override
    public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
        if (preference instanceof AccessPoint) {
            mSelectedAccessPoint = (AccessPoint) preference;
            /** Bypass dialog for unsecured, unsaved networks */
            if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE &&
                    mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
                mSelectedAccessPoint.generateOpenNetworkConfig();
                if (!savedNetworksExist) {
                    savedNetworksExist = true;
                    getActivity().invalidateOptionsMenu();
                }
                connect(mSelectedAccessPoint.getConfig());
            } else {
                showDialog(mSelectedAccessPoint, false);
            }
        } else {
            return super.onPreferenceTreeClick(screen, preference);
        }
        return true;
    }
            从中可见,如果我们点击的是AccessPoint则先判断当前被点击的AccessPoint如不需要密码且networkId=-1则直接连接,如果需要密码则显示弹框showDialog。在showDialog中有点绕,他会显示一个SettingsDialogFragment,但此DialogFragment的Dialog来源则是WiFiSettings的onCreateDialog方法。在onCreateDialog中则根据dialogId可知是case1,在其中新建了WifiDialog返回。
@Override
    public Dialog onCreateDialog(int dialogId) {
        switch (dialogId) {
            case WIFI_DIALOG_ID:
                AccessPoint ap = mDlgAccessPoint; // For manual launch
                if (ap == null) { // For re-launch from saved state
                    if (mAccessPointSavedState != null) {
                        ap = new AccessPoint(getActivity(), mAccessPointSavedState);
                        // For repeated orientation changes
                        mDlgAccessPoint = ap;
                        // Reset the saved access point data
                        mAccessPointSavedState = null;
                    }
                }
                // If it's null, fine, it's for Add Network
                mSelectedAccessPoint = ap;
                mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);
                return mDialog;
            //...

        }
        //...
    }
            2.点击OptionsMenu时(onOptionsItemSelected)弹出的菜单中有添加网络、保存网络、扫描WiFi、高级选项。其中扫描Wifi直接调用Scanner,高级选项和保存网络则是启动其他fragment,这里不展开。其他则是弹框操作,同样是弹出WifiDialog。
            3.当我们长按页面上的某个view控件时会在onCreateContextMenu中响应到,我们的WifiSettings在其中判断如果我们长按的是AccessPoint则会弹出menu,弹出的menu中有连接、忘记、修改、WriteNFC等按钮。onContextItemSelected中除了forget(忘记网络)按钮会直接调用WiFiManager的forget api之外,其他都会showDialog。
@Override
    public boolean onContextItemSelected(MenuItem item) {
        if (mSelectedAccessPoint == null) {
            return super.onContextItemSelected(item);
        }
        switch (item.getItemId()) {
            case MENU_ID_CONNECT: {
                if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
                    connect(mSelectedAccessPoint.networkId);
                } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
                    /** Bypass dialog for unsecured networks */
                    mSelectedAccessPoint.generateOpenNetworkConfig();
                    connect(mSelectedAccessPoint.getConfig());
                } else {
                    showDialog(mSelectedAccessPoint, true);
                }
                return true;
            }
            case MENU_ID_FORGET: {
                mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);
                return true;
            }
            case MENU_ID_MODIFY: {
                showDialog(mSelectedAccessPoint, true);
                return true;
            }
            case MENU_ID_WRITE_NFC:
                showDialog(WRITE_NFC_DIALOG_ID);
                return true;

        }
        return super.onContextItemSelected(item);
    }
  • WiFiSettings中其余关键方法
            1.忘记网络操作:
/* package */ void forget() {
        if (mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
            // Should not happen, but a monkey seems to trigger it
            Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
            return;
        }

        mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);

        if (mWifiManager.isWifiEnabled()) {
            mScanner.resume();
        }
        //更新WiFi条目
        updateAccessPoints();

        // We need to rename/replace "Next" button in wifi setup context.
        changeNextButtonState(false);
    }
            2.连接网络:
/* package */ void submit(WifiConfigController configController) {

        final WifiConfiguration config = configController.getConfig();

        if (config == null) {
            if (mSelectedAccessPoint != null
                    && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
                //调用WiFiManager的connect方法
                connect(mSelectedAccessPoint.networkId);
            }
        } else if (config.networkId != INVALID_NETWORK_ID) {
            if (mSelectedAccessPoint != null) {
                mWifiManager.save(config, mSaveListener);
            }
        } else {
            if (configController.isEdit()) {
                mWifiManager.save(config, mSaveListener);
            } else {
                //调用WiFiManager的connect方法
                connect(config);
            }
        }
        //重新调用startScan
        if (mWifiManager.isWifiEnabled()) {
            mScanner.resume();
        }
        //更新Wifi列表
        updateAccessPoints();
    }
Scanner
    Scanner扫描器时WifiSettings的内部类,其本质上是一个Handler。拥有resume(开始扫描)、pause(暂停扫描)、forceScan(强制扫描-立即执行)等方法。
    扫描Wifi本质上是调用WiFiManager的startScan方法开始扫描。
private static class Scanner extends Handler {
        private int mRetry = 0;
        private WifiSettings mWifiSettings = null;

        Scanner(WifiSettings wifiSettings) {
            mWifiSettings = wifiSettings;
        }

        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 (mWifiSettings.mWifiManager.startScan()) {
                mRetry = 0;
            } else if (++mRetry >= 3) {
                mRetry = 0;
                Activity activity = mWifiSettings.getActivity();
                if (activity != null) {
                    Toast.makeText(activity, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
                }
                return;
            }
            sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
        }
    }
WifiDialog
    当我们长按wifi列表或者点击菜单键等操作时弹出的弹窗dialog,在弹窗中可显示该WiFi的ip配置、代理、名称、安全协议等信息,同时可根据创建弹窗时的edit属性来控制这些信息的可编辑性。
    在WifiDialog类中并无几行代码,Google把其中的代码进行了拆分,逻辑部分交由WifiConfigController控制。可参见WifiDialog的onCreate方法:
@Override
    protected void onCreate(Bundle savedInstanceState) {
        mView = getLayoutInflater().inflate(R.layout.wifi_dialog, null);
        setView(mView);
        setInverseBackgroundForced(true);
        mController = new WifiConfigController(this, mView, mAccessPoint, mEdit);
        super.onCreate(savedInstanceState);

        if (mHideSubmitButton) {
            mController.hideSubmitButton();
        } else {
            /* During creation, the submit button can be unavailable to determine
             * visibility. Right after creation, update button visibility */
            mController.enableSubmitIfAppropriate();
        }
    }
    而在WifiConfigController中,对各个控件进行可编辑区分,如果是新建Wifi则处于编辑状态,其它则具体根据WifiDialog的传参mEdit有关。
    在WifiConfigController中主要展示了以下三类信息:
      //安全信息 - 身份认证协议
      showSecurityFields();
      //ip配置信息 - ip、网关、dns
      showIpConfigFields();
      //代理信息 - static、pac、none
      showProxyFields();
    当我们提交弹框中的wifi信息时,会调用上文中WifiSettings中其余关键方法中的submit方法,在submit方法中,我们可以看见其会去获取WifiConfigController的config信息(通过getConfig方法)。
    getConfig方法显示是一个重要的知识点,此方法构造了WifiConfiguration信息,而WifiConfiguration正是我们连接网络时需要的重要参数之一。

/* package */ WifiConfiguration getConfig() {
        if (mAccessPoint != null && mAccessPoint.networkId != INVALID_NETWORK_ID && !mEdit) {
            return null;
        }

        WifiConfiguration config = new WifiConfiguration();

        if (mAccessPoint == null) {
            //wifi名称
            config.SSID = AccessPoint.convertToQuotedString(
                    mSsidView.getText().toString());
            // If the user adds a network manually, assume that it is hidden.
            //新建的网络默认名称hide
            config.hiddenSSID = true;
        } else if (mAccessPoint.networkId == INVALID_NETWORK_ID) {
            //networkId=-1
            config.SSID = AccessPoint.convertToQuotedString(
                    mAccessPoint.ssid);
        } else {
            //networkId用于区分各个wifi
            config.networkId = mAccessPoint.networkId;
        }
        //加密类型区分为 none、wep、wpa/wpa2 psk
        switch (mAccessPointSecurity) {
            case AccessPoint.SECURITY_NONE:
                config.allowedKeyManagement.set(KeyMgmt.NONE);
                break;

            case AccessPoint.SECURITY_WEP:
                config.allowedKeyManagement.set(KeyMgmt.NONE);
                config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
                config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
                if (mPasswordView.length() != 0) {
                    int length = mPasswordView.length();
                    String password = mPasswordView.getText().toString();
                    // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
                    if ((length == 10 || length == 26 || length == 58) &&
                            password.matches("[0-9A-Fa-f]*")) {
                        config.wepKeys[0] = password;
                    } else {
                        config.wepKeys[0] = '"' + password + '"';
                    }
                }
                break;

            case AccessPoint.SECURITY_PSK:
                config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
                if (mPasswordView.length() != 0) {
                    String password = mPasswordView.getText().toString();
                    if (password.matches("[0-9A-Fa-f]{64}")) {
                        config.preSharedKey = password;
                    } else {
                        config.preSharedKey = '"' + password + '"';
                    }
                }
                break;

            case AccessPoint.SECURITY_EAP:
                config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
                config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
                config.enterpriseConfig = new WifiEnterpriseConfig();
                int eapMethod = mEapMethodSpinner.getSelectedItemPosition();
                int phase2Method = mPhase2Spinner.getSelectedItemPosition();
                config.enterpriseConfig.setEapMethod(eapMethod);
                switch (eapMethod) {
                    case Eap.PEAP:
                        // PEAP supports limited phase2 values
                        // Map the index from the PHASE2_PEAP_ADAPTER to the one used
                        // by the API which has the full list of PEAP methods.
                        switch(phase2Method) {
                            case WIFI_PEAP_PHASE2_NONE:
                                config.enterpriseConfig.setPhase2Method(Phase2.NONE);
                                break;
                            case WIFI_PEAP_PHASE2_MSCHAPV2:
                                config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2);
                                break;
                            case WIFI_PEAP_PHASE2_GTC:
                                config.enterpriseConfig.setPhase2Method(Phase2.GTC);
                                break;
                            default:
                                Log.e(TAG, "Unknown phase2 method" + phase2Method);
                                break;
                        }
                        break;
                    default:
                        // The default index from PHASE2_FULL_ADAPTER maps to the API
                        config.enterpriseConfig.setPhase2Method(phase2Method);
                        break;
                }
                //身份认证协议
                String caCert = (String) mEapCaCertSpinner.getSelectedItem();
                if (caCert.equals(unspecifiedCert)) caCert = "";
                config.enterpriseConfig.setCaCertificateAlias(caCert);
                String clientCert = (String) mEapUserCertSpinner.getSelectedItem();
                if (clientCert.equals(unspecifiedCert)) clientCert = "";
                config.enterpriseConfig.setClientCertificateAlias(clientCert);
                config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString());
                config.enterpriseConfig.setAnonymousIdentity(
                        mEapAnonymousView.getText().toString());

                if (mPasswordView.isShown()) {
                    // For security reasons, a previous password is not displayed to user.
                    // Update only if it has been changed.
                    if (mPasswordView.length() > 0) {
                        config.enterpriseConfig.setPassword(mPasswordView.getText().toString());
                    }
                } else {
                    // clear password
                    config.enterpriseConfig.setPassword(mPasswordView.getText().toString());
                }
                break;
            default:
                return null;
        }

        config.setIpConfiguration(
                new IpConfiguration(mIpAssignment, mProxySettings,
                                    mStaticIpConfiguration, mHttpProxy));

        return config;
    }

    可以看见,其中配置了当前wifi网络的SSID、networkId、根据加密类型得到的密码、ip信息。事实上,创建wifi的config信息参考此处getConfig方法。以下是我们的WifiDialog的各个按钮点击的回调,forget最终会调用WiFiManager的forget方法,而submit最终会调用WiFiManager的connect方法尝试连接wifi网络。

    @Override
    public void onClick(DialogInterface dialogInterface, int button) {
        if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
            forget();
        } else if (button == WifiDialog.BUTTON_SUBMIT) {
            if (mDialog != null) {
                submit(mDialog.getController());
            }
        }
    }
AccessPoint

    在这里我们只讲解AccessPoint解析WiFiConfiguration或ScanResult的过程。在上边WiFiSetings的生命周期onResume中会加载可用的AccessPoint到UI页面。而在关键的constructAccessPoints方法中,新建的AccessPoint有两种构造传参方式,一种传入WiFiConfiguration,一种传入ScanResult。

AccessPoint(Context context, WifiConfiguration config) {
        super(context);
        loadConfig(config);
        refresh();
    }

    AccessPoint(Context context, ScanResult result) {
        super(context);
        loadResult(result);
        refresh();
    }

    其中构造方法内部的loadConfig和loadResult就是对两种参数分别进行解析的过程。

private void loadConfig(WifiConfiguration config) {
        ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
        bssid = config.BSSID;
        security = getSecurity(config);
        networkId = config.networkId;
        mConfig = config;
    }

    private void loadResult(ScanResult result) {
        ssid = result.SSID;
        bssid = result.BSSID;
        security = getSecurity(result);
        wpsAvailable = security != SECURITY_EAP && result.capabilities.contains("WPS");
        if (security == SECURITY_PSK)
            pskType = getPskType(result);
        mRssi = result.level;
        mScanResult = result;
        if (result.seen > mSeen) {
            mSeen = result.seen;
        }
    }

    在两者内部:

        1.取出SSID,即当前wifi的名称。

        2.取出BSSID,当前wifi的mac地址。

        3.判断当前的加密类型,通过getSecurity方法。如果是PSK加密则需继续通过getPskType得知。

        4.loadConfig中取出networkId。

        5.loadResult中获取信号强度。

    通过WiFiConfiguration和ScanResult判断加密类型的getSecurity方法如下:

static int getSecurity(WifiConfiguration config) {
        if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
            return SECURITY_PSK;
        }
        if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
                config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
            return SECURITY_EAP;
        }
        return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
    }

    private static int getSecurity(ScanResult result) {
        if (result.capabilities.contains("WEP")) {
            return SECURITY_WEP;
        } else if (result.capabilities.contains("PSK")) {
            return SECURITY_PSK;
        } else if (result.capabilities.contains("EAP")) {
            return SECURITY_EAP;
        }
        return SECURITY_NONE;
    }

    从上可知,如果是从config中判断加密类型,可追溯到WifiConfigController中geConfig方法的设置。此处通过判断WifiConfiguration的allowedKeyManagement中设置的值判断加密类型。如果是从ScanResult中获取加密类型,则需从ScanResult的capabilities中判断,如果包含WEP,这说明时WEP加密类型,如果包含PSK,则需进一步判断是WPA还是WPA2加密类型,如果capabilities中包含EAP,这说明是身份认证协议类型,需通过enterpriseConfig字段判断是使用的哪种身份认证协议。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值