转载 用于学习

https://blog.csdn.net/zoe6553/article/details/6622245?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167353805316800222890370%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=167353805316800222890370&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-4-6622245-null-null.142v70one_line,201v4add_ask&utm_term=wifi%E8%BF%9E%E6%8E%A5%E6%B5%81%E7%A8%8B&spm=1018.2226.3001.4187

Wi-Fi 连接过程可以从 Settings App 中点击任意 Wi-Fi 条目连接说起。点击条目以后会弹出一个对话框,根据不同的 Wi-Fi 类型需要填入必要的信息,再点击连接按钮,发起连接过程。
在这里插入图片描述

点击 Dialog 上的按钮会路由到 WifiDialog.BUTTON_SUBMIT 分支,如果是已经连接成功的 Wi-Fi 则路由到 WifiDialog.BUTTON_FORGET 分支。WifiDialog.BUTTON_SUBMIT 分支进一步调用了 submit(…) 方法。

submit(…) 方法中首先调用 WifiConfigController 类的 getConfig() 方法获取此 Wi-Fi 的 WifiConfiguration。接着就会调用 WifiManager save(…) 方法保存此 WifiConfiguration,最后进一步调用 connect(…) 方法,connect(…) 方法实际调用了 WifiManager 的 connect(…) 方法。

packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java

public class WifiSettings extends RestrictedSettingsFragment
       implements DialogInterface.OnClickListener, Indexable, WifiTracker.WifiListener,
       AccessPointListener {
   ......
   @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());
           }
       }
   }

   /* package */ void submit(WifiConfigController configController) {

       final WifiConfiguration config = configController.getConfig();

       if (config == null) {
           if (mSelectedAccessPoint != null
                   && mSelectedAccessPoint.isSaved()) {
               connect(mSelectedAccessPoint.getConfig());
           }
       } else if (configController.isModify()) {
           mWifiManager.save(config, mSaveListener);
       } else {
           mWifiManager.save(config, mSaveListener);
           if (mSelectedAccessPoint != null) { // Not an "Add network"
               connect(config);
           }
       }

       mWifiTracker.resumeScanning();
   }
   ......
   protected void connect(final WifiConfiguration config) {
       MetricsLogger.action(getActivity(), MetricsLogger.ACTION_WIFI_CONNECT);
       mWifiManager.connect(config, mConnectListener);
   }    
   ......
}


getConfig() 方法实现行数不少,实际上就是在构建 WifiConfiguration 对象。根据不同的 Wi-Fi 安全类型去填充 WifiConfiguration 中不同的字段,这里包括我们家庭常用的 WPA2/Personal 以及企业常用的 WPA2/Enterprise。企业级 Wi-Fi 会额外构建 WifiEnterpriseConfig 对象。另外,这部分构建 WifiConfiguration 对象的代码对 App 做连接 Wi-Fi 功能有非常大的参考意义。

packages/apps/Settings/src/com/android/settings/wifi/WifiConfigController.java

public class WifiConfigController implements TextWatcher,
       AdapterView.OnItemSelectedListener, OnCheckedChangeListener {
    ......
    /* package */ WifiConfiguration getConfig() {
        if (!mEdit) {
            return null;
        }

        WifiConfiguration config = new WifiConfiguration();

        if (mAccessPoint == null) {
            config.SSID = AccessPoint.convertToQuotedString(
                    mSsidView.getText().toString());
            // If the user adds a network manually, assume that it is hidden.
            config.hiddenSSID = true;
        } else if (!mAccessPoint.isSaved()) {
            config.SSID = AccessPoint.convertToQuotedString(
                    mAccessPoint.getSsidStr());
        } else {
            config.networkId = mAccessPoint.getConfig().networkId;
        }

        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);
                if (eapMethod == Eap.SIM || eapMethod == Eap.AKA || eapMethod == Eap.AKA_PRIME) {
                    config.enterpriseConfig.setIdentity("");
                    config.enterpriseConfig.setAnonymousIdentity("");
                } else if (eapMethod == Eap.PWD) {
                    config.enterpriseConfig.setIdentity(mEapIdentityView.getText().toString());
                    config.enterpriseConfig.setAnonymousIdentity("");
                } else {
                    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;
    }    
    ......
}


