Android -- Wifi的save()操作

Android -- Wifi的save()操作


当我们在Settings中设置静态IP连接时,会调用到WifiManager::save()函数:
   /**
     * Save the given network in the supplicant config. If the network already
     * exists, the configuration is updated. A new network is enabled
     * by default.
     *
     * For a new network, this function is used instead of a
     * sequence of addNetwork(), enableNetwork() and saveConfiguration().
     *
     * For an existing network, it accomplishes the task of updateNetwork()
     * and saveConfiguration()
     *
     * @param config the set of variables that describe the configuration,
     *            contained in a {@link WifiConfiguration} object.
     * @param listener for callbacks on success or failure. Can be null.
     * @throws IllegalStateException if the WifiManager instance needs to be
     * initialized again
     * @hide
     */
    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);
    }
有注释可知,save函数会把传入WifiConfiguration信息代表的网络保存进wpa_supplicant.conf文件中;如果网络是已经存在的,则会更新信息;如果是新添加的,则会重新添加到配置文件中。

WifiServiceImpl::ClientHandler处理该消息:
/* Client commands are forwarded to state machine */
                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;
                }
主要做消息转发处理,看WifiStateMachine中的处理流程。ConnectModeState状态处理该消息:
case WifiManager.SAVE_NETWORK:
                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
                    // Fall thru
                case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
                    lastSavedConfigurationAttempt = null; // Used for debug
                    config = (WifiConfiguration) message.obj;
                    if (config == null) {
                        loge("ERROR: SAVE_NETWORK with null configuration"
                                + mSupplicantStateTracker.getSupplicantStateName()
                                + " my state " + getCurrentState().getName());
                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
                                WifiManager.ERROR);
                        break;
                    }
                    lastSavedConfigurationAttempt = new WifiConfiguration(config);
                    int nid = config.networkId;
                    logd("SAVE_NETWORK id=" + Integer.toString(nid)
                                + " config=" + config.SSID
                                + " nid=" + config.networkId
                                + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
                                + " my state " + getCurrentState().getName());

                    // Only record the uid if this is user initiated
                    boolean checkUid = (message.what == WifiManager.SAVE_NETWORK);
                    if (checkUid && !recordUidIfAuthorized(config, message.sendingUid,
                            /* onlyAnnotate */ false)) {
                        logw("Not authorized to update network "
                             + " config=" + config.SSID
                             + " cnid=" + config.networkId
                             + " uid=" + message.sendingUid);
                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
                                       WifiManager.NOT_AUTHORIZED);
                        break;
                    }

                    result = mWifiConfigStore.saveNetwork(config, WifiConfiguration.UNKNOWN_UID);
                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
                        if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
                            if (result.hasIpChanged()) {
                                // The currently connection configuration was changed
                                // We switched from DHCP to static or from static to DHCP, or the
                                // static IP address has changed.
                                log("Reconfiguring IP on connection");
                                // TODO: clear addresses and disable IPv6
                                // to simplify obtainingIpState.
                                transitionTo(mObtainingIpState);
                            }
                            if (result.hasProxyChanged()) {
                                log("Reconfiguring proxy on connection");
                                updateLinkProperties(CMD_UPDATE_LINKPROPERTIES);
                            }
                        }
                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
                        broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);

                        if (VDBG) {
                           logd("Success save network nid="
                                    + Integer.toString(result.getNetworkId()));
                        }

                        synchronized(mScanResultCache) {
                            /**
                             * If the command comes from WifiManager, then
                             * tell autojoin the user did try to modify and save that network,
                             * and interpret the SAVE_NETWORK as a request to connect
                             */
                            boolean user = message.what == WifiManager.SAVE_NETWORK;

                            // Did this connect come from settings
                            boolean persistConnect =
                                mWifiConfigStore.checkConfigOverridePermission(message.sendingUid);

                            if (user) {
                                mWifiConfigStore.updateLastConnectUid(config, message.sendingUid);
                                mWifiConfigStore.writeKnownNetworkHistory(false);
                            }

                            mWifiAutoJoinController.updateConfigurationHistory(result.getNetworkId()
                                    , user, persistConnect);
                            mWifiAutoJoinController.attemptAutoJoin();
                        }
                    } else {
                        loge("Failed to save network");
                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
                                WifiManager.ERROR);
                    }
                    break;