connect(…) 表示使用给定的 networkId 连接到网络,它用来代替 enableNetwork(),saveConfiguration() 和 reconnect()

save(…) 方法将给定的网络保存在 supplicant 配置中。如果网络已经存在,则会更新配置。默认情况下启用新网络。

  1. 对于新网络,将使用此函数代替 addNetwork(),enableNetwork() 和 saveConfiguration() 的顺序调用;
  2. 对于现有网络,它可以完成 updateNetwork() 和 saveConfiguration() 的任务。

这两个方法都先调用了 validateChannel() 来检查 AsyncChannel 是否为 null,如果为 null 就会抛出 IllegalStateException 异常。接着通过 AsyncChannel 发送消息。

AsyncChannel 表示两个 Handler 之间的异步通道,两个 Handler 可能位于同一进程也可能在不同进程中,AysncChannel 可以使用两种协议。第一种是简单的 request/reply 协议,其中服务器不需要知道是哪个客户机发出请求。第二种使用模型是 server/destination 也需要知道它连接的是哪个客户机。

frameworks/base/wifi/java/android/net/wifi/WifiManager.java

public class WifiManager {
    ......
    public void connect(WifiConfiguration config, ActionListener listener) {
        if (config == null) throw new IllegalArgumentException("config cannot be null");
        validateChannel();
        // 当传递配置对象时,arg1 使用 INVALID_NETWORK_ID
        // arg1 用于在网络已经存在时传递网络 id
        sAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
                putListener(listener), config);
    }
    
    public void save(WifiConfiguration config, ActionListener listener) {
        if (config == null) throw new IllegalArgumentException("config cannot be null");
        validateChannel();
        sAsyncChannel.sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
    }
    ......
}


sAsyncChannel 初始化的时候传入了 Messenger 对象,代表目的地处理方。这是从 getWifiServiceMessenger() 方法拿到的。此方法获取 WifiService Handler 的一个引用,这被客户端用来与 WifiService 建立 AsyncChannel。

frameworks/base/wifi/java/android/net/wifi/WifiManager.java

public class WifiManager {
    ......
    public Messenger getWifiServiceMessenger() {
        try {
            return mService.getWifiServiceMessenger();
        } catch (RemoteException e) {
            return null;
        } catch (SecurityException e) {
            return null;
        }
    }
    ......
}


先进行权限检查,然后构建了一个 Messenger 对象返回。Messenger 构造器入参 mClientHandler 处理客户端发过来的消息。

frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java

public final class WifiServiceImpl extends IWifiManager.Stub {
    ......
    public Messenger getWifiServiceMessenger() {
        enforceAccessPermission();
        enforceChangePermission();
        return new Messenger(mClientHandler);
    }
    ......
}  


客户端发来的消息在 ClientHandler handleMessage(…) 方法中得到处理。WifiManager.SAVE_NETWORK 消息就是 WifiManager 调用 save(…) 方法发送过来的。而 WifiManager.CONNECT_NETWORK 消息是 connect(…) 发过来的。这两个消息在这里直接被 WifiStateMachine 进行了转发。

frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java

public final class WifiServiceImpl extends IWifiManager.Stub {
    ......
    private class ClientHandler extends Handler {

        ClientHandler(android.os.Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                ......
                /* 客户端命令被转发到状态机 */
                case WifiManager.CONNECT_NETWORK:
                case WifiManager.SAVE_NETWORK: {
                    WifiConfiguration config = (WifiConfiguration) msg.obj;
                    int networkId = msg.arg1;
                    if (msg.what == WifiManager.SAVE_NETWORK) {
                        Slog.e("WiFiServiceImpl ", "SAVE"
                                + " nid=" + Integer.toString(networkId)
                                + " uid=" + msg.sendingUid
                                + " name="
                                + mContext.getPackageManager().getNameForUid(msg.sendingUid));
                    }
                    if (msg.what == WifiManager.CONNECT_NETWORK) {
                        Slog.e("WiFiServiceImpl ", "CONNECT "
                                + " nid=" + Integer.toString(networkId)
                                + " uid=" + msg.sendingUid
                                + " name="
                                + mContext.getPackageManager().getNameForUid(msg.sendingUid));
                    }

                    if (config != null && isValid(config)) {
                        if (DBG) Slog.d(TAG, "Connect with config" + config);
                        mWifiStateMachine.sendMessage(Message.obtain(msg));
                    } else if (config == null
                            && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
                        if (DBG) Slog.d(TAG, "Connect with networkId" + networkId);
                        mWifiStateMachine.sendMessage(Message.obtain(msg));
                    } else {
                        Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
                        if (msg.what == WifiManager.CONNECT_NETWORK) {
                            replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,
                                    WifiManager.INVALID_ARGS);
                        } else {
                            replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED,
                                    WifiManager.INVALID_ARGS);
                        }
                    }
                    break;
                }
                case WifiManager.FORGET_NETWORK:
                    if (isOwner(msg.sendingUid)) {
                        mWifiStateMachine.sendMessage(Message.obtain(msg));
                    } else {
                        Slog.e(TAG, "Forget is not authorized for user");
                        replyFailed(msg, WifiManager.FORGET_NETWORK_FAILED,
                                WifiManager.NOT_AUTHORIZED);
                    }
                    break;
                case WifiManager.START_WPS:
                case WifiManager.CANCEL_WPS:
                case WifiManager.DISABLE_NETWORK:
                case WifiManager.RSSI_PKTCNT_FETCH: {
                    mWifiStateMachine.sendMessage(Message.obtain(msg));
                    break;
                }
                default: {
                    Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg);
                    break;
                }
            }
        }

        ......
    }
    private ClientHandler mClientHandler;
    ......
}  


保存网络配置,和连接都是在 ConnectModeState 类 processMessage(…) 方法中进行处理的。

保存 Wi-Fi 配置步骤:调用 WifiConfigStore 类 saveNetwork(…) 方法进行保存配置;

连接 Wi-Fi 步骤:

  1. 调用 WifiConfigStore 类 saveNetwork(…) 方法进行保存配置;
  2. 调用 WifiConfigStore 类 enableNetworkWithoutBroadcast(…) 方法进行 enable;
  3. 调用 WifiConfigStore 类 selectNetwork(…) 方法进行 select。

frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java

public class WifiStateMachine extends StateMachine implements WifiNative.WifiPnoEventHandler,
    WifiNative.WifiRssiEventHandler {
    ......
    class ConnectModeState extends State {

        @Override
        public void enter() {
            connectScanningService();
        }

        @Override
        public boolean processMessage(Message message) {
            WifiConfiguration config;
            int netId;
            boolean ok;
            boolean didDisconnect;
            String bssid;
            String ssid;
            NetworkUpdateResult result;
            logStateAndMessage(message, getClass().getSimpleName());

            switch (message.what) {
                ......
                case WifiManager.CONNECT_NETWORK:
                    /**
                     *  对于一个新的网络,将传递一个配置来创建和连接。
                     *  对于一个现有的网络,将传递一个网络 id
                     */
                    netId = message.arg1;
                    config = (WifiConfiguration) message.obj;
                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
                    boolean updatedExisting = false;

                    /* 保存网络配置 */
                    if (config != null) {
                        ......
                        result = mWifiConfigStore.saveNetwork(config, message.sendingUid);
                        netId = result.getNetworkId();
                    }
                    ......
                    // 请确保网络已启用,因为 supplicant 将不会重新启用它
                    mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);

                    if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ true,
                            message.sendingUid) && mWifiNative.reconnect()) {
                        lastConnectAttemptTimestamp = System.currentTimeMillis();
                        targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);

                        /* 状态跟踪器在 completion/failure 时处理启用网络 */
                        mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
                        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
                        ......
                    } else {
                        loge("Failed to connect config: " + config + " netId: " + netId);
                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                                WifiManager.ERROR);
                        break;
                    }
                    break;
                case WifiManager.SAVE_NETWORK:
                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
                    // Fall thru
                case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
                    lastSavedConfigurationAttempt = null; // Used for debug
                    config = (WifiConfiguration) message.obj;
                    ......
                    result = mWifiConfigStore.saveNetwork(config, WifiConfiguration.UNKNOWN_UID);
                    ......
                    break;
                ......
                default:
                    return NOT_HANDLED;
            }
            return HANDLED;
        }
    }
    ......
}