如果检测出IP配置信息有变化,则会切换到ObtainingIpState状态,并触发autojoin流程。如果用户使用的是静态IP,看ObtainingIpState的enter()函数:
if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
                if (isRoaming()) {
                    renewDhcp();
                } else {
                    // Remove any IP address on the interface in case we're switching from static
                    // IP configuration to DHCP. This is safe because if we get here when not
                    // roaming, we don't have a usable address.
                    clearIPv4Address(mInterfaceName);
                    startDhcp();
                }
                obtainingIpWatchdogCount++;
                logd("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
                // Get Link layer stats so as we get fresh tx packet counters
                getWifiLinkLayerStats(true);
                sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
                        obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);
            } else {
                // stop any running dhcp before assigning static IP
                stopDhcp();
                StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(
                        mLastNetworkId);
                if (config.ipAddress == null) {
                    logd("Static IP lacks address");
                    sendMessage(CMD_STATIC_IP_FAILURE);
                } else {
                    InterfaceConfiguration ifcg = new InterfaceConfiguration();
                    ifcg.setLinkAddress(config.ipAddress);
                    ifcg.setInterfaceUp();
                    try {
                        mNwService.setInterfaceConfig(mInterfaceName, ifcg);
                        if (DBG) log("Static IP configuration succeeded");
                        DhcpResults dhcpResults = new DhcpResults(config);
                        sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
                    } catch (RemoteException re) {
                        loge("Static IP configuration failed: " + re);
                        sendMessage(CMD_STATIC_IP_FAILURE);
                    } catch (IllegalStateException e) {
                        loge("Static IP configuration failed: " + e);
                        sendMessage(CMD_STATIC_IP_FAILURE);
                    }
                }
            }
看else分支,在设置静态IP之前,调用stopDhcp()停掉当前的dhcp流程,获取到上层传入的静态IP配置信息:

  1. 如果config为null,则认为静态配置失败
  2. 调用mNwService.setInterfaceConfig(),设置配置的IP地址信息
  3. 组装DhcpResults对象,并发送CMD_STATIC_IP_SUCCESS消息,更新网络状态

CMD_STATIC_IP_SUCCESS在ObtainingIpState被处理:
              case CMD_STATIC_IP_SUCCESS:
                  handleIPv4Success((DhcpResults) message.obj, CMD_STATIC_IP_SUCCESS);
                  break;
    private void handleIPv4Success(DhcpResults dhcpResults, int reason) {

        if (PDBG) {
            logd("handleIPv4Success <" + dhcpResults.toString() + ">");
            logd("link address " + dhcpResults.ipAddress);
        }

        Inet4Address addr;
        synchronized (mDhcpResultsLock) {
            mDhcpResults = dhcpResults;
            addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
        }

        if (isRoaming()) {
            int previousAddress = mWifiInfo.getIpAddress();
            int newAddress = NetworkUtils.inetAddressToInt(addr);
            if (previousAddress != newAddress) {
                logd("handleIPv4Success, roaming and address changed" +
                        mWifiInfo + " got: " + addr);
            }
        }
        mWifiInfo.setInetAddress(addr);
        mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
        updateLinkProperties(reason);
    }
将当前静态IP设置的DhcpResults赋给状态机的成员变量mDchpResults,最后调用updateLinkProperties()更新连接配置信息:
// If we just configured or lost IP configuration, do the needful.
        // We don't just call handleSuccessfulIpConfiguration() or handleIpConfigurationLost()
        // here because those should only be called if we're attempting to connect or already
        // connected, whereas updateLinkProperties can be called at any time.
        switch (reason) {
            case DhcpStateMachine.DHCP_SUCCESS:
            case CMD_STATIC_IP_SUCCESS:
                // IPv4 provisioning succeded. Advance to connected state.
                sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
                if (!isProvisioned) {
                    // Can never happen unless DHCP reports success but isProvisioned thinks the
                    // resulting configuration is invalid (e.g., no IPv4 address, or the state in
                    // mLinkProperties is out of sync with reality, or there's a bug in this code).
                    // TODO: disconnect here instead. If our configuration is not usable, there's no
                    // point in staying connected, and if mLinkProperties is out of sync with
                    // reality, that will cause problems in the future.
                    logd("IPv4 config succeeded, but not provisioned");
                }
                break;
跟踪CMD_IP_CONFIGURATION_SUCCESSFUL消息。ObtainingIpState不处理该消息,其父状态L2ConnectedState处理:
                case CMD_IP_CONFIGURATION_SUCCESSFUL:
                    handleSuccessfulIpConfiguration();
                    sendConnectedState();
                    transitionTo(mConnectedState);
                    break;
静态IP配置成功后,调用handleSuccessfulIpConfiguration()做之后的一些准备工作;调用sendConnectedState(),发送WifiManager.NETWORK_STATE_CHANGED_ACTION广播,通知其他组件网络连接状态的变化(此处是CONNECTED)。最后将Wifi状态机切换到ConnectedSate,代表网络已经连接完成。








你可能感兴趣的:(Android网络接入框架分析)