添加/更新指定的配置并保存配置。

关键步骤:

  1. 调用 addOrUpdateNetworkNative(…) 添加或者更新配置;
  2. 如果是一个新网络,调用 WifiNative 类 enableNetwork(…) 方法 enable 网络;
  3. 调用 WifiNative 类 saveConfig(…) 保存配置。

frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConfigStore.java

public class WifiConfigStore extends IpConfigStore {
    ......
    NetworkUpdateResult saveNetwork(WifiConfiguration config, int uid) {
        WifiConfiguration conf;

        // 一个新的网络不能有 null SSID
        if (config == null || (config.networkId == INVALID_NETWORK_ID &&
                config.SSID == null)) {
            return new NetworkUpdateResult(INVALID_NETWORK_ID);
        }
        ......
        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
        NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
        int netId = result.getNetworkId();

        if (VDBG) localLog("WifiConfigStore: saveNetwork got it back netId=", netId);

        /* enable 一个新网络 */
        if (newNetwork && netId != INVALID_NETWORK_ID) {
            if (VDBG) localLog("WifiConfigStore: will enable netId=", netId);

            mWifiNative.enableNetwork(netId, false);
            conf = mConfiguredNetworks.get(netId);
            if (conf != null)
                conf.status = Status.ENABLED;
        }

        conf = mConfiguredNetworks.get(netId);
        if (conf != null) {
            if (conf.autoJoinStatus != WifiConfiguration.AUTO_JOIN_ENABLED) {
                if (VDBG) localLog("WifiConfigStore: re-enabling: " + conf.SSID);

                // 重新启用自动加入,因为已经提供了新信息
                conf.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
                enableNetworkWithoutBroadcast(conf.networkId, false);
            }
            if (VDBG) {
                loge("WifiConfigStore: saveNetwork got config back netId="
                        + Integer.toString(netId)
                        + " uid=" + Integer.toString(config.creatorUid));
            }
        }

        mWifiNative.saveConfig();
        sendConfiguredNetworksChangedBroadcast(conf, result.isNewNetwork() ?
                WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
        return result;
    }
    ......
}


如果提供的 networkId 是 INVALID_NETWORK_ID,我们将创建一个新的空网络配置。否则,网络id应该引用现有的配置。添加网络调用了 WifiNative 类 addNetwork() 方法。接着会配置参数主要调用 WifiNative 类 setNetworkVariable(…) 方法实现。

frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java

public class WifiStateMachine extends StateMachine implements WifiNative.WifiPnoEventHandler,
    WifiNative.WifiRssiEventHandler {
    ......
    private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config, int uid) {
        ......
        int netId = config.networkId;
        boolean newNetwork = false;
        // INVALID_NETWORK_ID 的 networkId 表示我们希望创建一个新的网络
        if (netId == INVALID_NETWORK_ID) {
            WifiConfiguration savedConfig = mConfiguredNetworks.getByConfigKey(config.configKey());
            if (savedConfig != null) {
                netId = savedConfig.networkId;
            } else {
                if (mMOManager.getHomeSP(config.FQDN) != null) {
                    loge("addOrUpdateNetworkNative passpoint " + config.FQDN
                            + " was found, but no network Id");
                }
                newNetwork = true;
                // 添加网络
                netId = mWifiNative.addNetwork();
                if (netId < 0) {
                    loge("Failed to add a network!");
                    return new NetworkUpdateResult(INVALID_NETWORK_ID);
                } else {
                    loge("addOrUpdateNetworkNative created netId=" + netId);
                }
            }
        }

        boolean updateFailed = true;
        // 配置网络参数
        setVariables: {

            if (config.SSID != null &&
                    !mWifiNative.setNetworkVariable(
                        netId,
                        WifiConfiguration.ssidVarName,
                        encodeSSID(config.SSID))) {
                loge("failed to set SSID: "+config.SSID);
                break setVariables;
            }

            ......
            // 配置企业级 Wi-Fi 网络参数
            if (config.enterpriseConfig != null &&
                    config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
                WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
                ......
            }
            updateFailed = false;
        } // End of setVariables

        if (updateFailed) {
            if (newNetwork) {
                mWifiNative.removeNetwork(netId);
                loge("Failed to set a network variable, removed network: " + netId);
            }
            return new NetworkUpdateResult(INVALID_NETWORK_ID);
        }

        /* 
         * 网络配置的更新需要从 supplicant 那里读取它们,以更新 mConfiguredNetworks。
         */
        WifiConfiguration currentConfig = mConfiguredNetworks.get(netId);
        if (currentConfig == null) {
            currentConfig = new WifiConfiguration();
            currentConfig.setIpAssignment(IpAssignment.DHCP);
            currentConfig.setProxySettings(ProxySettings.NONE);
            currentConfig.networkId = netId;
            if (config != null) {
                // Carry over the creation parameters
                currentConfig.selfAdded = config.selfAdded;
                currentConfig.didSelfAdd = config.didSelfAdd;
                currentConfig.ephemeral = config.ephemeral;
                currentConfig.autoJoinUseAggressiveJoinAttemptThreshold
                        = config.autoJoinUseAggressiveJoinAttemptThreshold;
                currentConfig.lastConnectUid = config.lastConnectUid;
                currentConfig.lastUpdateUid = config.lastUpdateUid;
                currentConfig.creatorUid = config.creatorUid;
                currentConfig.creatorName = config.creatorName;
                currentConfig.lastUpdateName = config.lastUpdateName;
                currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
                currentConfig.FQDN = config.FQDN;
                currentConfig.providerFriendlyName = config.providerFriendlyName;
                currentConfig.roamingConsortiumIds = config.roamingConsortiumIds;
                currentConfig.validatedInternetAccess = config.validatedInternetAccess;
                currentConfig.numNoInternetAccessReports = config.numNoInternetAccessReports;
                currentConfig.updateTime = config.updateTime;
                currentConfig.creationTime = config.creationTime;
            }
            if (DBG) {
                log("created new config netId=" + Integer.toString(netId)
                        + " uid=" + Integer.toString(currentConfig.creatorUid)
                        + " name=" + currentConfig.creatorName);
            }
        }
        ......
        readNetworkVariables(currentConfig);
        ......
        mConfiguredNetworks.put(netId, currentConfig);

        NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(currentConfig, config);
        result.setIsNewNetwork(newNetwork);
        result.setNetworkId(netId);
        ......
        return result;
    }    
    ......
}


现在不难发现承上启下的接口就是 WifiNative 类。可以清晰得看出把每个方法都包装成了一条命令执行了,以 addNetwork(…) 为例来分析一下。

frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java

public class WifiNative {
    ......
    public int addNetwork() {
        return doIntCommand("ADD_NETWORK");
    }
    
    public boolean setNetworkVariable(int netId, String name, String value) {
        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
        if (name.equals(WifiConfiguration.pskVarName)
                || name.equals(WifiEnterpriseConfig.PASSWORD_KEY)) {
            return doBooleanCommandWithoutLogging("SET_NETWORK " + netId + " " + name + " " + value);
        } else {
            return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
        }
    }
    
    public boolean enableNetwork(int netId, boolean disableOthers) {
        if (DBG) logDbg("enableNetwork nid=" + Integer.toString(netId)
                + " disableOthers=" + disableOthers);
        if (disableOthers) {
            return doBooleanCommand("SELECT_NETWORK " + netId);
        } else {
            return doBooleanCommand("ENABLE_NETWORK " + netId);
        }
    }
    ......
    public boolean selectNetwork(int netId) {
        if (DBG) logDbg("selectNetwork nid=" + Integer.toString(netId));
        return doBooleanCommand("SELECT_NETWORK " + netId);
    }
    ......
    public boolean saveConfig() {
        return doBooleanCommand("SAVE_CONFIG");
    }
    ......
}


doIntCommand(…) 方法内部调用了 doIntCommandNative(…) jni 方法转入 Native 层处理。

frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java

public class WifiNative {
    ......
    private native int doIntCommandNative(String command);
    ......
    private int doIntCommand(String command) {
        if (DBG) Log.d(mTAG, "doInt: " + command);
        synchronized (mLock) {
            int cmdId = getNewCmdIdLocked();
            String toLog = Integer.toString(cmdId) + ":" + mInterfacePrefix + command;
            int result = doIntCommandNative(mInterfacePrefix + command);
            localLog(toLog + " -> " + result);
            if (DBG) Log.d(mTAG, "   returned " + result);
            return result;
        }
    }    
    ......
}


android_net_wifi_doIntCommand(…) -> doIntCommand(…) -> doCommand(…) -> wifi_command(…)

frameworks/opt/net/wifi/service/jni/com_android_server_wifi_WifiNative.cpp

static jint android_net_wifi_doIntCommand(JNIEnv* env, jobject, jstring javaCommand) {
    return doIntCommand(env, javaCommand);
}
......
static jint doIntCommand(JNIEnv* env, jstring javaCommand) {
    char reply[REPLY_BUF_SIZE];
    if (!doCommand(env, javaCommand, reply, sizeof(reply))) {
        return -1;
    }
    return static_cast<jint>(atoi(reply));
}
......
static bool doCommand(JNIEnv* env, jstring javaCommand,
                      char* reply, size_t reply_len) {
    ScopedUtfChars command(env, javaCommand);
    if (command.c_str() == NULL) {
        return false; // ScopedUtfChars already threw on error.
    }

    if (DBG) {
        ALOGD("doCommand: %s", command.c_str());
    }

    --reply_len; // Ensure we have room to add NUL termination.
    if (::wifi_command(command.c_str(), reply, &reply_len) != 0) {
        return false;
    }

    // Strip off trailing newline.
    if (reply_len > 0 && reply[reply_len-1] == '\n') {
        reply[reply_len-1] = '\0';
    } else {
        reply[reply_len] = '\0';
    }
    return true;
}


wifi_command(…) 方法实现在 HAL 层,其只是将调用转发到 wifi_send_command(…) 方法。

wifi_command(…) 向 Wi-Fi 驱动程序发出命令。Android 扩展了链接 hostap.epitest.fi/wpa_supplic… 中列出的标准命令,包括支持向驱动发送命令:

请参阅 wifi/java/android/net/wifi/WifiNative.java 了解支持的驱动程序命令的详细信息。

wifi_send_command(…) 关键工作是由 wpa_ctrl_request(…) 方法完成的。

hardware/libhardware_legacy/wifi/wifi.c

int wifi_command(const char *command, char *reply, size_t *reply_len)
{
    return wifi_send_command(command, reply, reply_len);
}
......
int wifi_send_command(const char *cmd, char *reply, size_t *reply_len)
{
    int ret;
    if (ctrl_conn == NULL) {
        ALOGV("Not connected to wpa_supplicant - \"%s\" command dropped.\n", cmd);
        return -1;
    }
    ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), reply, reply_len, NULL);
    if (ret == -2) {
        ALOGD("'%s' command timed out.\n", cmd);
        /* unblocks the monitor receive socket for termination */
        TEMP_FAILURE_RETRY(write(exit_sockets[0], "T", 1));
        return -2;
    } else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) {
        return -1;
    }
    if (strncmp(cmd, "PING", 4) == 0) {
        reply[*reply_len] = '\0';
    }
    return 0;
}

wpa_supplicant 实现了一个控制接口,外部程序可以使用该接口来控制 wpa_supplicant 守护程序的操作以及获取状态信息和事件通知。有一个小型 C 库,以单个 C 文件 wpa_ctrl.c 的形式提供,该库提供了帮助程序功能以方便使用控制界面。外部程序可以将此文件链接到其中,然后使用 wpa_ctrl.h 中记录的库函数与 wpa_supplicant 进行交互。该库也可以与 C++ 一起使用。 wpa_cli.c 和 wpa_gui 是使用此库的示例程序。

进程间通信有多种机制。例如,Linux 版本的 wpa_supplicant 使用 UNIX 域套接字作为控制接口,而 Windows 版本的 UDP 套接字。wpa_ctrl.h 中定义的功能的使用可用于从外部程序隐藏所用 IPC 的细节。

使用控制界面

需要与 wpa_supplicant 通信的外部程序(例如 GUI 或配置实用程序)应链接到 wpa_ctrl.c。这使他们可以使用辅助函数通过 wpa_ctrl_open() 打开与控制界面的连接,并通过 wpa_ctrl_request() 发送命令。

wpa_supplicant 使用控制接口进行两种类型的通信:命令和未经请求的事件消息。命令是一对消息,来自外部程序的请求和来自 wpa_supplicant 的响应。这些可以使用 wpa_ctrl_request() 执行。wpa_supplicant 将未经请求的事件消息发送到控制接口连接,而没有来自外部程序的特定请求来接收每个消息。但是,外部程序需要使用 wpa_ctrl_attach() 连接到控制接口,以接收这些未经请求的消息。

如果控制接口连接既用于命令又用于未经请求的事件消息,则有可能在命令请求和响应之间接收未经请求的消息。wpa_ctrl_request() 调用方将需要提供一个回调 msg_cb 来处理这些消息。通常,通过两次调用 wpa_ctrl_open() 来打开两个控制接口连接,然后将其中一个连接用于命令,将另一个连接用于未经请求的消息,会更容易。这样,命令 请求/响应对 不会被未经请求的消息破坏。 wpa_cli 是一个示例,该示例说明了如何同时使用两个连接和 wpa_gui 演示如何使用两个单独的连接。

一旦不再需要控制接口连接,则应通过调用 wpa_ctrl_close() 将其关闭。如果该连接用于未经请求的事件消息,则应首先通过调用 wpa_ctrl_detach() 断开连接。

命令最终由 socket send(…) 发送了出去。

external/wpa_supplicant_8/wpa_supplicant/src/common/wpa_ctrl.c

#ifdef CTRL_IFACE_SOCKET
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))
{
	struct timeval tv;
	struct os_reltime started_at;
	int res;
	fd_set rfds;
	const char *_cmd;
	char *cmd_buf = NULL;
	size_t _cmd_len;

#ifdef CONFIG_CTRL_IFACE_UDP
	if (ctrl->cookie) {
		char *pos;
		_cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len;
		cmd_buf = os_malloc(_cmd_len);
		if (cmd_buf == NULL)
			return -1;
		_cmd = cmd_buf;
		pos = cmd_buf;
		os_strlcpy(pos, ctrl->cookie, _cmd_len);
		pos += os_strlen(ctrl->cookie);
		*pos++ = ' ';
		os_memcpy(pos, cmd, cmd_len);
	} else
#endif /* CONFIG_CTRL_IFACE_UDP */
	{
		_cmd = cmd;
		_cmd_len = cmd_len;
	}

	errno = 0;
	started_at.sec = 0;
	started_at.usec = 0;
retry_send:
	if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
		if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK)
		{
			/*
			 * Must be a non-blocking socket... Try for a bit
			 * longer before giving up.
			 */
			if (started_at.sec == 0)
				os_get_reltime(&started_at);
			else {
				struct os_reltime n;
				os_get_reltime(&n);
				/* Try for a few seconds. */
				if (os_reltime_expired(&n, &started_at, 5))
					goto send_err;
			}
			os_sleep(1, 0);
			goto retry_send;
		}
	send_err:
		os_free(cmd_buf);
		return -1;
	}
	os_free(cmd_buf);

	for (;;) {
		tv.tv_sec = 10;
		tv.tv_usec = 0;
		FD_ZERO(&rfds);
		FD_SET(ctrl->s, &rfds);
		res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
		if (res < 0)
			return res;
		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] = '\0';
					msg_cb(reply, res);
				}
				continue;
			}
			*reply_len = res;
			break;
		} else {
			return -2;
		}
	}
	return 0;
}
#endif /* CTRL_IFACE_SOCKET */


转载自:

作者:天涯一角
链接:https://juejin.cn/post/6944597154358902820
来源:稀土掘金
or (;😉 {
tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(ctrl->s, &rfds);
res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
if (res < 0)
return res;
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] = ‘\0’;
msg_cb(reply, res);
}
continue;
}
reply_len = res;
break;
} else {
return -2;
}
}
return 0;
}
#endif /
CTRL_IFACE_SOCKET */




转载自:

作者:天涯一角
链接:https://juejin.cn/post/6944597154358902820
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值